From 418b8ff6a7eb45c025abb2791d9b25a047b8cf49 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 1 Aug 2014 17:36:56 -0700 Subject: [PATCH 001/133] Add support for UUIDs in domains. --- .../com/sk89q/worldguard/LocalPlayer.java | 29 ++- .../sk89q/worldguard/bukkit/BukkitPlayer.java | 18 +- .../worldguard/domains/DefaultDomain.java | 176 ++++++++++++------ .../com/sk89q/worldguard/domains/Domain.java | 38 +++- .../worldguard/domains/DomainCollection.java | 30 ++- .../sk89q/worldguard/domains/GroupDomain.java | 73 +++++++- .../worldguard/domains/PlayerDomain.java | 134 +++++++++++-- .../java/com/sk89q/worldguard/TestPlayer.java | 16 +- .../worldguard/domains/DefaultDomainTest.java | 120 ++++++++++++ 9 files changed, 536 insertions(+), 98 deletions(-) create mode 100644 src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java diff --git a/src/main/java/com/sk89q/worldguard/LocalPlayer.java b/src/main/java/com/sk89q/worldguard/LocalPlayer.java index 45ea8cf3..b1964aa9 100644 --- a/src/main/java/com/sk89q/worldguard/LocalPlayer.java +++ b/src/main/java/com/sk89q/worldguard/LocalPlayer.java @@ -21,16 +21,26 @@ import com.sk89q.worldedit.Vector; +import java.util.UUID; + public abstract class LocalPlayer { + /** - * Get a player's name. + * Get this player's name. * * @return The player's name */ public abstract String getName(); + + /** + * Get this player's unique ID. + * + * @return a UUID + */ + public abstract UUID getUniqueId(); /** - * Returns true if the player is inside a group. + * Returns true if this player is inside a group. * * @param group The group to check * @return Whether this player is in {@code group} @@ -38,42 +48,42 @@ public abstract class LocalPlayer { public abstract boolean hasGroup(String group); /** - * Get the player's position. + * Get this player's position. * * @return The player's position */ public abstract Vector getPosition(); /** - * Kick the player. + * Kick this player. * * @param msg The message to kick the player with */ public abstract void kick(String msg); /** - * Ban the player. + * Ban this player. * * @param msg The message to ban the player with */ public abstract void ban(String msg); /** - * Send the player a message; + * Send this player a message. * * @param msg The message to send to the player */ public abstract void printRaw(String msg); /** - * Get the player's list of groups. + * Get this player's list of groups. * * @return The groups this player is in */ public abstract String[] getGroups(); /** - * Returns whether a player has permission. + * Returns whether this player has permission. * * @param perm The permission to check * @return Whether this player has {@code perm} @@ -82,13 +92,12 @@ public abstract class LocalPlayer { @Override public boolean equals(Object obj) { - return obj instanceof LocalPlayer && ((LocalPlayer) obj).getName().equals(getName()); - } @Override public int hashCode() { return getName().hashCode(); } + } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java b/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java index b6587797..8bcd56f5 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java @@ -25,11 +25,19 @@ import com.sk89q.worldedit.Vector; import com.sk89q.worldguard.LocalPlayer; +import java.util.UUID; + +import static com.google.common.base.Preconditions.checkNotNull; + public class BukkitPlayer extends LocalPlayer { - private Player player; - private WorldGuardPlugin plugin; + + private final WorldGuardPlugin plugin; + private final Player player; public BukkitPlayer(WorldGuardPlugin plugin, Player player) { + checkNotNull(plugin); + checkNotNull(player); + this.plugin = plugin; this.player = player; } @@ -39,6 +47,11 @@ public String getName() { return player.getName(); } + @Override + public UUID getUniqueId() { + return player.getUniqueId(); + } + @Override public boolean hasGroup(String group) { return plugin.inGroup(player, group); @@ -75,4 +88,5 @@ public void printRaw(String msg) { public boolean hasPermission(String perm) { return plugin.hasPermission(player, perm); } + } diff --git a/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java b/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java index 6ba5befa..e8093784 100644 --- a/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java +++ b/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java @@ -26,76 +26,151 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; +import java.util.UUID; +/** + * A combination of a {@link PlayerDomain} and a {@link GroupDomain}. + */ public class DefaultDomain implements Domain { - private final Set groups; - private final Set players; - - public DefaultDomain() { - this.groups = new CopyOnWriteArraySet(); - this.players = new CopyOnWriteArraySet(); - } - + + private final PlayerDomain playerDomain = new PlayerDomain(); + private final GroupDomain groupDomain = new GroupDomain(); + + /** + * Add the given player to the domain, identified by the player's name. + * + * @param name the name of the player + * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ + */ + @Deprecated public void addPlayer(String name) { - players.add(name.toLowerCase()); + playerDomain.addPlayer(name); } - - public void addPlayer(LocalPlayer player) { - players.add(player.getName().toLowerCase()); - } - + + /** + * Remove the given player from the domain, identified by the player's name. + * + * @param name the name of the player + * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ + */ + @Deprecated public void removePlayer(String name) { - players.remove(name.toLowerCase()); + playerDomain.removePlayer(name); } - + + /** + * Add the given player to the domain, identified by the player's UUID. + * + * @param uniqueId the UUID of the player + */ + public void addPlayer(UUID uniqueId) { + playerDomain.addPlayer(uniqueId); + } + + /** + * Remove the given player from the domain, identified by either the + * player's name, the player's unique ID, or both. + * + * @param player the player + */ public void removePlayer(LocalPlayer player) { - players.remove(player.getName().toLowerCase()); + playerDomain.removePlayer(player); } - - public void addGroup(String name) { - groups.add(name.toLowerCase()); + + /** + * Add the given player to the domain, identified by the player's UUID. + * + * @param player the player + */ + public void addPlayer(LocalPlayer player) { + playerDomain.addPlayer(player); } - - public void removeGroup(String name) { - groups.remove(name.toLowerCase()); - } - - public Set getGroups() { - return groups; - } - + + /** + * Get the set of player names. + * + * @return the set of player names + * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ + */ + @Deprecated public Set getPlayers() { - return players; + return playerDomain.getPlayers(); + } + + /** + * Get the set of player UUIDs. + * + * @return the set of player UUIDs + */ + public Set getUniqueIds() { + return playerDomain.getUniqueIds(); + } + + /** + * Add the name of the group to the domain. + * + * @param name the name of the group. + */ + public void addGroup(String name) { + groupDomain.addGroup(name); + } + + /** + * Remove the given group from the domain. + * + * @param name the name of the group + */ + public void removeGroup(String name) { + groupDomain.removeGroup(name); + } + + /** + * Get the set of group names. + * + * @return the set of group names + */ + public Set getGroups() { + return groupDomain.getGroups(); } @Override public boolean contains(LocalPlayer player) { - if (contains(player.getName())) { - return true; - } + return playerDomain.contains(player) || groupDomain.contains(player); + } - for (String group : groups) { - if (player.hasGroup(group)) { - return true; - } - } - - return false; + @Override + public boolean contains(UUID uniqueId) { + return playerDomain.contains(uniqueId); } @Override public boolean contains(String playerName) { - return players.contains(playerName.toLowerCase()); + return playerDomain.contains(playerName); } + @Override public int size() { - return groups.size() + players.size(); + return groupDomain.size() + playerDomain.size(); } - + + @Override + public void clear() { + playerDomain.clear(); + groupDomain.clear(); + } + + public void removeAll() { + clear(); + } + + @SuppressWarnings("deprecation") public String toPlayersString() { StringBuilder str = new StringBuilder(); - List output = new ArrayList(players); + List output = new ArrayList(); + output.addAll(playerDomain.getPlayers()); + for (UUID uuid : playerDomain.getUniqueIds()) { + output.add("uuid:" + uuid); + } Collections.sort(output, String.CASE_INSENSITIVE_ORDER); for (Iterator it = output.iterator(); it.hasNext();) { str.append(it.next()); @@ -108,7 +183,7 @@ public String toPlayersString() { public String toGroupsString() { StringBuilder str = new StringBuilder(); - for (Iterator it = groups.iterator(); it.hasNext(); ) { + for (Iterator it = groupDomain.getGroups().iterator(); it.hasNext(); ) { str.append("*"); str.append(it.next()); if (it.hasNext()) { @@ -120,11 +195,12 @@ public String toGroupsString() { public String toUserFriendlyString() { StringBuilder str = new StringBuilder(); - if (players.size() > 0) { + + if (playerDomain.size() > 0) { str.append(toPlayersString()); } - if (groups.size() > 0) { + if (groupDomain.size() > 0) { if (str.length() > 0) { str.append("; "); } @@ -135,8 +211,4 @@ public String toUserFriendlyString() { return str.toString(); } - public void removeAll() { - groups.clear(); - players.clear(); - } } diff --git a/src/main/java/com/sk89q/worldguard/domains/Domain.java b/src/main/java/com/sk89q/worldguard/domains/Domain.java index 7eaca201..0d9c3f5a 100644 --- a/src/main/java/com/sk89q/worldguard/domains/Domain.java +++ b/src/main/java/com/sk89q/worldguard/domains/Domain.java @@ -21,21 +21,53 @@ import com.sk89q.worldguard.LocalPlayer; +import java.util.UUID; + +/** + * A domain contains a list of memberships. + */ public interface Domain { + /** * Returns true if a domain contains a player. * - * @param player The player to check + * @param player the player to check * @return whether this domain contains {@code player} */ boolean contains(LocalPlayer player); /** - * Returns true if a domain contains a player.
- * This method doesn't check for groups! + * Returns true if a domain contains a player. + * + *

This method doesn't check for groups!

+ * + * @param uniqueId the UUID of the user + * @return whether this domain contains a player by that name + */ + boolean contains(UUID uniqueId); + + /** + * Returns true if a domain contains a player. + * + *

This method doesn't check for groups!

* * @param playerName The name of the player to check * @return whether this domain contains a player by that name + * @deprecated names are deprecated in MC 1.7+ in favor of UUIDs */ + @Deprecated boolean contains(String playerName); + + /** + * Get the number of entries. + * + * @return the number of entries + */ + int size(); + + /** + * Remove all entries. + */ + void clear(); + } diff --git a/src/main/java/com/sk89q/worldguard/domains/DomainCollection.java b/src/main/java/com/sk89q/worldguard/domains/DomainCollection.java index f34ec5f7..f149e24a 100644 --- a/src/main/java/com/sk89q/worldguard/domains/DomainCollection.java +++ b/src/main/java/com/sk89q/worldguard/domains/DomainCollection.java @@ -19,12 +19,18 @@ package com.sk89q.worldguard.domains; -import java.util.LinkedHashSet; -import java.util.Set; - import com.sk89q.worldguard.LocalPlayer; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @deprecated not used by WorldGuard and not maintained + */ +@Deprecated public class DomainCollection implements Domain { + private Set domains; public DomainCollection() { @@ -39,10 +45,16 @@ public void remove(Domain domain) { domains.remove(domain); } + @Override public int size() { return domains.size(); } + @Override + public void clear() { + domains.clear(); + } + @Override public boolean contains(LocalPlayer player) { for (Domain domain : domains) { @@ -54,6 +66,17 @@ public boolean contains(LocalPlayer player) { return false; } + @Override + public boolean contains(UUID uniqueId) { + for (Domain domain : domains) { + if (domain.contains(uniqueId)) { + return true; + } + } + + return false; + } + @Override public boolean contains(String playerName) { for (Domain domain : domains) { @@ -64,4 +87,5 @@ public boolean contains(String playerName) { return false; } + } diff --git a/src/main/java/com/sk89q/worldguard/domains/GroupDomain.java b/src/main/java/com/sk89q/worldguard/domains/GroupDomain.java index c5edb7d7..5118d762 100644 --- a/src/main/java/com/sk89q/worldguard/domains/GroupDomain.java +++ b/src/main/java/com/sk89q/worldguard/domains/GroupDomain.java @@ -19,28 +19,62 @@ package com.sk89q.worldguard.domains; -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.Set; - import com.sk89q.worldguard.LocalPlayer; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArraySet; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Contains groups in a domain. + */ public class GroupDomain implements Domain { - private Set groups; + private final Set groups = new CopyOnWriteArraySet(); + + /** + * Create a new instance. + */ public GroupDomain() { - this.groups = new LinkedHashSet(); } - public GroupDomain(String[] groups) { - this.groups = new LinkedHashSet(Arrays.asList(groups)); + /** + * Create a new instance. + * + * @param groupsy an array of groups + */ + public GroupDomain(String[] groupsy) { + checkNotNull(groupsy); + for (String group : groupsy) { + addGroup(group); + } } + /** + * Add the name of the group to the domain. + * + * @param name the name of the group. + */ public void addGroup(String name) { - groups.add(name); + checkNotNull(name); + groups.add(name.toLowerCase()); } + /** + * Remove the given group from the domain. + * + * @param name the name of the group + */ + public void removeGroup(String name) { + checkNotNull(name); + groups.remove(name.toLowerCase()); + } + + @Override public boolean contains(LocalPlayer player) { + checkNotNull(player); for (String group : groups) { if (player.hasGroup(group)) { return true; @@ -50,12 +84,33 @@ public boolean contains(LocalPlayer player) { return false; } + /** + * Get the set of group names. + * + * @return the set of group names + */ + public Set getGroups() { + return groups; + } + + @Override + public boolean contains(UUID uniqueId) { + return false; // GroupDomains can't contain UUIDs + } + @Override public boolean contains(String playerName) { return false; // GroupDomains can't contain player names. } + @Override public int size() { return groups.size(); } + + @Override + public void clear() { + groups.clear(); + } + } diff --git a/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java b/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java index 0c50a338..d7ae0d60 100644 --- a/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java +++ b/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java @@ -19,40 +19,144 @@ package com.sk89q.worldguard.domains; -import java.util.HashSet; -import java.util.Set; - import com.sk89q.worldguard.LocalPlayer; -public class PlayerDomain implements Domain { - private Set players; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArraySet; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Stores players (only) in a domain. + */ +public class PlayerDomain implements Domain { + + private final Set uniqueIds = new CopyOnWriteArraySet(); + private final Set names = new CopyOnWriteArraySet(); + + /** + * Create a new instance. + */ public PlayerDomain() { - this.players = new HashSet(); } - public PlayerDomain(String[] players) { - this.players = new HashSet(); - - for (String name : players) { - this.players.add(name.toLowerCase()); + /** + * Create a new instance with the given names. + * + * @param names an array of names + * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ + */ + @Deprecated + public PlayerDomain(String[] names) { + for (String name : names) { + addPlayer(name); } } + /** + * Add the given player to the domain, identified by the player's name. + * + * @param name the name of the player + * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ + */ + @Deprecated public void addPlayer(String name) { - players.add(name.toLowerCase()); + checkNotNull(name); + names.add(name.toLowerCase()); } + /** + * Add the given player to the domain, identified by the player's UUID. + * + * @param uniqueId the UUID of the player + */ + public void addPlayer(UUID uniqueId) { + checkNotNull(uniqueId); + uniqueIds.add(uniqueId); + } + + /** + * Add the given player to the domain, identified by the player's UUID. + * + * @param player the player + */ + public void addPlayer(LocalPlayer player) { + checkNotNull(player); + addPlayer(player.getUniqueId()); + } + + /** + * Remove the given player from the domain, identified by the player's name. + * + * @param name the name of the player + * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ + */ + @Deprecated + public void removePlayer(String name) { + checkNotNull(name); + names.remove(name.toLowerCase()); + } + + /** + * Remove the given player from the domain, identified by either the + * player's name, the player's unique ID, or both. + * + * @param player the player + */ + public void removePlayer(LocalPlayer player) { + checkNotNull(player); + names.remove(player.getName().toLowerCase()); + uniqueIds.remove(player.getUniqueId()); + } + + @Override public boolean contains(LocalPlayer player) { - return contains(player.getName()); + checkNotNull(player); + return contains(player.getName()) || contains(player.getUniqueId()); + } + + /** + * Get the set of player names. + * + * @return the set of player names + * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ + */ + @Deprecated + public Set getPlayers() { + return names; + } + + /** + * Get the set of player UUIDs. + * + * @return the set of player UUIDs + */ + public Set getUniqueIds() { + return uniqueIds; + } + + @Override + public boolean contains(UUID uniqueId) { + checkNotNull(uniqueId); + return uniqueIds.contains(uniqueId); } @Override public boolean contains(String playerName) { - return players.contains(playerName.toLowerCase()); + checkNotNull(playerName); + return names.contains(playerName.toLowerCase()); } + @Override public int size() { - return players.size(); + return names.size() + uniqueIds.size(); } + + @Override + public void clear() { + uniqueIds.clear(); + names.clear(); + } + } diff --git a/src/test/java/com/sk89q/worldguard/TestPlayer.java b/src/test/java/com/sk89q/worldguard/TestPlayer.java index 041796b9..21ac0f6a 100644 --- a/src/test/java/com/sk89q/worldguard/TestPlayer.java +++ b/src/test/java/com/sk89q/worldguard/TestPlayer.java @@ -19,15 +19,18 @@ package com.sk89q.worldguard; +import com.sk89q.worldedit.Vector; + import java.util.HashSet; import java.util.Set; - -import com.sk89q.worldedit.Vector; +import java.util.UUID; @org.junit.Ignore public class TestPlayer extends LocalPlayer { - private String name; - private Set groups = new HashSet(); + + private final UUID uuid = UUID.randomUUID(); + private final String name; + private final Set groups = new HashSet(); public TestPlayer(String name) { this.name = name; @@ -42,6 +45,11 @@ public String getName() { return name; } + @Override + public UUID getUniqueId() { + return uuid; + } + @Override public boolean hasGroup(String group) { return groups.contains(group.toLowerCase()); diff --git a/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java b/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java new file mode 100644 index 00000000..f1b24cfb --- /dev/null +++ b/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java @@ -0,0 +1,120 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.domains; + +import com.sk89q.worldguard.TestPlayer; +import junit.framework.TestCase; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class DefaultDomainTest extends TestCase { + + public void testContains() throws Exception { + TestPlayer player1 = new TestPlayer("test1"); + TestPlayer player2 = new TestPlayer("test2"); + player2.addGroup("group1"); + player2.addGroup("group2"); + TestPlayer player3 = new TestPlayer("test3"); + player3.addGroup("group1"); + player3.addGroup("group3"); + + DefaultDomain domain; + + domain = new DefaultDomain(); + domain.addGroup("group1"); + assertThat(domain.contains(player1), is(false)); + assertThat(domain.contains(player2), is(true)); + assertThat(domain.contains(player3), is(true)); + + domain = new DefaultDomain(); + domain.addGroup("group1"); + domain.addGroup("group2"); + assertThat(domain.contains(player1), is(false)); + assertThat(domain.contains(player2), is(true)); + assertThat(domain.contains(player3), is(true)); + + domain = new DefaultDomain(); + domain.addGroup("group1"); + domain.addGroup("group3"); + assertThat(domain.contains(player1), is(false)); + assertThat(domain.contains(player2), is(true)); + assertThat(domain.contains(player3), is(true)); + + domain = new DefaultDomain(); + domain.addGroup("group3"); + assertThat(domain.contains(player1), is(false)); + assertThat(domain.contains(player2), is(false)); + assertThat(domain.contains(player3), is(true)); + + domain = new DefaultDomain(); + domain.addPlayer(player1.getName()); + assertThat(domain.contains(player1), is(true)); + assertThat(domain.contains(player2), is(false)); + assertThat(domain.contains(player3), is(false)); + + domain = new DefaultDomain(); + domain.addGroup("group3"); + domain.addPlayer(player1.getName()); + assertThat(domain.contains(player1), is(true)); + assertThat(domain.contains(player2), is(false)); + assertThat(domain.contains(player3), is(true)); + + domain = new DefaultDomain(); + domain.addGroup("group3"); + domain.addPlayer(player1.getUniqueId()); + assertThat(domain.contains(player1), is(true)); + assertThat(domain.contains(player2), is(false)); + assertThat(domain.contains(player3), is(true)); + + domain = new DefaultDomain(); + domain.addGroup("group3"); + domain.addPlayer(player1.getName()); + domain.addPlayer(player1.getUniqueId()); + assertThat(domain.contains(player1), is(true)); + assertThat(domain.contains(player2), is(false)); + assertThat(domain.contains(player3), is(true)); + + domain = new DefaultDomain(); + domain.addGroup("group3"); + domain.addPlayer(player1); + assertThat(domain.contains(player1), is(true)); + assertThat(domain.contains(player2), is(false)); + assertThat(domain.contains(player3), is(true)); + + domain = new DefaultDomain(); + domain.addPlayer(player1); + assertThat(domain.contains(player1), is(true)); + assertThat(domain.contains(player2), is(false)); + assertThat(domain.contains(player3), is(false)); + + domain = new DefaultDomain(); + domain.addPlayer(player2); + domain.addPlayer(player3); + assertThat(domain.contains(player1), is(false)); + assertThat(domain.contains(player2), is(true)); + assertThat(domain.contains(player3), is(true)); + } + + public void testSize() throws Exception { + + } + +} \ No newline at end of file From 51f98c9eb70c58be39219529ff5d9b4794fa1f89 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 1 Aug 2014 19:19:48 -0700 Subject: [PATCH 002/133] Reorganize MySQL region DB and added BoneCP as a dependency. --- pom.xml | 27 +- .../database/mysql/AbstractJob.java | 78 ++ .../database/mysql/MySQLDatabaseImpl.java | 215 +++ .../database/mysql/RegionCommitter.java | 652 +++++++++ .../database/mysql/RegionLoader.java | 446 +++++++ .../AbstractAsynchronousDatabase.java | 2 +- .../protection/databases/MySQLDatabase.java | 1164 +---------------- .../protection/databases/YAMLDatabase.java | 27 +- 8 files changed, 1440 insertions(+), 1171 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java create mode 100644 src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java create mode 100644 src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java create mode 100644 src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java diff --git a/pom.xml b/pom.xml index c73fa8ff..9e30a902 100644 --- a/pom.xml +++ b/pom.xml @@ -138,6 +138,14 @@ jar + + com.jolbox + bonecp + 0.8.0.RELEASE + compile + jar + + com.google.guava guava @@ -167,13 +175,27 @@ compile jar - + junit junit - 4.8.1 + 4.11 test jar + + + org.hamcrest + hamcrest-core + + + + + + org.hamcrest + hamcrest-library + 1.2.1 + test + true @@ -338,6 +360,7 @@ net.sf.opencsv:opencsv org.khelekore:prtree + com.jolbox:bonecp diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java new file mode 100644 index 00000000..44e6e3ec --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java @@ -0,0 +1,78 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.protection.database.mysql; + +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import org.yaml.snakeyaml.error.YAMLException; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +@SuppressWarnings("ProtectedField") +abstract class AbstractJob { + + protected final MySQLDatabaseImpl database; + protected final ConfigurationManager config; + protected final Connection conn; + protected final Logger logger; + + protected AbstractJob(MySQLDatabaseImpl database, Connection conn) { + checkNotNull(database); + checkNotNull(conn); + this.database = database; + this.config = database.getConfiguration(); + this.conn = conn; + this.logger = database.getLogger(); + } + + static void closeQuietly(ResultSet rs) { + if (rs != null) { + try { + rs.close(); + } catch (SQLException ignored) {} + } + } + + static void closeQuietly(Statement st) { + if (st != null) { + try { + st.close(); + } catch (SQLException ignored) {} + } + } + + protected Object sqlUnmarshal(String rawValue) { + try { + return database.getYaml().load(rawValue); + } catch (YAMLException e) { + return String.valueOf(rawValue); + } + } + + protected String sqlMarshal(Object rawObject) { + return database.getYaml().dump(rawObject); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java new file mode 100644 index 00000000..5b4e09ad --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java @@ -0,0 +1,215 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.protection.database.mysql; + +import com.jolbox.bonecp.BoneCP; +import com.jolbox.bonecp.BoneCPConfig; +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.protection.databases.AbstractAsynchronousDatabase; +import com.sk89q.worldguard.protection.databases.InvalidTableFormatException; +import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.DumperOptions.FlowStyle; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.representer.Representer; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * For internal use. Do not subclass. + */ +public class MySQLDatabaseImpl extends AbstractAsynchronousDatabase { + + private final ConfigurationManager config; + private final Logger logger; + + private final BoneCP connectionPool; + private final Yaml yaml = createYaml(); + private final int worldId; + + private Map regions = new HashMap(); + + public MySQLDatabaseImpl(ConfigurationManager config, String worldName, Logger logger) throws ProtectionDatabaseException { + checkNotNull(config); + checkNotNull(worldName); + checkNotNull(logger); + + this.config = config; + this.logger = logger; + + BoneCPConfig poolConfig = new BoneCPConfig(); + poolConfig.setJdbcUrl(config.sqlDsn); + poolConfig.setUsername(config.sqlUsername); + poolConfig.setPassword(config.sqlPassword); + + try { + connectionPool = new BoneCP(poolConfig); + } catch (SQLException e) { + throw new ProtectionDatabaseException("Failed to connect to the database", e); + } + + try { + worldId = chooseWorldId(worldName); + } catch (SQLException e) { + throw new ProtectionDatabaseException("Failed to choose the ID for this world", e); + } + } + + private int chooseWorldId(String worldName) throws SQLException { + Connection conn = getConnection(); + PreparedStatement worldStmt = null; + ResultSet worldResult = null; + PreparedStatement insertWorldStatement = null; + + try { + PreparedStatement verTest = null; + try { + // Test if the database is up to date, if not throw a critical error + verTest = conn.prepareStatement("SELECT `world_id` FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 0, 1;"); + verTest.execute(); + } catch (SQLException ex) { + throw new InvalidTableFormatException("region_storage_update_20110325.sql"); + } finally { + AbstractJob.closeQuietly(verTest); + } + + worldStmt = conn.prepareStatement( + "SELECT `id` FROM `" + config.sqlTablePrefix + "world` WHERE `name` = ? LIMIT 0, 1" + ); + + worldStmt.setString(1, worldName); + worldResult = worldStmt.executeQuery(); + + if (worldResult.first()) { + return worldResult.getInt("id"); + } else { + insertWorldStatement = conn.prepareStatement( + "INSERT INTO " + + "`" + config.sqlTablePrefix + "world` " + + "(`id`, `name`) VALUES (null, ?)", + Statement.RETURN_GENERATED_KEYS + ); + + insertWorldStatement.setString(1, worldName); + insertWorldStatement.execute(); + ResultSet generatedKeys = insertWorldStatement.getGeneratedKeys(); + + if (generatedKeys.first()) { + return generatedKeys.getInt(1); + } else { + throw new SQLException("Expected result, got none"); + } + } + } finally { + AbstractJob.closeQuietly(worldResult); + AbstractJob.closeQuietly(worldStmt); + AbstractJob.closeQuietly(insertWorldStatement); + } + } + + private static Yaml createYaml() { + DumperOptions options = new DumperOptions(); + options.setIndent(2); + options.setDefaultFlowStyle(FlowStyle.FLOW); + Representer representer = new Representer(); + representer.setDefaultFlowStyle(FlowStyle.FLOW); + + // We have to use this in order to properly save non-string values + return new Yaml(new SafeConstructor(), new Representer(), options); + } + + ConfigurationManager getConfiguration() { + return this.config; + } + + Logger getLogger() { + return logger; + } + + Yaml getYaml() { + return yaml; + } + + int getWorldId() { + return worldId; + } + + private Connection getConnection() throws SQLException { + return connectionPool.getConnection(); + } + + @Override + public Map getRegions() { + return regions; + } + + @Override + public void setRegions(Map regions) { + this.regions = regions; + } + + @Override + protected void performLoad() throws ProtectionDatabaseException { + Connection connection = null; + try { + connection = getConnection(); + setRegions(new RegionLoader(this, connection, regions).call()); + } catch (SQLException e) { + throw new ProtectionDatabaseException("Failed to load regions database", e); + } finally { + if (connection != null) { + try { + connection.close(); + } catch (SQLException ignored) { + } + } + } + } + + @Override + protected void performSave() throws ProtectionDatabaseException { + Connection connection = null; + try { + connection = getConnection(); + new RegionCommitter(this, connection, getRegions()).call(); + } catch (SQLException e) { + throw new ProtectionDatabaseException("Failed to save regions database", e); + } finally { + if (connection != null) { + try { + connection.close(); + } catch (SQLException ignored) { + } + } + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java new file mode 100644 index 00000000..4a13f7f2 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java @@ -0,0 +1,652 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.protection.database.mysql; + +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.databases.RegionDBUtil; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +import static com.google.common.base.Preconditions.checkNotNull; + +class RegionCommitter extends AbstractJob implements Callable { + + private final Map regions; + private final int worldId; + + RegionCommitter(MySQLDatabaseImpl database, Connection conn, Map regions) { + super(database, conn); + checkNotNull(regions); + this.regions = regions; + this.worldId = database.getWorldId(); + } + + @Override + public Object call() throws SQLException, ProtectionDatabaseException { + + /* + * As we don't get notified on the creation/removal of regions: + * 1) We get a list of all of the in-database regions + * 2) We iterate over all of the in-memory regions + * 2a) If the region is in the database, we update the database and + * remove the region from the in-database list + * b) If the region is not in the database, we insert it + * 3) We iterate over what remains of the in-database list and remove + * them from the database + * + * TODO: Look at adding/removing/updating the database when the in + * memory region is created/remove/updated + * + * @see com.sk89q.worldguard.protection.databases.ProtectionDatabase#save() + */ + + List regionsInDatabase = new ArrayList(); + + PreparedStatement getAllRegionsStatement = null; + ResultSet getAllRegionsResult = null; + try { + getAllRegionsStatement = this.conn.prepareStatement( + "SELECT `region`.`id` FROM " + + "`" + config.sqlTablePrefix + "region` AS `region` " + + "WHERE `world_id` = ? " + ); + + getAllRegionsStatement.setInt(1, this.worldId); + getAllRegionsResult = getAllRegionsStatement.executeQuery(); + + while(getAllRegionsResult.next()) { + regionsInDatabase.add(getAllRegionsResult.getString("id")); + } + } catch (SQLException ex) { + logger.warning("Could not get region list for save comparison: " + ex.getMessage()); + } finally { + closeQuietly(getAllRegionsResult); + closeQuietly(getAllRegionsStatement); + } + + for (Map.Entry entry : regions.entrySet()) { + String name = entry.getKey(); + ProtectedRegion region = entry.getValue(); + + try { + if (regionsInDatabase.contains(name)) { + regionsInDatabase.remove(name); + + if (region instanceof ProtectedCuboidRegion) { + updateRegionCuboid( (ProtectedCuboidRegion) region ); + } else if (region instanceof ProtectedPolygonalRegion) { + updateRegionPoly2D( (ProtectedPolygonalRegion) region ); + } else if (region instanceof GlobalProtectedRegion) { + updateRegionGlobal( (GlobalProtectedRegion) region ); + } else { + this.updateRegion(region, region.getClass().getCanonicalName()); + } + } else { + if (region instanceof ProtectedCuboidRegion) { + insertRegionCuboid( (ProtectedCuboidRegion) region ); + } else if (region instanceof ProtectedPolygonalRegion) { + insertRegionPoly2D( (ProtectedPolygonalRegion) region ); + } else if (region instanceof GlobalProtectedRegion) { + insertRegionGlobal( (GlobalProtectedRegion) region ); + } else { + this.insertRegion(region, region.getClass().getCanonicalName()); + } + } + } catch (SQLException ex) { + logger.warning("Could not save region " + region.getId().toLowerCase() + ": " + ex.getMessage()); + throw new ProtectionDatabaseException(ex); + } + } + + for (Map.Entry entry : regions.entrySet()) { + PreparedStatement setParentStatement = null; + try { + if (entry.getValue().getParent() == null) continue; + + setParentStatement = this.conn.prepareStatement( + "UPDATE `" + config.sqlTablePrefix + "region` SET " + + "`parent` = ? " + + "WHERE `id` = ? AND `world_id` = " + this.worldId + ); + + setParentStatement.setString(1, entry.getValue().getParent().getId().toLowerCase()); + setParentStatement.setString(2, entry.getValue().getId().toLowerCase()); + + setParentStatement.execute(); + } catch (SQLException ex) { + logger.warning("Could not save region parents " + entry.getValue().getId().toLowerCase() + ": " + ex.getMessage()); + throw new ProtectionDatabaseException(ex); + } finally { + closeQuietly(setParentStatement); + } + } + + for (String name : regionsInDatabase) { + PreparedStatement removeRegion = null; + try { + removeRegion = this.conn.prepareStatement( + "DELETE FROM `" + config.sqlTablePrefix + "region` WHERE `id` = ? " + ); + + removeRegion.setString(1, name); + removeRegion.execute(); + } catch (SQLException ex) { + logger.warning("Could not remove region from database " + name + ": " + ex.getMessage()); + } finally { + closeQuietly(removeRegion); + } + } + + return null; + } + + /* + * Returns the database id for the user + * If it doesn't exits it adds the user and returns the id. + */ + private Map getUserIds(String... usernames) { + Map users = new HashMap(); + + if (usernames.length < 1) return users; + + ResultSet findUsersResults = null; + PreparedStatement insertUserStatement = null; + PreparedStatement findUsersStatement = null; + try { + findUsersStatement = this.conn.prepareStatement( + String.format( + "SELECT " + + "`user`.`id`, " + + "`user`.`name` " + + "FROM `" + config.sqlTablePrefix + "user` AS `user` " + + "WHERE `name` IN (%s)", + RegionDBUtil.preparePlaceHolders(usernames.length) + ) + ); + + RegionDBUtil.setValues(findUsersStatement, usernames); + + findUsersResults = findUsersStatement.executeQuery(); + + while(findUsersResults.next()) { + users.put(findUsersResults.getString("name"), findUsersResults.getInt("id")); + } + + insertUserStatement = this.conn.prepareStatement( + "INSERT INTO " + + "`" + config.sqlTablePrefix + "user` ( " + + "`id`, " + + "`name`" + + ") VALUES (null, ?)", + Statement.RETURN_GENERATED_KEYS + ); + + for (String username : usernames) { + if (!users.containsKey(username)) { + insertUserStatement.setString(1, username); + insertUserStatement.execute(); + ResultSet generatedKeys = insertUserStatement.getGeneratedKeys(); + if (generatedKeys.first()) { + users.put(username, generatedKeys.getInt(1)); + } else { + logger.warning("Could not get the database id for user " + username); + } + } + } + } catch (SQLException ex) { + ex.printStackTrace(); + logger.warning("Could not get the database id for the users " + usernames.toString() + "\n\t" + ex.getMessage()); + Throwable t = ex.getCause(); + while (t != null) { + logger.warning(t.getMessage()); + t = t.getCause(); + } + } finally { + closeQuietly(findUsersResults); + closeQuietly(findUsersStatement); + closeQuietly(insertUserStatement); + } + + return users; + } + + /* + * Returns the database id for the groups + * If it doesn't exits it adds the group and returns the id. + */ + private Map getGroupIds(String... groupnames) { + Map groups = new HashMap(); + + if (groupnames.length < 1) return groups; + + PreparedStatement findGroupsStatement = null; + ResultSet findGroupsResults = null; + PreparedStatement insertGroupStatement = null; + try { + findGroupsStatement = this.conn.prepareStatement( + String.format( + "SELECT " + + "`group`.`id`, " + + "`group`.`name` " + + "FROM `" + config.sqlTablePrefix + "group` AS `group` " + + "WHERE `name` IN (%s)", + RegionDBUtil.preparePlaceHolders(groupnames.length) + ) + ); + + RegionDBUtil.setValues(findGroupsStatement, groupnames); + + findGroupsResults = findGroupsStatement.executeQuery(); + + while(findGroupsResults.next()) { + groups.put(findGroupsResults.getString("name"), findGroupsResults.getInt("id")); + } + + insertGroupStatement = this.conn.prepareStatement( + "INSERT INTO " + + "`" + config.sqlTablePrefix + "group` ( " + + "`id`, " + + "`name`" + + ") VALUES (null, ?)", + Statement.RETURN_GENERATED_KEYS + ); + + for (String groupname : groupnames) { + if (!groups.containsKey(groupname)) { + insertGroupStatement.setString(1, groupname); + insertGroupStatement.execute(); + ResultSet generatedKeys = insertGroupStatement.getGeneratedKeys(); + if (generatedKeys.first()) { + groups.put(groupname, generatedKeys.getInt(1)); + } else { + logger.warning("Could not get the database id for user " + groupname); + } + } + } + } catch (SQLException ex) { + logger.warning("Could not get the database id for the groups " + groupnames.toString() + ex.getMessage()); + } finally { + closeQuietly(findGroupsResults); + closeQuietly(findGroupsStatement); + closeQuietly(insertGroupStatement); + } + + return groups; + } + + private void updateFlags(ProtectedRegion region) throws SQLException { + PreparedStatement clearCurrentFlagStatement = null; + try { + clearCurrentFlagStatement = this.conn.prepareStatement( + "DELETE FROM `" + config.sqlTablePrefix + "region_flag` " + + "WHERE `region_id` = ? " + + "AND `world_id` = " + this.worldId + ); + + clearCurrentFlagStatement.setString(1, region.getId().toLowerCase()); + clearCurrentFlagStatement.execute(); + + for (Map.Entry, Object> entry : region.getFlags().entrySet()) { + if (entry.getValue() == null) continue; + + Object flag = sqlMarshal(marshalFlag(entry.getKey(), entry.getValue())); + + PreparedStatement insertFlagStatement = null; + try { + insertFlagStatement = this.conn.prepareStatement( + "INSERT INTO `" + config.sqlTablePrefix + "region_flag` ( " + + "`id`, " + + "`region_id`, " + + "`world_id`, " + + "`flag`, " + + "`value` " + + ") VALUES (null, ?, " + this.worldId + ", ?, ?)" + ); + + insertFlagStatement.setString(1, region.getId().toLowerCase()); + insertFlagStatement.setString(2, entry.getKey().getName()); + insertFlagStatement.setObject(3, flag); + + insertFlagStatement.execute(); + } finally { + closeQuietly(insertFlagStatement); + } + } + } finally { + closeQuietly(clearCurrentFlagStatement); + } + } + + private void updatePlayerAndGroups(ProtectedRegion region, Boolean owners) throws SQLException { + DefaultDomain domain; + + if (owners) { + domain = region.getOwners(); + } else { + domain = region.getMembers(); + } + + PreparedStatement deleteUsersForRegion = null; + PreparedStatement insertUsersForRegion = null; + PreparedStatement deleteGroupsForRegion = null; + PreparedStatement insertGroupsForRegion = null; + + try { + deleteUsersForRegion = this.conn.prepareStatement( + "DELETE FROM `" + config.sqlTablePrefix + "region_players` " + + "WHERE `region_id` = ? " + + "AND `world_id` = " + this.worldId + " " + + "AND `owner` = ?" + ); + + deleteUsersForRegion.setString(1, region.getId().toLowerCase()); + deleteUsersForRegion.setBoolean(2, owners); + deleteUsersForRegion.execute(); + + insertUsersForRegion = this.conn.prepareStatement( + "INSERT INTO `" + config.sqlTablePrefix + "region_players` " + + "(`region_id`, `world_id`, `user_id`, `owner`) " + + "VALUES (?, " + this.worldId + ", ?, ?)" + ); + + Set var = domain.getPlayers(); + + for (Integer player : getUserIds(var.toArray(new String[var.size()])).values()) { + insertUsersForRegion.setString(1, region.getId().toLowerCase()); + insertUsersForRegion.setInt(2, player); + insertUsersForRegion.setBoolean(3, owners); + + insertUsersForRegion.execute(); + } + + deleteGroupsForRegion = this.conn.prepareStatement( + "DELETE FROM `" + config.sqlTablePrefix + "region_groups` " + + "WHERE `region_id` = ? " + + "AND `world_id` = " + this.worldId + " " + + "AND `owner` = ?" + ); + + deleteGroupsForRegion.setString(1, region.getId().toLowerCase()); + deleteGroupsForRegion.setBoolean(2, owners); + deleteGroupsForRegion.execute(); + + insertGroupsForRegion = this.conn.prepareStatement( + "INSERT INTO `" + config.sqlTablePrefix + "region_groups` " + + "(`region_id`, `world_id`, `group_id`, `owner`) " + + "VALUES (?, " + this.worldId + ", ?, ?)" + ); + + Set groupVar = domain.getGroups(); + for (Integer group : getGroupIds(groupVar.toArray(new String[groupVar.size()])).values()) { + insertGroupsForRegion.setString(1, region.getId().toLowerCase()); + insertGroupsForRegion.setInt(2, group); + insertGroupsForRegion.setBoolean(3, owners); + + insertGroupsForRegion.execute(); + } + } finally { + closeQuietly(deleteGroupsForRegion); + closeQuietly(deleteUsersForRegion); + closeQuietly(insertGroupsForRegion); + closeQuietly(insertUsersForRegion); + } + } + + @SuppressWarnings("unchecked") + private Object marshalFlag(Flag flag, Object val) { + return flag.marshal( (V) val ); + } + + private void insertRegion(ProtectedRegion region, String type) throws SQLException { + PreparedStatement insertRegionStatement = null; + try { + insertRegionStatement = this.conn.prepareStatement( + "INSERT INTO `" + config.sqlTablePrefix + "region` (" + + "`id`, " + + "`world_id`, " + + "`type`, " + + "`priority`, " + + "`parent` " + + ") VALUES (?, ?, ?, ?, null)" + ); + + insertRegionStatement.setString(1, region.getId().toLowerCase()); + insertRegionStatement.setInt(2, this.worldId); + insertRegionStatement.setString(3, type); + insertRegionStatement.setInt(4, region.getPriority()); + + insertRegionStatement.execute(); + } finally { + closeQuietly(insertRegionStatement); + } + + updateFlags(region); + + updatePlayerAndGroups(region, false); + updatePlayerAndGroups(region, true); + } + + private void insertRegionCuboid(ProtectedCuboidRegion region) throws SQLException { + insertRegion(region, "cuboid"); + + PreparedStatement insertCuboidRegionStatement = null; + try { + insertCuboidRegionStatement = this.conn.prepareStatement( + "INSERT INTO `" + config.sqlTablePrefix + "region_cuboid` (" + + "`region_id`, " + + "`world_id`, " + + "`min_z`, " + + "`min_y`, " + + "`min_x`, " + + "`max_z`, " + + "`max_y`, " + + "`max_x` " + + ") VALUES (?, " + this.worldId + ", ?, ?, ?, ?, ?, ?)" + ); + + BlockVector min = region.getMinimumPoint(); + BlockVector max = region.getMaximumPoint(); + + insertCuboidRegionStatement.setString(1, region.getId().toLowerCase()); + insertCuboidRegionStatement.setInt(2, min.getBlockZ()); + insertCuboidRegionStatement.setInt(3, min.getBlockY()); + insertCuboidRegionStatement.setInt(4, min.getBlockX()); + insertCuboidRegionStatement.setInt(5, max.getBlockZ()); + insertCuboidRegionStatement.setInt(6, max.getBlockY()); + insertCuboidRegionStatement.setInt(7, max.getBlockX()); + + insertCuboidRegionStatement.execute(); + } finally { + closeQuietly(insertCuboidRegionStatement); + } + } + + private void insertRegionPoly2D(ProtectedPolygonalRegion region) throws SQLException { + insertRegion(region, "poly2d"); + + PreparedStatement insertPoly2dRegionStatement = null; + try { + insertPoly2dRegionStatement = this.conn.prepareStatement( + "INSERT INTO `" + config.sqlTablePrefix + "region_poly2d` (" + + "`region_id`, " + + "`world_id`, " + + "`max_y`, " + + "`min_y` " + + ") VALUES (?, " + this.worldId + ", ?, ?)" + ); + + insertPoly2dRegionStatement.setString(1, region.getId().toLowerCase()); + insertPoly2dRegionStatement.setInt(2, region.getMaximumPoint().getBlockY()); + insertPoly2dRegionStatement.setInt(3, region.getMinimumPoint().getBlockY()); + + insertPoly2dRegionStatement.execute(); + } finally { + closeQuietly(insertPoly2dRegionStatement); + } + + updatePoly2dPoints(region); + } + + private void updatePoly2dPoints(ProtectedPolygonalRegion region) throws SQLException { + PreparedStatement clearPoly2dPointsForRegionStatement = null; + PreparedStatement insertPoly2dPointStatement = null; + + try { + clearPoly2dPointsForRegionStatement = this.conn.prepareStatement( + "DELETE FROM `" + config.sqlTablePrefix + "region_poly2d_point` " + + "WHERE `region_id` = ? " + + "AND `world_id` = " + this.worldId + ); + + clearPoly2dPointsForRegionStatement.setString(1, region.getId().toLowerCase()); + + clearPoly2dPointsForRegionStatement.execute(); + + insertPoly2dPointStatement = this.conn.prepareStatement( + "INSERT INTO `" + config.sqlTablePrefix + "region_poly2d_point` (" + + "`id`, " + + "`region_id`, " + + "`world_id`, " + + "`z`, " + + "`x` " + + ") VALUES (null, ?, " + this.worldId + ", ?, ?)" + ); + + String lowerId = region.getId().toLowerCase(); + for (BlockVector2D point : region.getPoints()) { + insertPoly2dPointStatement.setString(1, lowerId); + insertPoly2dPointStatement.setInt(2, point.getBlockZ()); + insertPoly2dPointStatement.setInt(3, point.getBlockX()); + + insertPoly2dPointStatement.execute(); + } + } finally { + closeQuietly(clearPoly2dPointsForRegionStatement); + closeQuietly(insertPoly2dPointStatement); + } + } + + private void insertRegionGlobal(GlobalProtectedRegion region) throws SQLException { + insertRegion(region, "global"); + } + + private void updateRegion(ProtectedRegion region, String type) throws SQLException { + PreparedStatement updateRegionStatement = null; + try { + updateRegionStatement = this.conn.prepareStatement( + "UPDATE `" + config.sqlTablePrefix + "region` SET " + + "`priority` = ? WHERE `id` = ? AND `world_id` = " + this.worldId + ); + + updateRegionStatement.setInt(1, region.getPriority()); + updateRegionStatement.setString(2, region.getId().toLowerCase()); + + updateRegionStatement.execute(); + } finally { + closeQuietly(updateRegionStatement); + } + + updateFlags(region); + + updatePlayerAndGroups(region, false); + updatePlayerAndGroups(region, true); + } + + private void updateRegionCuboid(ProtectedCuboidRegion region) throws SQLException { + updateRegion(region, "cuboid"); + + PreparedStatement updateCuboidRegionStatement = null; + try { + updateCuboidRegionStatement = this.conn.prepareStatement( + "UPDATE `" + config.sqlTablePrefix + "region_cuboid` SET " + + "`min_z` = ?, " + + "`min_y` = ?, " + + "`min_x` = ?, " + + "`max_z` = ?, " + + "`max_y` = ?, " + + "`max_x` = ? " + + "WHERE `region_id` = ? " + + "AND `world_id` = " + this.worldId + ); + + BlockVector min = region.getMinimumPoint(); + BlockVector max = region.getMaximumPoint(); + + updateCuboidRegionStatement.setInt(1, min.getBlockZ()); + updateCuboidRegionStatement.setInt(2, min.getBlockY()); + updateCuboidRegionStatement.setInt(3, min.getBlockX()); + updateCuboidRegionStatement.setInt(4, max.getBlockZ()); + updateCuboidRegionStatement.setInt(5, max.getBlockY()); + updateCuboidRegionStatement.setInt(6, max.getBlockX()); + updateCuboidRegionStatement.setString(7, region.getId().toLowerCase()); + + updateCuboidRegionStatement.execute(); + } finally { + closeQuietly(updateCuboidRegionStatement); + } + } + + private void updateRegionPoly2D(ProtectedPolygonalRegion region) throws SQLException { + updateRegion(region, "poly2d"); + + PreparedStatement updatePoly2dRegionStatement = null; + try { + updatePoly2dRegionStatement = this.conn.prepareStatement( + "UPDATE `" + config.sqlTablePrefix + "region_poly2d` SET " + + "`max_y` = ?, " + + "`min_y` = ? " + + "WHERE `region_id` = ? " + + "AND `world_id` = " + this.worldId + ); + + updatePoly2dRegionStatement.setInt(1, region.getMaximumPoint().getBlockY()); + updatePoly2dRegionStatement.setInt(2, region.getMinimumPoint().getBlockY()); + updatePoly2dRegionStatement.setString(3, region.getId().toLowerCase()); + + updatePoly2dRegionStatement.execute(); + } finally { + closeQuietly(updatePoly2dRegionStatement); + } + updatePoly2dPoints(region); + } + + private void updateRegionGlobal(GlobalProtectedRegion region) throws SQLException { + updateRegion(region, "global"); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java new file mode 100644 index 00000000..59ad301d --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java @@ -0,0 +1,446 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.protection.database.mysql; + +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +import static com.google.common.base.Preconditions.checkNotNull; + +class RegionLoader extends AbstractJob implements Callable> { + + private final Map regions; + private final int worldId; + + private Map cuboidRegions; + private Map poly2dRegions; + private Map globalRegions; + private Map parentSets; + + RegionLoader(MySQLDatabaseImpl database, Connection conn, Map regions) { + super(database, conn); + checkNotNull(regions); + this.regions = regions; + this.worldId = database.getWorldId(); + } + + @Override + public Map call() { + parentSets = new HashMap(); + + // We load the cuboid regions first, as this is likely to be the + // largest dataset. This should save time in regards to the putAll()s + this.loadCuboid(); + Map regions = this.cuboidRegions; + this.cuboidRegions = null; + + this.loadPoly2d(); + regions.putAll(this.poly2dRegions); + this.poly2dRegions = null; + + this.loadGlobal(); + regions.putAll(this.globalRegions); + this.globalRegions = null; + + // Relink parents // Taken verbatim from YAMLDatabase + for (Map.Entry entry : parentSets.entrySet()) { + ProtectedRegion parent = regions.get(entry.getValue()); + if (parent != null) { + try { + entry.getKey().setParent(parent); + } catch (CircularInheritanceException e) { + logger.warning("Circular inheritance detect with '" + + entry.getValue() + "' detected as a parent"); + } + } else { + logger.warning("Unknown region parent: " + entry.getValue()); + } + } + + return regions; + } + + private void loadFlags(ProtectedRegion region) { + // @TODO: Iterate _ONCE_ + PreparedStatement flagsStatement = null; + ResultSet flagsResultSet = null; + try { + flagsStatement = this.conn.prepareStatement( + "SELECT " + + "`region_flag`.`flag`, " + + "`region_flag`.`value` " + + "FROM `" + config.sqlTablePrefix + "region_flag` AS `region_flag` " + + "WHERE `region_id` = ? " + + "AND `world_id` = " + this.worldId + ); + + flagsStatement.setString(1, region.getId().toLowerCase()); + flagsResultSet = flagsStatement.executeQuery(); + + Map regionFlags = new HashMap(); + while (flagsResultSet.next()) { + regionFlags.put( + flagsResultSet.getString("flag"), + sqlUnmarshal(flagsResultSet.getString("value")) + ); + } + + // @TODO: Make this better + for (Flag flag : DefaultFlag.getFlags()) { + Object o = regionFlags.get(flag.getName()); + if (o != null) { + setFlag(region, flag, o); + } + } + } catch (SQLException ex) { + logger.warning( + "Unable to load flags for region " + + region.getId().toLowerCase() + ": " + ex.getMessage() + ); + } finally { + closeQuietly(flagsResultSet); + closeQuietly(flagsStatement); + } + } + + private void setFlag(ProtectedRegion region, Flag flag, Object rawValue) { + T val = flag.unmarshal(rawValue); + if (val == null) { + logger.warning("Failed to parse flag '" + flag.getName() + + "' with value '" + rawValue.toString() + "'"); + return; + } + region.setFlag(flag, val); + } + + private void loadOwnersAndMembers(ProtectedRegion region) { + DefaultDomain owners = new DefaultDomain(); + DefaultDomain members = new DefaultDomain(); + + ResultSet userSet = null; + PreparedStatement usersStatement = null; + try { + usersStatement = this.conn.prepareStatement( + "SELECT " + + "`user`.`name`, " + + "`region_players`.`owner` " + + "FROM `" + config.sqlTablePrefix + "region_players` AS `region_players` " + + "LEFT JOIN `" + config.sqlTablePrefix + "user` AS `user` ON ( " + + "`region_players`.`user_id` = " + + "`user`.`id`) " + + "WHERE `region_players`.`region_id` = ? " + + "AND `region_players`.`world_id` = " + this.worldId + ); + + usersStatement.setString(1, region.getId().toLowerCase()); + userSet = usersStatement.executeQuery(); + while (userSet.next()) { + if (userSet.getBoolean("owner")) { + owners.addPlayer(userSet.getString("name")); + } else { + members.addPlayer(userSet.getString("name")); + } + } + } catch (SQLException ex) { + logger.warning("Unable to load users for region " + region.getId().toLowerCase() + ": " + ex.getMessage()); + } finally { + closeQuietly(userSet); + closeQuietly(usersStatement); + } + + PreparedStatement groupsStatement = null; + ResultSet groupSet = null; + try { + groupsStatement = this.conn.prepareStatement( + "SELECT " + + "`group`.`name`, " + + "`region_groups`.`owner` " + + "FROM `" + config.sqlTablePrefix + "region_groups` AS `region_groups` " + + "LEFT JOIN `" + config.sqlTablePrefix + "group` AS `group` ON ( " + + "`region_groups`.`group_id` = " + + "`group`.`id`) " + + "WHERE `region_groups`.`region_id` = ? " + + "AND `region_groups`.`world_id` = " + this.worldId + ); + + groupsStatement.setString(1, region.getId().toLowerCase()); + groupSet = groupsStatement.executeQuery(); + while (groupSet.next()) { + if (groupSet.getBoolean("owner")) { + owners.addGroup(groupSet.getString("name")); + } else { + members.addGroup(groupSet.getString("name")); + } + } + } catch (SQLException ex) { + logger.warning("Unable to load groups for region " + region.getId().toLowerCase() + ": " + ex.getMessage()); + } finally { + closeQuietly(groupSet); + closeQuietly(groupsStatement); + } + + region.setOwners(owners); + region.setMembers(members); + } + + private void loadGlobal() { + Map regions = + new HashMap(); + + PreparedStatement globalRegionStatement = null; + ResultSet globalResultSet = null; + try { + globalRegionStatement = this.conn.prepareStatement( + "SELECT " + + "`region`.`id`, " + + "`region`.`priority`, " + + "`parent`.`id` AS `parent` " + + "FROM `" + config.sqlTablePrefix + "region` AS `region` " + + "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " + + "ON (`region`.`parent` = `parent`.`id` " + + "AND `region`.`world_id` = `parent`.`world_id`) " + + "WHERE `region`.`type` = 'global' " + + "AND `region`.`world_id` = ? " + ); + + globalRegionStatement.setInt(1, this.worldId); + globalResultSet = globalRegionStatement.executeQuery(); + + while (globalResultSet.next()) { + ProtectedRegion region = new GlobalProtectedRegion(globalResultSet.getString("id")); + + region.setPriority(globalResultSet.getInt("priority")); + + this.loadFlags(region); + this.loadOwnersAndMembers(region); + + regions.put(globalResultSet.getString("id"), region); + + String parentId = globalResultSet.getString("parent"); + if (parentId != null) { + parentSets.put(region, parentId); + } + } + } catch (SQLException ex) { + ex.printStackTrace(); + logger.warning("Unable to load regions from sql database: " + ex.getMessage()); + Throwable t = ex.getCause(); + while (t != null) { + logger.warning("\t\tCause: " + t.getMessage()); + t = t.getCause(); + } + } finally { + closeQuietly(globalResultSet); + closeQuietly(globalRegionStatement); + } + + globalRegions = regions; + } + + private void loadCuboid() { + Map regions = new HashMap(); + + PreparedStatement cuboidRegionStatement = null; + ResultSet cuboidResultSet = null; + try { + cuboidRegionStatement = this.conn.prepareStatement( + "SELECT " + + "`region_cuboid`.`min_z`, " + + "`region_cuboid`.`min_y`, " + + "`region_cuboid`.`min_x`, " + + "`region_cuboid`.`max_z`, " + + "`region_cuboid`.`max_y`, " + + "`region_cuboid`.`max_x`, " + + "`region`.`id`, " + + "`region`.`priority`, " + + "`parent`.`id` AS `parent` " + + "FROM `" + config.sqlTablePrefix + "region_cuboid` AS `region_cuboid` " + + "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `region` " + + "ON (`region_cuboid`.`region_id` = `region`.`id` " + + "AND `region_cuboid`.`world_id` = `region`.`world_id`) " + + "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " + + "ON (`region`.`parent` = `parent`.`id` " + + "AND `region`.`world_id` = `parent`.`world_id`) " + + "WHERE `region`.`world_id` = ? " + ); + + cuboidRegionStatement.setInt(1, this.worldId); + cuboidResultSet = cuboidRegionStatement.executeQuery(); + + while (cuboidResultSet.next()) { + Vector pt1 = new Vector( + cuboidResultSet.getInt("min_x"), + cuboidResultSet.getInt("min_y"), + cuboidResultSet.getInt("min_z") + ); + Vector pt2 = new Vector( + cuboidResultSet.getInt("max_x"), + cuboidResultSet.getInt("max_y"), + cuboidResultSet.getInt("max_z") + ); + + BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector(); + BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector(); + ProtectedRegion region = new ProtectedCuboidRegion( + cuboidResultSet.getString("id"), + min, + max + ); + + region.setPriority(cuboidResultSet.getInt("priority")); + + this.loadFlags(region); + this.loadOwnersAndMembers(region); + + regions.put(cuboidResultSet.getString("id"), region); + + String parentId = cuboidResultSet.getString("parent"); + if (parentId != null) { + parentSets.put(region, parentId); + } + } + + } catch (SQLException ex) { + ex.printStackTrace(); + logger.warning("Unable to load regions from sql database: " + ex.getMessage()); + Throwable t = ex.getCause(); + while (t != null) { + logger.warning("\t\tCause: " + t.getMessage()); + t = t.getCause(); + } + } finally { + closeQuietly(cuboidResultSet); + closeQuietly(cuboidRegionStatement); + } + + cuboidRegions = regions; + } + + private void loadPoly2d() { + Map regions = new HashMap(); + + PreparedStatement poly2dRegionStatement = null; + ResultSet poly2dResultSet = null; + PreparedStatement poly2dVectorStatement = null; + try { + poly2dRegionStatement = this.conn.prepareStatement( + "SELECT " + + "`region_poly2d`.`min_y`, " + + "`region_poly2d`.`max_y`, " + + "`region`.`id`, " + + "`region`.`priority`, " + + "`parent`.`id` AS `parent` " + + "FROM `" + config.sqlTablePrefix + "region_poly2d` AS `region_poly2d` " + + "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `region` " + + "ON (`region_poly2d`.`region_id` = `region`.`id` " + + "AND `region_poly2d`.`world_id` = `region`.`world_id`) " + + "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " + + "ON (`region`.`parent` = `parent`.`id` " + + "AND `region`.`world_id` = `parent`.`world_id`) " + + "WHERE `region`.`world_id` = ? " + ); + + poly2dRegionStatement.setInt(1, this.worldId); + poly2dResultSet = poly2dRegionStatement.executeQuery(); + + poly2dVectorStatement = this.conn.prepareStatement( + "SELECT " + + "`region_poly2d_point`.`x`, " + + "`region_poly2d_point`.`z` " + + "FROM `" + config.sqlTablePrefix + "region_poly2d_point` AS `region_poly2d_point` " + + "WHERE `region_poly2d_point`.`region_id` = ? " + + "AND `region_poly2d_point`.`world_id` = " + this.worldId + ); + + while (poly2dResultSet.next()) { + String id = poly2dResultSet.getString("id"); + + Integer minY = poly2dResultSet.getInt("min_y"); + Integer maxY = poly2dResultSet.getInt("max_y"); + List points = new ArrayList(); + + poly2dVectorStatement.setString(1, id); + ResultSet poly2dVectorResultSet = poly2dVectorStatement.executeQuery(); + + while (poly2dVectorResultSet.next()) { + points.add(new BlockVector2D( + poly2dVectorResultSet.getInt("x"), + poly2dVectorResultSet.getInt("z") + )); + } + + if (points.size() < 3) { + logger.warning(String.format("Invalid polygonal region '%s': region only has %d point(s). Ignoring.", id, points.size())); + continue; + } + + closeQuietly(poly2dVectorResultSet); + + ProtectedRegion region = new ProtectedPolygonalRegion(id, points, minY, maxY); + + region.setPriority(poly2dResultSet.getInt("priority")); + + this.loadFlags(region); + this.loadOwnersAndMembers(region); + + regions.put(poly2dResultSet.getString("id"), region); + + String parentId = poly2dResultSet.getString("parent"); + if (parentId != null) { + parentSets.put(region, parentId); + } + } + } catch (SQLException ex) { + ex.printStackTrace(); + logger.warning("Unable to load regions from sql database: " + ex.getMessage()); + Throwable t = ex.getCause(); + while (t != null) { + logger.warning("\t\tCause: " + t.getMessage()); + t = t.getCause(); + } + } finally { + closeQuietly(poly2dResultSet); + closeQuietly(poly2dRegionStatement); + closeQuietly(poly2dVectorStatement); + } + + poly2dRegions = regions; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java index 3bd7a8ab..0eac245d 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java @@ -43,7 +43,7 @@ * asynchronously loading and saving region data while only allowing one * single operation (either load or save) occurring at a given time. */ -abstract class AbstractAsynchronousDatabase extends AbstractProtectionDatabase { +public abstract class AbstractAsynchronousDatabase extends AbstractProtectionDatabase { private static final Logger log = Logger.getLogger(AbstractAsynchronousDatabase.class.getName()); diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java index ff5470da..e237a22e 100755 --- a/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java @@ -20,1171 +20,15 @@ package com.sk89q.worldguard.protection.databases; -import com.sk89q.worldedit.BlockVector; -import com.sk89q.worldedit.BlockVector2D; -import com.sk89q.worldedit.Vector; import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.flags.Flag; -import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; -import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.DumperOptions.FlowStyle; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.SafeConstructor; -import org.yaml.snakeyaml.error.YAMLException; -import org.yaml.snakeyaml.representer.Representer; +import com.sk89q.worldguard.internal.protection.database.mysql.MySQLDatabaseImpl; -import java.sql.*; -import java.util.*; -import java.util.logging.Level; import java.util.logging.Logger; -public class MySQLDatabase extends AbstractProtectionDatabase { - private final Logger logger; +public class MySQLDatabase extends MySQLDatabaseImpl { - private Yaml yaml; - - private Map regions; - - private Map cuboidRegions; - private Map poly2dRegions; - private Map globalRegions; - private Map parentSets; - - private final ConfigurationManager config; - - private Connection conn; - private int worldDbId = -1; // The database will never have an id of -1; - - public MySQLDatabase(ConfigurationManager config, String world, Logger logger) throws ProtectionDatabaseException { - this.config = config; - String world1 = world; - this.logger = logger; - - PreparedStatement worldStmt = null; - ResultSet worldResult = null; - PreparedStatement insertWorldStatement = null; - try { - connect(); - - PreparedStatement verTest = null; - try { - // Test if the database is up to date, if not throw a critical error - verTest = this.conn.prepareStatement( - "SELECT `world_id` FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 0,1;" - ); - verTest.execute(); - } catch (SQLException ex) { - throw new InvalidTableFormatException( - "region_storage_update_20110325.sql" - ); - } finally { - closeResource(verTest); - } - - worldStmt = conn.prepareStatement( - "SELECT `id` FROM " + - "`" + config.sqlTablePrefix + "world` " + - "WHERE `name` = ? LIMIT 0,1" - ); - - worldStmt.setString(1, world1); - worldResult = worldStmt.executeQuery(); - - if (worldResult.first()) { - this.worldDbId = worldResult.getInt("id"); - } else { - insertWorldStatement = this.conn.prepareStatement( - "INSERT INTO " + - "`" + config.sqlTablePrefix + "world` " + - "(`id`, `name`) VALUES (null, ?)", - Statement.RETURN_GENERATED_KEYS - ); - - insertWorldStatement.setString(1, world); - insertWorldStatement.execute(); - ResultSet generatedKeys = insertWorldStatement.getGeneratedKeys(); - if (generatedKeys.first()) { - this.worldDbId = generatedKeys.getInt(1); - } - } - } catch (SQLException ex) { - logger.log(Level.SEVERE, ex.getMessage(), ex); - // We havn't connected to the databases, or there was an error - // initialising the world record, so there is no point continuing - return; - } finally { - closeResource(worldResult); - closeResource(worldStmt); - closeResource(insertWorldStatement); - } - - if (this.worldDbId <= 0) { - logger.log(Level.SEVERE, "Could not find or create the world"); - // There was an error initialising the world record, so there is - // no point continuing - return; - } - - DumperOptions options = new DumperOptions(); - options.setIndent(2); - options.setDefaultFlowStyle(FlowStyle.FLOW); - Representer representer = new Representer(); - representer.setDefaultFlowStyle(FlowStyle.FLOW); - - // We have to use this in order to properly save non-string values - yaml = new Yaml(new SafeConstructor(), new Representer(), options); + public MySQLDatabase(ConfigurationManager config, String worldName, Logger logger) throws ProtectionDatabaseException { + super(config, worldName, logger); } - private void connect() throws SQLException { - if (conn != null) { - // Check if the connection is still alive/valid. - try { - conn.isValid(2); - } catch (SQLException ex) { - // Test if validation failed because the connection is dead, - // and if it is mark the connection as closed (the MySQL Driver - // does not ensure that the connection is marked as closed unless - // the close() method has been called. - if ("08S01".equals(ex.getSQLState())) { - conn.close(); - } - } - } - if (conn == null || conn.isClosed()) { - conn = DriverManager.getConnection(config.sqlDsn, config.sqlUsername, config.sqlPassword); - } - } - - private void loadFlags(ProtectedRegion region) { - // @TODO: Iterate _ONCE_ - PreparedStatement flagsStatement = null; - ResultSet flagsResultSet = null; - try { - flagsStatement = this.conn.prepareStatement( - "SELECT " + - "`region_flag`.`flag`, " + - "`region_flag`.`value` " + - "FROM `" + config.sqlTablePrefix + "region_flag` AS `region_flag` " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldDbId - ); - - flagsStatement.setString(1, region.getId().toLowerCase()); - flagsResultSet = flagsStatement.executeQuery(); - - Map regionFlags = new HashMap(); - while (flagsResultSet.next()) { - regionFlags.put( - flagsResultSet.getString("flag"), - sqlUnmarshal(flagsResultSet.getString("value")) - ); - } - - // @TODO: Make this better - for (Flag flag : DefaultFlag.getFlags()) { - Object o = regionFlags.get(flag.getName()); - if (o != null) { - setFlag(region, flag, o); - } - } - } catch (SQLException ex) { - logger.warning( - "Unable to load flags for region " - + region.getId().toLowerCase() + ": " + ex.getMessage() - ); - } finally { - closeResource(flagsResultSet); - closeResource(flagsStatement); - } - } - - private void setFlag(ProtectedRegion region, Flag flag, Object rawValue) { - T val = flag.unmarshal(rawValue); - if (val == null) { - logger.warning("Failed to parse flag '" + flag.getName() - + "' with value '" + rawValue.toString() + "'"); - return; - } - region.setFlag(flag, val); - } - - private void loadOwnersAndMembers(ProtectedRegion region) { - DefaultDomain owners = new DefaultDomain(); - DefaultDomain members = new DefaultDomain(); - - ResultSet userSet = null; - PreparedStatement usersStatement = null; - try { - usersStatement = this.conn.prepareStatement( - "SELECT " + - "`user`.`name`, " + - "`region_players`.`owner` " + - "FROM `" + config.sqlTablePrefix + "region_players` AS `region_players` " + - "LEFT JOIN `" + config.sqlTablePrefix + "user` AS `user` ON ( " + - "`region_players`.`user_id` = " + - "`user`.`id`) " + - "WHERE `region_players`.`region_id` = ? " + - "AND `region_players`.`world_id` = " + this.worldDbId - ); - - usersStatement.setString(1, region.getId().toLowerCase()); - userSet = usersStatement.executeQuery(); - while(userSet.next()) { - if (userSet.getBoolean("owner")) { - owners.addPlayer(userSet.getString("name")); - } else { - members.addPlayer(userSet.getString("name")); - } - } - } catch (SQLException ex) { - logger.warning("Unable to load users for region " + region.getId().toLowerCase() + ": " + ex.getMessage()); - } finally { - closeResource(userSet); - closeResource(usersStatement); - } - - PreparedStatement groupsStatement = null; - ResultSet groupSet = null; - try { - groupsStatement = this.conn.prepareStatement( - "SELECT " + - "`group`.`name`, " + - "`region_groups`.`owner` " + - "FROM `" + config.sqlTablePrefix + "region_groups` AS `region_groups` " + - "LEFT JOIN `" + config.sqlTablePrefix + "group` AS `group` ON ( " + - "`region_groups`.`group_id` = " + - "`group`.`id`) " + - "WHERE `region_groups`.`region_id` = ? " + - "AND `region_groups`.`world_id` = " + this.worldDbId - ); - - groupsStatement.setString(1, region.getId().toLowerCase()); - groupSet = groupsStatement.executeQuery(); - while(groupSet.next()) { - if (groupSet.getBoolean("owner")) { - owners.addGroup(groupSet.getString("name")); - } else { - members.addGroup(groupSet.getString("name")); - } - } - } catch (SQLException ex) { - logger.warning("Unable to load groups for region " + region.getId().toLowerCase() + ": " + ex.getMessage()); - } finally { - closeResource(groupSet); - closeResource(groupsStatement); - } - - region.setOwners(owners); - region.setMembers(members); - } - - private void loadGlobal() { - Map regions = - new HashMap(); - - PreparedStatement globalRegionStatement = null; - ResultSet globalResultSet = null; - try { - globalRegionStatement = this.conn.prepareStatement( - "SELECT " + - "`region`.`id`, " + - "`region`.`priority`, " + - "`parent`.`id` AS `parent` " + - "FROM `" + config.sqlTablePrefix + "region` AS `region` " + - "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " + - "ON (`region`.`parent` = `parent`.`id` " + - "AND `region`.`world_id` = `parent`.`world_id`) " + - "WHERE `region`.`type` = 'global' " + - "AND `region`.`world_id` = ? " - ); - - globalRegionStatement.setInt(1, this.worldDbId); - globalResultSet = globalRegionStatement.executeQuery(); - - while (globalResultSet.next()) { - ProtectedRegion region = new GlobalProtectedRegion(globalResultSet.getString("id")); - - region.setPriority(globalResultSet.getInt("priority")); - - this.loadFlags(region); - this.loadOwnersAndMembers(region); - - regions.put(globalResultSet.getString("id"), region); - - String parentId = globalResultSet.getString("parent"); - if (parentId != null) { - parentSets.put(region, parentId); - } - } - } catch (SQLException ex) { - ex.printStackTrace(); - logger.warning("Unable to load regions from sql database: " + ex.getMessage()); - Throwable t = ex.getCause(); - while (t != null) { - logger.warning("\t\tCause: " + t.getMessage()); - t = t.getCause(); - } - } finally { - closeResource(globalResultSet); - closeResource(globalRegionStatement); - } - - globalRegions = regions; - } - - private void loadCuboid() { - Map regions = - new HashMap(); - - PreparedStatement cuboidRegionStatement = null; - ResultSet cuboidResultSet = null; - try { - cuboidRegionStatement = this.conn.prepareStatement( - "SELECT " + - "`region_cuboid`.`min_z`, " + - "`region_cuboid`.`min_y`, " + - "`region_cuboid`.`min_x`, " + - "`region_cuboid`.`max_z`, " + - "`region_cuboid`.`max_y`, " + - "`region_cuboid`.`max_x`, " + - "`region`.`id`, " + - "`region`.`priority`, " + - "`parent`.`id` AS `parent` " + - "FROM `" + config.sqlTablePrefix + "region_cuboid` AS `region_cuboid` " + - "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `region` " + - "ON (`region_cuboid`.`region_id` = `region`.`id` " + - "AND `region_cuboid`.`world_id` = `region`.`world_id`) " + - "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " + - "ON (`region`.`parent` = `parent`.`id` " + - "AND `region`.`world_id` = `parent`.`world_id`) " + - "WHERE `region`.`world_id` = ? " - ); - - cuboidRegionStatement.setInt(1, this.worldDbId); - cuboidResultSet = cuboidRegionStatement.executeQuery(); - - while (cuboidResultSet.next()) { - Vector pt1 = new Vector( - cuboidResultSet.getInt("min_x"), - cuboidResultSet.getInt("min_y"), - cuboidResultSet.getInt("min_z") - ); - Vector pt2 = new Vector( - cuboidResultSet.getInt("max_x"), - cuboidResultSet.getInt("max_y"), - cuboidResultSet.getInt("max_z") - ); - - BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector(); - BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector(); - ProtectedRegion region = new ProtectedCuboidRegion( - cuboidResultSet.getString("id"), - min, - max - ); - - region.setPriority(cuboidResultSet.getInt("priority")); - - this.loadFlags(region); - this.loadOwnersAndMembers(region); - - regions.put(cuboidResultSet.getString("id"), region); - - String parentId = cuboidResultSet.getString("parent"); - if (parentId != null) { - parentSets.put(region, parentId); - } - } - - } catch (SQLException ex) { - ex.printStackTrace(); - logger.warning("Unable to load regions from sql database: " + ex.getMessage()); - Throwable t = ex.getCause(); - while (t != null) { - logger.warning("\t\tCause: " + t.getMessage()); - t = t.getCause(); - } - } finally { - closeResource(cuboidResultSet); - closeResource(cuboidRegionStatement); - } - - cuboidRegions = regions; - } - - private void loadPoly2d() { - Map regions = - new HashMap(); - - PreparedStatement poly2dRegionStatement = null; - ResultSet poly2dResultSet = null; - PreparedStatement poly2dVectorStatement = null; - try { - poly2dRegionStatement = this.conn.prepareStatement( - "SELECT " + - "`region_poly2d`.`min_y`, " + - "`region_poly2d`.`max_y`, " + - "`region`.`id`, " + - "`region`.`priority`, " + - "`parent`.`id` AS `parent` " + - "FROM `" + config.sqlTablePrefix + "region_poly2d` AS `region_poly2d` " + - "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `region` " + - "ON (`region_poly2d`.`region_id` = `region`.`id` " + - "AND `region_poly2d`.`world_id` = `region`.`world_id`) " + - "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " + - "ON (`region`.`parent` = `parent`.`id` " + - "AND `region`.`world_id` = `parent`.`world_id`) " + - "WHERE `region`.`world_id` = ? " - ); - - poly2dRegionStatement.setInt(1, this.worldDbId); - poly2dResultSet = poly2dRegionStatement.executeQuery(); - - poly2dVectorStatement = this.conn.prepareStatement( - "SELECT " + - "`region_poly2d_point`.`x`, " + - "`region_poly2d_point`.`z` " + - "FROM `" + config.sqlTablePrefix + "region_poly2d_point` AS `region_poly2d_point` " + - "WHERE `region_poly2d_point`.`region_id` = ? " + - "AND `region_poly2d_point`.`world_id` = " + this.worldDbId - ); - - while (poly2dResultSet.next()) { - String id = poly2dResultSet.getString("id"); - - Integer minY = poly2dResultSet.getInt("min_y"); - Integer maxY = poly2dResultSet.getInt("max_y"); - List points = new ArrayList(); - - poly2dVectorStatement.setString(1, id); - ResultSet poly2dVectorResultSet = poly2dVectorStatement.executeQuery(); - - while(poly2dVectorResultSet.next()) { - points.add(new BlockVector2D( - poly2dVectorResultSet.getInt("x"), - poly2dVectorResultSet.getInt("z") - )); - } - - if (points.size() < 3) { - logger.warning(String.format("Invalid polygonal region '%s': region only has %d point(s). Ignoring.", id, points.size())); - continue; - } - - closeResource(poly2dVectorResultSet); - - ProtectedRegion region = new ProtectedPolygonalRegion(id, points, minY, maxY); - - region.setPriority(poly2dResultSet.getInt("priority")); - - this.loadFlags(region); - this.loadOwnersAndMembers(region); - - regions.put(poly2dResultSet.getString("id"), region); - - String parentId = poly2dResultSet.getString("parent"); - if (parentId != null) { - parentSets.put(region, parentId); - } - } - } catch (SQLException ex) { - ex.printStackTrace(); - logger.warning("Unable to load regions from sql database: " + ex.getMessage()); - Throwable t = ex.getCause(); - while (t != null) { - logger.warning("\t\tCause: " + t.getMessage()); - t = t.getCause(); - } - } finally { - closeResource(poly2dResultSet); - closeResource(poly2dRegionStatement); - closeResource(poly2dVectorStatement); - } - - poly2dRegions = regions; - } - - @Override - public void load() throws ProtectionDatabaseException { - try { - connect(); - } catch (SQLException ex) { - throw new ProtectionDatabaseException(ex); - } - - parentSets = new HashMap(); - - // We load the cuboid regions first, as this is likely to be the - // largest dataset. This should save time in regards to the putAll()s - this.loadCuboid(); - Map regions = this.cuboidRegions; - this.cuboidRegions = null; - - this.loadPoly2d(); - regions.putAll(this.poly2dRegions); - this.poly2dRegions = null; - - this.loadGlobal(); - regions.putAll(this.globalRegions); - this.globalRegions = null; - - // Relink parents // Taken verbatim from YAMLDatabase - for (Map.Entry entry : parentSets.entrySet()) { - ProtectedRegion parent = regions.get(entry.getValue()); - if (parent != null) { - try { - entry.getKey().setParent(parent); - } catch (CircularInheritanceException e) { - logger.warning("Circular inheritance detect with '" - + entry.getValue() + "' detected as a parent"); - } - } else { - logger.warning("Unknown region parent: " + entry.getValue()); - } - } - - this.regions = regions; - } - - - /* - * Returns the database id for the user - * If it doesn't exits it adds the user and returns the id. - */ - private Map getUserIds(String... usernames) { - Map users = new HashMap(); - - if (usernames.length < 1) return users; - - ResultSet findUsersResults = null; - PreparedStatement insertUserStatement = null; - PreparedStatement findUsersStatement = null; - try { - findUsersStatement = this.conn.prepareStatement( - String.format( - "SELECT " + - "`user`.`id`, " + - "`user`.`name` " + - "FROM `" + config.sqlTablePrefix + "user` AS `user` " + - "WHERE `name` IN (%s)", - RegionDBUtil.preparePlaceHolders(usernames.length) - ) - ); - - RegionDBUtil.setValues(findUsersStatement, usernames); - - findUsersResults = findUsersStatement.executeQuery(); - - while(findUsersResults.next()) { - users.put(findUsersResults.getString("name"), findUsersResults.getInt("id")); - } - - insertUserStatement = this.conn.prepareStatement( - "INSERT INTO " + - "`" + config.sqlTablePrefix + "user` ( " + - "`id`, " + - "`name`" + - ") VALUES (null, ?)", - Statement.RETURN_GENERATED_KEYS - ); - - for (String username : usernames) { - if (!users.containsKey(username)) { - insertUserStatement.setString(1, username); - insertUserStatement.execute(); - ResultSet generatedKeys = insertUserStatement.getGeneratedKeys(); - if (generatedKeys.first()) { - users.put(username, generatedKeys.getInt(1)); - } else { - logger.warning("Could not get the database id for user " + username); - } - } - } - } catch (SQLException ex) { - ex.printStackTrace(); - logger.warning("Could not get the database id for the users " + usernames.toString() + "\n\t" + ex.getMessage()); - Throwable t = ex.getCause(); - while (t != null) { - logger.warning(t.getMessage()); - t = t.getCause(); - } - } finally { - closeResource(findUsersResults); - closeResource(findUsersStatement); - closeResource(insertUserStatement); - } - - return users; - } - - - /* - * Returns the database id for the groups - * If it doesn't exits it adds the group and returns the id. - */ - private Map getGroupIds(String... groupnames) { - Map groups = new HashMap(); - - if (groupnames.length < 1) return groups; - - PreparedStatement findGroupsStatement = null; - ResultSet findGroupsResults = null; - PreparedStatement insertGroupStatement = null; - try { - findGroupsStatement = this.conn.prepareStatement( - String.format( - "SELECT " + - "`group`.`id`, " + - "`group`.`name` " + - "FROM `" + config.sqlTablePrefix + "group` AS `group` " + - "WHERE `name` IN (%s)", - RegionDBUtil.preparePlaceHolders(groupnames.length) - ) - ); - - RegionDBUtil.setValues(findGroupsStatement, groupnames); - - findGroupsResults = findGroupsStatement.executeQuery(); - - while(findGroupsResults.next()) { - groups.put(findGroupsResults.getString("name"), findGroupsResults.getInt("id")); - } - - insertGroupStatement = this.conn.prepareStatement( - "INSERT INTO " + - "`" + config.sqlTablePrefix + "group` ( " + - "`id`, " + - "`name`" + - ") VALUES (null, ?)", - Statement.RETURN_GENERATED_KEYS - ); - - for (String groupname : groupnames) { - if (!groups.containsKey(groupname)) { - insertGroupStatement.setString(1, groupname); - insertGroupStatement.execute(); - ResultSet generatedKeys = insertGroupStatement.getGeneratedKeys(); - if (generatedKeys.first()) { - groups.put(groupname, generatedKeys.getInt(1)); - } else { - logger.warning("Could not get the database id for user " + groupname); - } - } - } - } catch (SQLException ex) { - logger.warning("Could not get the database id for the groups " + groupnames.toString() + ex.getMessage()); - } finally { - closeResource(findGroupsResults); - closeResource(findGroupsStatement); - closeResource(insertGroupStatement); - } - - return groups; - } - - /* - * As we don't get notified on the creation/removal of regions: - * 1) We get a list of all of the in-database regions - * 2) We iterate over all of the in-memory regions - * 2a) If the region is in the database, we update the database and - * remove the region from the in-database list - * b) If the region is not in the database, we insert it - * 3) We iterate over what remains of the in-database list and remove - * them from the database - * - * TODO: Look at adding/removing/updating the database when the in - * memory region is created/remove/updated - * - * @see com.sk89q.worldguard.protection.databases.ProtectionDatabase#save() - */ - @Override - public void save() throws ProtectionDatabaseException { - try { - connect(); - } catch (SQLException ex) { - throw new ProtectionDatabaseException(ex); - } - - List regionsInDatabase = new ArrayList(); - - PreparedStatement getAllRegionsStatement = null; - ResultSet getAllRegionsResult = null; - try { - getAllRegionsStatement = this.conn.prepareStatement( - "SELECT `region`.`id` FROM " + - "`" + config.sqlTablePrefix + "region` AS `region` " + - "WHERE `world_id` = ? " - ); - - getAllRegionsStatement.setInt(1, this.worldDbId); - getAllRegionsResult = getAllRegionsStatement.executeQuery(); - - while(getAllRegionsResult.next()) { - regionsInDatabase.add(getAllRegionsResult.getString("id")); - } - } catch (SQLException ex) { - logger.warning("Could not get region list for save comparison: " + ex.getMessage()); - } finally { - closeResource(getAllRegionsResult); - closeResource(getAllRegionsStatement); - } - - for (Map.Entry entry : regions.entrySet()) { - String name = entry.getKey(); - ProtectedRegion region = entry.getValue(); - - try { - if (regionsInDatabase.contains(name)) { - regionsInDatabase.remove(name); - - if (region instanceof ProtectedCuboidRegion) { - updateRegionCuboid( (ProtectedCuboidRegion) region ); - } else if (region instanceof ProtectedPolygonalRegion) { - updateRegionPoly2D( (ProtectedPolygonalRegion) region ); - } else if (region instanceof GlobalProtectedRegion) { - updateRegionGlobal( (GlobalProtectedRegion) region ); - } else { - this.updateRegion(region, region.getClass().getCanonicalName()); - } - } else { - if (region instanceof ProtectedCuboidRegion) { - insertRegionCuboid( (ProtectedCuboidRegion) region ); - } else if (region instanceof ProtectedPolygonalRegion) { - insertRegionPoly2D( (ProtectedPolygonalRegion) region ); - } else if (region instanceof GlobalProtectedRegion) { - insertRegionGlobal( (GlobalProtectedRegion) region ); - } else { - this.insertRegion(region, region.getClass().getCanonicalName()); - } - } - } catch (SQLException ex) { - logger.warning("Could not save region " + region.getId().toLowerCase() + ": " + ex.getMessage()); - throw new ProtectionDatabaseException(ex); - } - } - - for (Map.Entry entry : regions.entrySet()) { - PreparedStatement setParentStatement = null; - try { - if (entry.getValue().getParent() == null) continue; - - setParentStatement = this.conn.prepareStatement( - "UPDATE `" + config.sqlTablePrefix + "region` SET " + - "`parent` = ? " + - "WHERE `id` = ? AND `world_id` = " + this.worldDbId - ); - - setParentStatement.setString(1, entry.getValue().getParent().getId().toLowerCase()); - setParentStatement.setString(2, entry.getValue().getId().toLowerCase()); - - setParentStatement.execute(); - } catch (SQLException ex) { - logger.warning("Could not save region parents " + entry.getValue().getId().toLowerCase() + ": " + ex.getMessage()); - throw new ProtectionDatabaseException(ex); - } finally { - closeResource(setParentStatement); - } - } - - for (String name : regionsInDatabase) { - PreparedStatement removeRegion = null; - try { - removeRegion = this.conn.prepareStatement( - "DELETE FROM `" + config.sqlTablePrefix + "region` WHERE `id` = ? " - ); - - removeRegion.setString(1, name); - removeRegion.execute(); - } catch (SQLException ex) { - logger.warning("Could not remove region from database " + name + ": " + ex.getMessage()); - } finally { - closeResource(removeRegion); - } - } - - } - - private void updateFlags(ProtectedRegion region) throws SQLException { - PreparedStatement clearCurrentFlagStatement = null; - try { - clearCurrentFlagStatement = this.conn.prepareStatement( - "DELETE FROM `" + config.sqlTablePrefix + "region_flag` " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldDbId - ); - - clearCurrentFlagStatement.setString(1, region.getId().toLowerCase()); - clearCurrentFlagStatement.execute(); - - for (Map.Entry, Object> entry : region.getFlags().entrySet()) { - if (entry.getValue() == null) continue; - - Object flag = sqlMarshal(marshalFlag(entry.getKey(), entry.getValue())); - - PreparedStatement insertFlagStatement = null; - try { - insertFlagStatement = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_flag` ( " + - "`id`, " + - "`region_id`, " + - "`world_id`, " + - "`flag`, " + - "`value` " + - ") VALUES (null, ?, " + this.worldDbId + ", ?, ?)" - ); - - insertFlagStatement.setString(1, region.getId().toLowerCase()); - insertFlagStatement.setString(2, entry.getKey().getName()); - insertFlagStatement.setObject(3, flag); - - insertFlagStatement.execute(); - } finally { - closeResource(insertFlagStatement); - } - } - } finally { - closeResource(clearCurrentFlagStatement); - } - } - - private void updatePlayerAndGroups(ProtectedRegion region, Boolean owners) throws SQLException { - DefaultDomain domain; - - if (owners) { - domain = region.getOwners(); - } else { - domain = region.getMembers(); - } - - PreparedStatement deleteUsersForRegion = null; - PreparedStatement insertUsersForRegion = null; - PreparedStatement deleteGroupsForRegion = null; - PreparedStatement insertGroupsForRegion = null; - - try { - deleteUsersForRegion = this.conn.prepareStatement( - "DELETE FROM `" + config.sqlTablePrefix + "region_players` " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldDbId + " " + - "AND `owner` = ?" - ); - - deleteUsersForRegion.setString(1, region.getId().toLowerCase()); - deleteUsersForRegion.setBoolean(2, owners); - deleteUsersForRegion.execute(); - - insertUsersForRegion = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_players` " + - "(`region_id`, `world_id`, `user_id`, `owner`) " + - "VALUES (?, " + this.worldDbId + ", ?, ?)" - ); - - Set var = domain.getPlayers(); - - for (Integer player : getUserIds(var.toArray(new String[var.size()])).values()) { - insertUsersForRegion.setString(1, region.getId().toLowerCase()); - insertUsersForRegion.setInt(2, player); - insertUsersForRegion.setBoolean(3, owners); - - insertUsersForRegion.execute(); - } - - deleteGroupsForRegion = this.conn.prepareStatement( - "DELETE FROM `" + config.sqlTablePrefix + "region_groups` " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldDbId + " " + - "AND `owner` = ?" - ); - - deleteGroupsForRegion.setString(1, region.getId().toLowerCase()); - deleteGroupsForRegion.setBoolean(2, owners); - deleteGroupsForRegion.execute(); - - insertGroupsForRegion = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_groups` " + - "(`region_id`, `world_id`, `group_id`, `owner`) " + - "VALUES (?, " + this.worldDbId + ", ?, ?)" - ); - - Set groupVar = domain.getGroups(); - for (Integer group : getGroupIds(groupVar.toArray(new String[groupVar.size()])).values()) { - insertGroupsForRegion.setString(1, region.getId().toLowerCase()); - insertGroupsForRegion.setInt(2, group); - insertGroupsForRegion.setBoolean(3, owners); - - insertGroupsForRegion.execute(); - } - } finally { - closeResource(deleteGroupsForRegion); - closeResource(deleteUsersForRegion); - closeResource(insertGroupsForRegion); - closeResource(insertUsersForRegion); - } - } - - @SuppressWarnings("unchecked") - private Object marshalFlag(Flag flag, Object val) { - return flag.marshal( (V) val ); - } - - private void insertRegion(ProtectedRegion region, String type) throws SQLException { - PreparedStatement insertRegionStatement = null; - try { - insertRegionStatement = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region` (" + - "`id`, " + - "`world_id`, " + - "`type`, " + - "`priority`, " + - "`parent` " + - ") VALUES (?, ?, ?, ?, null)" - ); - - insertRegionStatement.setString(1, region.getId().toLowerCase()); - insertRegionStatement.setInt(2, this.worldDbId); - insertRegionStatement.setString(3, type); - insertRegionStatement.setInt(4, region.getPriority()); - - insertRegionStatement.execute(); - } finally { - closeResource(insertRegionStatement); - } - - updateFlags(region); - - updatePlayerAndGroups(region, false); - updatePlayerAndGroups(region, true); - } - - private void insertRegionCuboid(ProtectedCuboidRegion region) throws SQLException { - insertRegion(region, "cuboid"); - - PreparedStatement insertCuboidRegionStatement = null; - try { - insertCuboidRegionStatement = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_cuboid` (" + - "`region_id`, " + - "`world_id`, " + - "`min_z`, " + - "`min_y`, " + - "`min_x`, " + - "`max_z`, " + - "`max_y`, " + - "`max_x` " + - ") VALUES (?, " + this.worldDbId + ", ?, ?, ?, ?, ?, ?)" - ); - - BlockVector min = region.getMinimumPoint(); - BlockVector max = region.getMaximumPoint(); - - insertCuboidRegionStatement.setString(1, region.getId().toLowerCase()); - insertCuboidRegionStatement.setInt(2, min.getBlockZ()); - insertCuboidRegionStatement.setInt(3, min.getBlockY()); - insertCuboidRegionStatement.setInt(4, min.getBlockX()); - insertCuboidRegionStatement.setInt(5, max.getBlockZ()); - insertCuboidRegionStatement.setInt(6, max.getBlockY()); - insertCuboidRegionStatement.setInt(7, max.getBlockX()); - - insertCuboidRegionStatement.execute(); - } finally { - closeResource(insertCuboidRegionStatement); - } - } - - private void insertRegionPoly2D(ProtectedPolygonalRegion region) throws SQLException { - insertRegion(region, "poly2d"); - - PreparedStatement insertPoly2dRegionStatement = null; - try { - insertPoly2dRegionStatement = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_poly2d` (" + - "`region_id`, " + - "`world_id`, " + - "`max_y`, " + - "`min_y` " + - ") VALUES (?, " + this.worldDbId + ", ?, ?)" - ); - - insertPoly2dRegionStatement.setString(1, region.getId().toLowerCase()); - insertPoly2dRegionStatement.setInt(2, region.getMaximumPoint().getBlockY()); - insertPoly2dRegionStatement.setInt(3, region.getMinimumPoint().getBlockY()); - - insertPoly2dRegionStatement.execute(); - } finally { - closeResource(insertPoly2dRegionStatement); - } - - updatePoly2dPoints(region); - } - - private void updatePoly2dPoints(ProtectedPolygonalRegion region) throws SQLException { - PreparedStatement clearPoly2dPointsForRegionStatement = null; - PreparedStatement insertPoly2dPointStatement = null; - - try { - clearPoly2dPointsForRegionStatement = this.conn.prepareStatement( - "DELETE FROM `" + config.sqlTablePrefix + "region_poly2d_point` " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldDbId - ); - - clearPoly2dPointsForRegionStatement.setString(1, region.getId().toLowerCase()); - - clearPoly2dPointsForRegionStatement.execute(); - - insertPoly2dPointStatement = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_poly2d_point` (" + - "`id`, " + - "`region_id`, " + - "`world_id`, " + - "`z`, " + - "`x` " + - ") VALUES (null, ?, " + this.worldDbId + ", ?, ?)" - ); - - String lowerId = region.getId().toLowerCase(); - for (BlockVector2D point : region.getPoints()) { - insertPoly2dPointStatement.setString(1, lowerId); - insertPoly2dPointStatement.setInt(2, point.getBlockZ()); - insertPoly2dPointStatement.setInt(3, point.getBlockX()); - - insertPoly2dPointStatement.execute(); - } - } finally { - closeResource(clearPoly2dPointsForRegionStatement); - closeResource(insertPoly2dPointStatement); - } - } - - private void insertRegionGlobal(GlobalProtectedRegion region) throws SQLException { - insertRegion(region, "global"); - } - - private void updateRegion(ProtectedRegion region, String type) throws SQLException { - PreparedStatement updateRegionStatement = null; - try { - updateRegionStatement = this.conn.prepareStatement( - "UPDATE `" + config.sqlTablePrefix + "region` SET " + - "`priority` = ? WHERE `id` = ? AND `world_id` = " + this.worldDbId - ); - - updateRegionStatement.setInt(1, region.getPriority()); - updateRegionStatement.setString(2, region.getId().toLowerCase()); - - updateRegionStatement.execute(); - } finally { - closeResource(updateRegionStatement); - } - - updateFlags(region); - - updatePlayerAndGroups(region, false); - updatePlayerAndGroups(region, true); - } - - private void updateRegionCuboid(ProtectedCuboidRegion region) throws SQLException { - updateRegion(region, "cuboid"); - - PreparedStatement updateCuboidRegionStatement = null; - try { - updateCuboidRegionStatement = this.conn.prepareStatement( - "UPDATE `" + config.sqlTablePrefix + "region_cuboid` SET " + - "`min_z` = ?, " + - "`min_y` = ?, " + - "`min_x` = ?, " + - "`max_z` = ?, " + - "`max_y` = ?, " + - "`max_x` = ? " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldDbId - ); - - BlockVector min = region.getMinimumPoint(); - BlockVector max = region.getMaximumPoint(); - - updateCuboidRegionStatement.setInt(1, min.getBlockZ()); - updateCuboidRegionStatement.setInt(2, min.getBlockY()); - updateCuboidRegionStatement.setInt(3, min.getBlockX()); - updateCuboidRegionStatement.setInt(4, max.getBlockZ()); - updateCuboidRegionStatement.setInt(5, max.getBlockY()); - updateCuboidRegionStatement.setInt(6, max.getBlockX()); - updateCuboidRegionStatement.setString(7, region.getId().toLowerCase()); - - updateCuboidRegionStatement.execute(); - } finally { - closeResource(updateCuboidRegionStatement); - } - } - - private void updateRegionPoly2D(ProtectedPolygonalRegion region) throws SQLException { - updateRegion(region, "poly2d"); - - PreparedStatement updatePoly2dRegionStatement = null; - try { - updatePoly2dRegionStatement = this.conn.prepareStatement( - "UPDATE `" + config.sqlTablePrefix + "region_poly2d` SET " + - "`max_y` = ?, " + - "`min_y` = ? " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldDbId - ); - - updatePoly2dRegionStatement.setInt(1, region.getMaximumPoint().getBlockY()); - updatePoly2dRegionStatement.setInt(2, region.getMinimumPoint().getBlockY()); - updatePoly2dRegionStatement.setString(3, region.getId().toLowerCase()); - - updatePoly2dRegionStatement.execute(); - } finally { - closeResource(updatePoly2dRegionStatement); - } - updatePoly2dPoints(region); - } - - private void updateRegionGlobal(GlobalProtectedRegion region) throws SQLException { - updateRegion(region, "global"); - } - - private void closeResource(ResultSet rs) { - if (rs != null) { - try { - rs.close(); - } catch (SQLException e) {} - } - } - - private void closeResource(Statement st) { - if (st != null) { - try { - st.close(); - } catch (SQLException e) {} - } - } - - @Override - public Map getRegions() { - return regions; - } - - @Override - public void setRegions(Map regions) { - this.regions = regions; - } - - protected Object sqlUnmarshal(String rawValue) { - try { - return yaml.load(rawValue); - } catch (YAMLException e) { - return String.valueOf(rawValue); - } - } - - protected String sqlMarshal(Object rawObject) { - return yaml.dump(rawObject); - } } diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java index 32e71ae3..4b362c7d 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java @@ -48,6 +48,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -220,16 +221,25 @@ private void setFlag(ProtectedRegion region, Flag flag, Object rawValue) region.setFlag(flag, val); } + @SuppressWarnings("deprecation") private DefaultDomain parseDomain(YAMLNode node) { if (node == null) { return new DefaultDomain(); } DefaultDomain domain = new DefaultDomain(); - + for (String name : node.getStringList("players", null)) { domain.addPlayer(name); } + + for (String stringId : node.getStringList("uuids", null)) { + try { + domain.addPlayer(UUID.fromString(stringId)); + } catch (IllegalArgumentException e) { + logger.log(Level.WARNING, "Failed to parse UUID '" + stringId +"'", e); + } + } for (String name : node.getStringList("groups", null)) { domain.addGroup(name); @@ -310,33 +320,34 @@ private Map getFlagData(ProtectedRegion region) { } @SuppressWarnings("unchecked") - private void addMarshalledFlag(Map flagData, - Flag flag, Object val) { + private void addMarshalledFlag(Map flagData, Flag flag, Object val) { if (val == null) { return; } + flagData.put(flag.getName(), flag.marshal((V) val)); } + @SuppressWarnings("deprecation") private Map getDomainData(DefaultDomain domain) { Map domainData = new HashMap(); setDomainData(domainData, "players", domain.getPlayers()); + setDomainData(domainData, "uuids", domain.getUniqueIds()); setDomainData(domainData, "groups", domain.getGroups()); return domainData; } - private void setDomainData(Map domainData, - String key, Set domain) { - if (domain.size() == 0) { + private void setDomainData(Map domainData, String key, Set domain) { + if (domain.isEmpty()) { return; } List list = new ArrayList(); - for (String str : domain) { - list.add(str); + for (Object str : domain) { + list.add(String.valueOf(str)); } domainData.put(key, list); From a2fc9c94fd17960ff74693d96f7d76fc99cb59be Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 1 Aug 2014 20:02:50 -0700 Subject: [PATCH 003/133] Fix NPE when a manager fails to load. --- .../protection/GlobalRegionManager.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index 213c10c3..dd23de6a 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -19,19 +19,6 @@ package com.sk89q.worldguard.protection; -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - -import java.io.File; -import java.io.FileNotFoundException; -import java.util.HashMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; - -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; - import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.bukkit.BukkitUtil; import com.sk89q.worldguard.bukkit.ConfigurationManager; @@ -44,6 +31,18 @@ import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.PRTreeRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; + +import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; /** * This class keeps track of region information for every world. It loads @@ -129,7 +128,9 @@ public void unloadAll() { public RegionManager load(World world) { RegionManager manager = create(world); - managers.put(world.getName(), manager); + if (manager != null) { + managers.put(world.getName(), manager); + } return manager; } From 7986238936340a648821e1d4d28059dbc0dcef12 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 1 Aug 2014 21:29:40 -0700 Subject: [PATCH 004/133] Add migrations for region MySQL support. --- ....sql => region_manual_update_20110325.sql} | 8 + pom.xml | 14 + src/main/build/import-control.xml | 2 + .../database/mysql/AbstractJob.java | 8 + .../database/mysql/MySQLDatabaseImpl.java | 111 ++++++- .../com/sk89q/worldguard/util/io/Closer.java | 308 ++++++++++++++++++ .../migrations/region/mysql/V1__Initial.sql | 60 ++-- 7 files changed, 469 insertions(+), 42 deletions(-) rename contrib/{region_storage_update_20110325.sql => region_manual_update_20110325.sql} (95%) create mode 100644 src/main/java/com/sk89q/worldguard/util/io/Closer.java rename contrib/region_storage.sql => src/main/resources/migrations/region/mysql/V1__Initial.sql (79%) diff --git a/contrib/region_storage_update_20110325.sql b/contrib/region_manual_update_20110325.sql similarity index 95% rename from contrib/region_storage_update_20110325.sql rename to contrib/region_manual_update_20110325.sql index bca8beca..765fe82b 100644 --- a/contrib/region_storage_update_20110325.sql +++ b/contrib/region_manual_update_20110325.sql @@ -1,3 +1,11 @@ +-- +-- This file only needs to be run if you are using a very old version +-- of the region database (before 2011/03/25). +-- +-- Otherwise, WG knows how to update your tables automatically, as well +-- as set them up initially. +-- + SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL'; diff --git a/pom.xml b/pom.xml index 9e30a902..45877df9 100644 --- a/pom.xml +++ b/pom.xml @@ -176,6 +176,14 @@ jar + + org.flywaydb + flyway-core + 3.0 + compile + jar + + junit junit @@ -230,6 +238,11 @@ blacklist.txt + + migrations/ + false + ${basedir}/src/main/resources/migrations/ + @@ -361,6 +374,7 @@ net.sf.opencsv:opencsv org.khelekore:prtree com.jolbox:bonecp + org.flywaydb:flyway-core diff --git a/src/main/build/import-control.xml b/src/main/build/import-control.xml index ba3066d0..907fc35d 100644 --- a/src/main/build/import-control.xml +++ b/src/main/build/import-control.xml @@ -35,4 +35,6 @@ + + \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java index 44e6e3ec..6987970d 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java @@ -47,6 +47,14 @@ protected AbstractJob(MySQLDatabaseImpl database, Connection conn) { this.logger = database.getLogger(); } + static void closeQuietly(Connection connection) { + if (connection != null) { + try { + connection.close(); + } catch (SQLException ignored) {} + } + } + static void closeQuietly(ResultSet rs) { if (rs != null) { try { diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java index 5b4e09ad..58f3e0ec 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java @@ -23,9 +23,12 @@ import com.jolbox.bonecp.BoneCPConfig; import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.protection.databases.AbstractAsynchronousDatabase; -import com.sk89q.worldguard.protection.databases.InvalidTableFormatException; import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.io.Closer; +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.FlywayException; +import org.flywaydb.core.api.MigrationVersion; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.DumperOptions.FlowStyle; import org.yaml.snakeyaml.Yaml; @@ -39,6 +42,7 @@ import java.sql.Statement; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; @@ -76,6 +80,14 @@ public MySQLDatabaseImpl(ConfigurationManager config, String worldName, Logger l throw new ProtectionDatabaseException("Failed to connect to the database", e); } + try { + migrate(); + } catch (FlywayException e) { + throw new ProtectionDatabaseException("Failed to migrate tables", e); + } catch (SQLException e) { + throw new ProtectionDatabaseException("Failed to migrate tables", e); + } + try { worldId = chooseWorldId(worldName); } catch (SQLException e) { @@ -83,6 +95,92 @@ public MySQLDatabaseImpl(ConfigurationManager config, String worldName, Logger l } } + private boolean tryQuery(Connection conn, String sql) throws SQLException { + Closer closer = Closer.create(); + try { + Statement statement = closer.register(conn.createStatement()); + statement.executeQuery(sql); + return true; + } catch (SQLException ex) { + return false; + } finally { + closer.closeQuietly(); + } + } + + /** + * Migrate the tables to the latest version. + * + * @throws SQLException thrown if a connection can't be opened + * @throws ProtectionDatabaseException thrown on other error + */ + private void migrate() throws SQLException, ProtectionDatabaseException { + Closer closer = Closer.create(); + Connection conn = closer.register(getConnection()); + + // Check some tables + boolean tablesExist; + boolean isRecent; + boolean hasMigrations; + + try { + tablesExist = tryQuery(conn, "SELECT * FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 1"); + isRecent = tryQuery(conn, "SELECT `world_id` FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 1"); + hasMigrations = tryQuery(conn, "SELECT * FROM `" + config.sqlTablePrefix + "migrations` LIMIT 1"); + } finally { + closer.closeQuietly(); + } + + // We don't bother with migrating really old tables + if (tablesExist && !isRecent) { + throw new ProtectionDatabaseException( + "Sorry, your tables are too old for the region SQL auto-migration system. " + + "Please run region_manual_update_20110325.sql on your database, which comes " + + "with WorldGuard or can be found in http://github.com/sk89q/worldguard"); + } + + // Our placeholders + Map placeHolders = new HashMap(); + placeHolders.put("tablePrefix", config.sqlTablePrefix); + + BoneCPConfig boneConfig = connectionPool.getConfig(); + + Flyway flyway = new Flyway(); + + // The MySQL support predates the usage of Flyway, so let's do some + // checks and issue messages appropriately + if (!hasMigrations) { + flyway.setInitOnMigrate(true); + + if (tablesExist) { + logger.log(Level.INFO, "The MySQL region tables exist but the migrations table seems to not exist yet. Creating the migrations table..."); + } else { + // By default, if Flyway sees any tables at all in the schema, it + // will assume that we are up to date, so we have to manually + // check ourselves and then ask Flyway to start from the beginning + // if our test table doesn't exist + flyway.setInitVersion(MigrationVersion.fromVersion("0")); + + logger.log(Level.INFO, "MySQL region tables do not exist: creating..."); + } + } + + flyway.setClassLoader(getClass().getClassLoader()); + flyway.setLocations("migrations/region/mysql"); + flyway.setDataSource(boneConfig.getJdbcUrl(), boneConfig.getUser(), boneConfig.getPassword()); + flyway.setTable(config.sqlTablePrefix + "migrations"); + flyway.setPlaceholders(placeHolders); + flyway.migrate(); + } + + /** + * Get the ID for this world from the database or pick a new one if + * an entry does not exist yet. + * + * @param worldName the world name + * @return a world ID + * @throws SQLException on a database access error + */ private int chooseWorldId(String worldName) throws SQLException { Connection conn = getConnection(); PreparedStatement worldStmt = null; @@ -90,17 +188,6 @@ private int chooseWorldId(String worldName) throws SQLException { PreparedStatement insertWorldStatement = null; try { - PreparedStatement verTest = null; - try { - // Test if the database is up to date, if not throw a critical error - verTest = conn.prepareStatement("SELECT `world_id` FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 0, 1;"); - verTest.execute(); - } catch (SQLException ex) { - throw new InvalidTableFormatException("region_storage_update_20110325.sql"); - } finally { - AbstractJob.closeQuietly(verTest); - } - worldStmt = conn.prepareStatement( "SELECT `id` FROM `" + config.sqlTablePrefix + "world` WHERE `name` = ? LIMIT 0, 1" ); diff --git a/src/main/java/com/sk89q/worldguard/util/io/Closer.java b/src/main/java/com/sk89q/worldguard/util/io/Closer.java new file mode 100644 index 00000000..43985131 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/io/Closer.java @@ -0,0 +1,308 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.io; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +public final class Closer implements Closeable { + + private static final Logger logger = Logger.getLogger(Closer.class.getCanonicalName()); + + /** + * The suppressor implementation to use for the current Java version. + */ + private static final Suppressor SUPPRESSOR = SuppressingSuppressor.isAvailable() + ? SuppressingSuppressor.INSTANCE + : LoggingSuppressor.INSTANCE; + + /** + * Creates a new {@link Closer}. + */ + public static Closer create() { + return new Closer(SUPPRESSOR); + } + + @VisibleForTesting + final Suppressor suppressor; + + // only need space for 2 elements in most cases, so try to use the smallest array possible + private final Deque stack = new ArrayDeque(4); + private Throwable thrown; + + @VisibleForTesting + Closer(Suppressor suppressor) { + this.suppressor = checkNotNull(suppressor); // checkNotNull to satisfy null tests + } + + /** + * Registers the given {@code closeable} to be closed when this {@code Closer} is + * {@linkplain #close closed}. + * + * @return the given {@code closeable} + */ + // close. this word no longer has any meaning to me. + public C register(C closeable) { + stack.push(closeable); + return closeable; + } + + /** + * Registers the given {@code connection} to be closed when this + * {@code Closer} is {@linkplain #close closed}. + * + * @return the given {@code connection} + */ + public C register(final C connection) { + register(new Closeable() { + @Override + public void close() throws IOException { + try { + connection.close(); + } catch (SQLException e) { + throw new IOException("Failed to close", e); + } + } + }); + return connection; + } + + /** + * Registers the given {@code statement} to be closed when this + * {@code Closer} is {@linkplain #close closed}. + * + * @return the given {@code statement} + */ + public C register(final C statement) { + register(new Closeable() { + @Override + public void close() throws IOException { + try { + statement.close(); + } catch (SQLException e) { + throw new IOException("Failed to close", e); + } + } + }); + return statement; + } + + /** + * Registers the given {@code resultSet} to be closed when this + * {@code Closer} is {@linkplain #close closed}. + * + * @return the given {@code resultSet} + */ + public C register(final C resultSet) { + register(new Closeable() { + @Override + public void close() throws IOException { + try { + resultSet.close(); + } catch (SQLException e) { + throw new IOException("Failed to close", e); + } + } + }); + return resultSet; + } + + /** + * Stores the given throwable and rethrows it. It will be rethrown as is if it is an + * {@code IOException}, {@code RuntimeException} or {@code Error}. Otherwise, it will be rethrown + * wrapped in a {@code RuntimeException}. Note: Be sure to declare all of the checked + * exception types your try block can throw when calling an overload of this method so as to avoid + * losing the original exception type. + * + *

This method always throws, and as such should be called as + * {@code throw closer.rethrow(e);} to ensure the compiler knows that it will throw. + * + * @return this method does not return; it always throws + * @throws IOException when the given throwable is an IOException + */ + public RuntimeException rethrow(Throwable e) throws IOException { + thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + throw Throwables.propagate(e); + } + + /** + * Stores the given throwable and rethrows it. It will be rethrown as is if it is an + * {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of the + * given type. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. Note: + * Be sure to declare all of the checked exception types your try block can throw when calling an + * overload of this method so as to avoid losing the original exception type. + * + *

This method always throws, and as such should be called as + * {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw. + * + * @return this method does not return; it always throws + * @throws IOException when the given throwable is an IOException + * @throws X when the given throwable is of the declared type X + */ + public RuntimeException rethrow(Throwable e, + Class declaredType) throws IOException, X { + thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + Throwables.propagateIfPossible(e, declaredType); + throw Throwables.propagate(e); + } + + /** + * Stores the given throwable and rethrows it. It will be rethrown as is if it is an + * {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of either + * of the given types. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. + * Note: Be sure to declare all of the checked exception types your try block can throw + * when calling an overload of this method so as to avoid losing the original exception type. + * + *

This method always throws, and as such should be called as + * {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw. + * + * @return this method does not return; it always throws + * @throws IOException when the given throwable is an IOException + * @throws X1 when the given throwable is of the declared type X1 + * @throws X2 when the given throwable is of the declared type X2 + */ + public RuntimeException rethrow( + Throwable e, Class declaredType1, Class declaredType2) throws IOException, X1, X2 { + thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + Throwables.propagateIfPossible(e, declaredType1, declaredType2); + throw Throwables.propagate(e); + } + + /** + * Closes all {@code Closeable} instances that have been added to this {@code Closer}. If an + * exception was thrown in the try block and passed to one of the {@code exceptionThrown} methods, + * any exceptions thrown when attempting to close a closeable will be suppressed. Otherwise, the + * first exception to be thrown from an attempt to close a closeable will be thrown and any + * additional exceptions that are thrown after that will be suppressed. + */ + @Override + public void close() throws IOException { + Throwable throwable = thrown; + + // close closeables in LIFO order + while (!stack.isEmpty()) { + Closeable closeable = stack.pop(); + try { + closeable.close(); + } catch (Throwable e) { + if (throwable == null) { + throwable = e; + } else { + suppressor.suppress(closeable, throwable, e); + } + } + } + + if (thrown == null && throwable != null) { + Throwables.propagateIfPossible(throwable, IOException.class); + throw new AssertionError(throwable); // not possible + } + } + + /** + * Close quietly. + */ + public void closeQuietly() { + try { + close(); + } catch (IOException ignored) { + } + } + + /** + * Suppression strategy interface. + */ + @VisibleForTesting interface Suppressor { + /** + * Suppresses the given exception ({@code suppressed}) which was thrown when attempting to close + * the given closeable. {@code thrown} is the exception that is actually being thrown from the + * method. Implementations of this method should not throw under any circumstances. + */ + void suppress(Closeable closeable, Throwable thrown, Throwable suppressed); + } + + /** + * Suppresses exceptions by logging them. + */ + @VisibleForTesting static final class LoggingSuppressor implements Suppressor { + + static final LoggingSuppressor INSTANCE = new LoggingSuppressor(); + + @Override + public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { + // log to the same place as Closeables + logger.log(Level.WARNING, "Suppressing exception thrown when closing " + closeable, suppressed); + } + } + + /** + * Suppresses exceptions by adding them to the exception that will be thrown using JDK7's + * addSuppressed(Throwable) mechanism. + */ + @VisibleForTesting static final class SuppressingSuppressor implements Suppressor { + + static final SuppressingSuppressor INSTANCE = new SuppressingSuppressor(); + + static boolean isAvailable() { + return addSuppressed != null; + } + + static final Method addSuppressed = getAddSuppressed(); + + private static Method getAddSuppressed() { + try { + return Throwable.class.getMethod("addSuppressed", Throwable.class); + } catch (Throwable e) { + return null; + } + } + + @Override + public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { + // ensure no exceptions from addSuppressed + if (thrown == suppressed) { + return; + } + try { + addSuppressed.invoke(thrown, suppressed); + } catch (Throwable e) { + // if, somehow, IllegalAccessException or another exception is thrown, fall back to logging + LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed); + } + } + } +} diff --git a/contrib/region_storage.sql b/src/main/resources/migrations/region/mysql/V1__Initial.sql similarity index 79% rename from contrib/region_storage.sql rename to src/main/resources/migrations/region/mysql/V1__Initial.sql index 34d9e80d..985b173f 100644 --- a/contrib/region_storage.sql +++ b/src/main/resources/migrations/region/mysql/V1__Initial.sql @@ -5,7 +5,7 @@ SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL'; -- ----------------------------------------------------- -- Table `group` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `group` ( +CREATE TABLE IF NOT EXISTS `${tablePrefix}group` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT , `name` VARCHAR(64) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , PRIMARY KEY (`id`) , @@ -18,7 +18,7 @@ COLLATE = utf8_bin; -- ----------------------------------------------------- -- Table `world` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `world` ( +CREATE TABLE IF NOT EXISTS `${tablePrefix}world` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT , `name` VARCHAR(128) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , PRIMARY KEY (`id`) , @@ -31,7 +31,7 @@ COLLATE = utf8_bin; -- ----------------------------------------------------- -- Table `region` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `region` ( +CREATE TABLE IF NOT EXISTS `${tablePrefix}region` ( `id` VARCHAR(128) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , `world_id` INT(10) UNSIGNED NOT NULL , `type` ENUM('cuboid','poly2d','global') CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , @@ -40,14 +40,14 @@ CREATE TABLE IF NOT EXISTS `region` ( PRIMARY KEY (`id`, `world_id`) , INDEX `fk_region_world` (`world_id` ASC) , INDEX `parent` (`parent` ASC) , - CONSTRAINT `fk_region_world1` + CONSTRAINT `fk_${tablePrefix}region_world1` FOREIGN KEY (`world_id` ) - REFERENCES `world` (`id` ) + REFERENCES `${tablePrefix}world` (`id` ) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `parent` + CONSTRAINT `${tablePrefix}parent` FOREIGN KEY (`parent` ) - REFERENCES `region` (`id` ) + REFERENCES `${tablePrefix}region` (`id` ) ON DELETE SET NULL ON UPDATE CASCADE) ENGINE = InnoDB @@ -58,7 +58,7 @@ COLLATE = utf8_bin; -- ----------------------------------------------------- -- Table `region_cuboid` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `region_cuboid` ( +CREATE TABLE IF NOT EXISTS `${tablePrefix}region_cuboid` ( `region_id` VARCHAR(128) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , `world_id` INT(10) UNSIGNED NOT NULL , `min_x` BIGINT(20) NOT NULL , @@ -69,9 +69,9 @@ CREATE TABLE IF NOT EXISTS `region_cuboid` ( `max_z` BIGINT(20) NOT NULL , PRIMARY KEY (`region_id`, `world_id`) , INDEX `fk_region_cuboid_region` (`region_id` ASC) , - CONSTRAINT `fk_region_cuboid_region` + CONSTRAINT `fk_${tablePrefix}region_cuboid_region` FOREIGN KEY (`region_id` , `world_id` ) - REFERENCES `region` (`id` , `world_id` ) + REFERENCES `${tablePrefix}region` (`id` , `world_id` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB @@ -82,7 +82,7 @@ COLLATE = utf8_bin; -- ----------------------------------------------------- -- Table `region_flag` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `region_flag` ( +CREATE TABLE IF NOT EXISTS `${tablePrefix}region_flag` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT , `region_id` VARCHAR(128) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , `world_id` INT(10) UNSIGNED NOT NULL , @@ -90,9 +90,9 @@ CREATE TABLE IF NOT EXISTS `region_flag` ( `value` VARCHAR(256) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , PRIMARY KEY (`id`) , INDEX `fk_flags_region` (`region_id` ASC, `world_id` ASC) , - CONSTRAINT `fk_flags_region` + CONSTRAINT `fk_${tablePrefix}flags_region` FOREIGN KEY (`region_id` , `world_id` ) - REFERENCES `region` (`id` , `world_id` ) + REFERENCES `${tablePrefix}region` (`id` , `world_id` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB @@ -103,7 +103,7 @@ COLLATE = utf8_bin; -- ----------------------------------------------------- -- Table `region_groups` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `region_groups` ( +CREATE TABLE IF NOT EXISTS `${tablePrefix}region_groups` ( `region_id` VARCHAR(128) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , `world_id` INT(10) UNSIGNED NOT NULL , `group_id` INT(10) UNSIGNED NOT NULL , @@ -111,14 +111,14 @@ CREATE TABLE IF NOT EXISTS `region_groups` ( PRIMARY KEY (`region_id`, `world_id`, `group_id`) , INDEX `fk_region_groups_region` (`region_id` ASC) , INDEX `fk_region_groups_group` (`group_id` ASC) , - CONSTRAINT `fk_region_groups_group` + CONSTRAINT `fk_${tablePrefix}region_groups_group` FOREIGN KEY (`group_id` ) - REFERENCES `group` (`id` ) + REFERENCES `${tablePrefix}group` (`id` ) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_region_groups_region` + CONSTRAINT `fk_${tablePrefix}region_groups_region` FOREIGN KEY (`region_id` , `world_id` ) - REFERENCES `region` (`id` , `world_id` ) + REFERENCES `${tablePrefix}region` (`id` , `world_id` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB @@ -129,7 +129,7 @@ COLLATE = utf8_bin; -- ----------------------------------------------------- -- Table `user` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `user` ( +CREATE TABLE IF NOT EXISTS `${tablePrefix}user` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT , `name` VARCHAR(64) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , PRIMARY KEY (`id`) , @@ -142,7 +142,7 @@ COLLATE = utf8_bin; -- ----------------------------------------------------- -- Table `region_players` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `region_players` ( +CREATE TABLE IF NOT EXISTS `${tablePrefix}region_players` ( `region_id` VARCHAR(128) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , `world_id` INT(10) UNSIGNED NOT NULL , `user_id` INT(10) UNSIGNED NOT NULL , @@ -150,14 +150,14 @@ CREATE TABLE IF NOT EXISTS `region_players` ( PRIMARY KEY (`region_id`, `world_id`, `user_id`) , INDEX `fk_region_players_region` (`region_id` ASC) , INDEX `fk_region_users_user` (`user_id` ASC) , - CONSTRAINT `fk_region_users_region` + CONSTRAINT `fk_${tablePrefix}region_users_region` FOREIGN KEY (`region_id` , `world_id` ) - REFERENCES `region` (`id` , `world_id` ) + REFERENCES `${tablePrefix}region` (`id` , `world_id` ) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `fk_region_users_user` + CONSTRAINT `fk_${tablePrefix}region_users_user` FOREIGN KEY (`user_id` ) - REFERENCES `user` (`id` ) + REFERENCES `${tablePrefix}user` (`id` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB @@ -168,16 +168,16 @@ COLLATE = utf8_bin; -- ----------------------------------------------------- -- Table `region_poly2d` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `region_poly2d` ( +CREATE TABLE IF NOT EXISTS `${tablePrefix}region_poly2d` ( `region_id` VARCHAR(128) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , `world_id` INT(10) UNSIGNED NOT NULL , `min_y` INT(11) NOT NULL , `max_y` INT(11) NOT NULL , PRIMARY KEY (`region_id`, `world_id`) , INDEX `fk_region_poly2d_region` (`region_id` ASC) , - CONSTRAINT `fk_region_poly2d_region` + CONSTRAINT `fk_${tablePrefix}region_poly2d_region` FOREIGN KEY (`region_id` , `world_id` ) - REFERENCES `region` (`id` , `world_id` ) + REFERENCES `${tablePrefix}region` (`id` , `world_id` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB @@ -188,7 +188,7 @@ COLLATE = utf8_bin; -- ----------------------------------------------------- -- Table `region_poly2d_point` -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `region_poly2d_point` ( +CREATE TABLE IF NOT EXISTS `${tablePrefix}region_poly2d_point` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT , `region_id` VARCHAR(128) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL , `world_id` INT(10) UNSIGNED NOT NULL , @@ -196,9 +196,9 @@ CREATE TABLE IF NOT EXISTS `region_poly2d_point` ( `z` BIGINT(20) NOT NULL , PRIMARY KEY (`id`) , INDEX `fk_region_poly2d_point_region_poly2d` (`region_id` ASC, `world_id` ASC) , - CONSTRAINT `fk_region_poly2d_point_region_poly2d` + CONSTRAINT `fk_${tablePrefix}region_poly2d_point_region_poly2d` FOREIGN KEY (`region_id` , `world_id` ) - REFERENCES `region_poly2d` (`region_id` , `world_id` ) + REFERENCES `${tablePrefix}region_poly2d` (`region_id` , `world_id` ) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE = InnoDB From 1a602f2f8aa0f4425973f25a4ff9c65d8a601772 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 1 Aug 2014 22:29:31 -0700 Subject: [PATCH 005/133] Implement UUID saving/loading support for regions. Also fix WORLDGUARD-3117. --- .../database/mysql/MySQLDatabaseImpl.java | 9 +- .../database/mysql/RegionCommitter.java | 112 ++++------- .../database/mysql/RegionLoader.java | 34 ++-- .../database/mysql/UserToIdCache.java | 181 ++++++++++++++++++ .../protection/databases/YAMLDatabase.java | 4 +- .../region/mysql/V2__Bug_fix_and_UUID.sql | 17 ++ 6 files changed, 267 insertions(+), 90 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java create mode 100644 src/main/resources/migrations/region/mysql/V2__Bug_fix_and_UUID.sql diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java index 58f3e0ec..5ca26e30 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java @@ -121,11 +121,13 @@ private void migrate() throws SQLException, ProtectionDatabaseException { // Check some tables boolean tablesExist; boolean isRecent; + boolean isBeforeMigrations; boolean hasMigrations; try { tablesExist = tryQuery(conn, "SELECT * FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 1"); isRecent = tryQuery(conn, "SELECT `world_id` FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 1"); + isBeforeMigrations = !tryQuery(conn, "SELECT `uuid` FROM `" + config.sqlTablePrefix + "user` LIMIT 1"); hasMigrations = tryQuery(conn, "SELECT * FROM `" + config.sqlTablePrefix + "migrations` LIMIT 1"); } finally { closer.closeQuietly(); @@ -153,6 +155,11 @@ private void migrate() throws SQLException, ProtectionDatabaseException { flyway.setInitOnMigrate(true); if (tablesExist) { + // Detect if this is before migrations + if (isBeforeMigrations) { + flyway.setInitVersion(MigrationVersion.fromVersion("1")); + } + logger.log(Level.INFO, "The MySQL region tables exist but the migrations table seems to not exist yet. Creating the migrations table..."); } else { // By default, if Flyway sees any tables at all in the schema, it @@ -268,7 +275,7 @@ protected void performLoad() throws ProtectionDatabaseException { Connection connection = null; try { connection = getConnection(); - setRegions(new RegionLoader(this, connection, regions).call()); + setRegions(new RegionLoader(this, connection).call()); } catch (SQLException e) { throw new ProtectionDatabaseException("Failed to load regions database", e); } finally { diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java index 4a13f7f2..ff3bbbd5 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java @@ -22,6 +22,8 @@ import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector2D; import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.internal.protection.database.mysql.UserToIdCache.NameToIdCache; +import com.sk89q.worldguard.internal.protection.database.mysql.UserToIdCache.UUIDToIdCache; import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; import com.sk89q.worldguard.protection.databases.RegionDBUtil; import com.sk89q.worldguard.protection.flags.Flag; @@ -40,13 +42,21 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.Callable; +import java.util.logging.Level; import static com.google.common.base.Preconditions.checkNotNull; class RegionCommitter extends AbstractJob implements Callable { + /* + ========= Everything below is a nightmare. ========= + */ + private final Map regions; + private final NameToIdCache usernameCache; + private final UUIDToIdCache uuidCache; private final int worldId; RegionCommitter(MySQLDatabaseImpl database, Connection conn, Map regions) { @@ -54,6 +64,8 @@ class RegionCommitter extends AbstractJob implements Callable { checkNotNull(regions); this.regions = regions; this.worldId = database.getWorldId(); + usernameCache = new NameToIdCache(database, conn); + uuidCache = new UUIDToIdCache(database, conn); } @Override @@ -175,76 +187,6 @@ public Object call() throws SQLException, ProtectionDatabaseException { return null; } - /* - * Returns the database id for the user - * If it doesn't exits it adds the user and returns the id. - */ - private Map getUserIds(String... usernames) { - Map users = new HashMap(); - - if (usernames.length < 1) return users; - - ResultSet findUsersResults = null; - PreparedStatement insertUserStatement = null; - PreparedStatement findUsersStatement = null; - try { - findUsersStatement = this.conn.prepareStatement( - String.format( - "SELECT " + - "`user`.`id`, " + - "`user`.`name` " + - "FROM `" + config.sqlTablePrefix + "user` AS `user` " + - "WHERE `name` IN (%s)", - RegionDBUtil.preparePlaceHolders(usernames.length) - ) - ); - - RegionDBUtil.setValues(findUsersStatement, usernames); - - findUsersResults = findUsersStatement.executeQuery(); - - while(findUsersResults.next()) { - users.put(findUsersResults.getString("name"), findUsersResults.getInt("id")); - } - - insertUserStatement = this.conn.prepareStatement( - "INSERT INTO " + - "`" + config.sqlTablePrefix + "user` ( " + - "`id`, " + - "`name`" + - ") VALUES (null, ?)", - Statement.RETURN_GENERATED_KEYS - ); - - for (String username : usernames) { - if (!users.containsKey(username)) { - insertUserStatement.setString(1, username); - insertUserStatement.execute(); - ResultSet generatedKeys = insertUserStatement.getGeneratedKeys(); - if (generatedKeys.first()) { - users.put(username, generatedKeys.getInt(1)); - } else { - logger.warning("Could not get the database id for user " + username); - } - } - } - } catch (SQLException ex) { - ex.printStackTrace(); - logger.warning("Could not get the database id for the users " + usernames.toString() + "\n\t" + ex.getMessage()); - Throwable t = ex.getCause(); - while (t != null) { - logger.warning(t.getMessage()); - t = t.getCause(); - } - } finally { - closeQuietly(findUsersResults); - closeQuietly(findUsersStatement); - closeQuietly(insertUserStatement); - } - - return users; - } - /* * Returns the database id for the groups * If it doesn't exits it adds the group and returns the id. @@ -384,14 +326,32 @@ private void updatePlayerAndGroups(ProtectedRegion region, Boolean owners) throw "VALUES (?, " + this.worldId + ", ?, ?)" ); - Set var = domain.getPlayers(); + // Map players to IDs + usernameCache.fetch(domain.getPlayers()); + uuidCache.fetch(domain.getUniqueIds()); - for (Integer player : getUserIds(var.toArray(new String[var.size()])).values()) { - insertUsersForRegion.setString(1, region.getId().toLowerCase()); - insertUsersForRegion.setInt(2, player); - insertUsersForRegion.setBoolean(3, owners); + for (String name : domain.getPlayers()) { + Integer id = usernameCache.find(name); + if (id != null) { + insertUsersForRegion.setString(1, region.getId().toLowerCase()); + insertUsersForRegion.setInt(2, id); + insertUsersForRegion.setBoolean(3, owners); + insertUsersForRegion.execute(); + } else { + logger.log(Level.WARNING, "Did not find an ID for the user identified as '" + name + "'"); + } + } - insertUsersForRegion.execute(); + for (UUID uuid : domain.getUniqueIds()) { + Integer id = uuidCache.find(uuid); + if (id != null) { + insertUsersForRegion.setString(1, region.getId().toLowerCase()); + insertUsersForRegion.setInt(2, id); + insertUsersForRegion.setBoolean(3, owners); + insertUsersForRegion.execute(); + } else { + logger.log(Level.WARNING, "Did not find an ID for the user identified by '" + uuid + "'"); + } } deleteGroupsForRegion = this.conn.prepareStatement( diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java index 59ad301d..648290e5 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java @@ -39,13 +39,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.concurrent.Callable; -import static com.google.common.base.Preconditions.checkNotNull; - class RegionLoader extends AbstractJob implements Callable> { - private final Map regions; + /* + ========= Everything below is a nightmare. ========= + */ + private final int worldId; private Map cuboidRegions; @@ -53,10 +55,8 @@ class RegionLoader extends AbstractJob implements Callable globalRegions; private Map parentSets; - RegionLoader(MySQLDatabaseImpl database, Connection conn, Map regions) { + RegionLoader(MySQLDatabaseImpl database, Connection conn) { super(database, conn); - checkNotNull(regions); - this.regions = regions; this.worldId = database.getWorldId(); } @@ -142,8 +142,7 @@ private void loadFlags(ProtectedRegion region) { private void setFlag(ProtectedRegion region, Flag flag, Object rawValue) { T val = flag.unmarshal(rawValue); if (val == null) { - logger.warning("Failed to parse flag '" + flag.getName() - + "' with value '" + rawValue.toString() + "'"); + logger.warning("Failed to parse flag '" + flag.getName() + "' with value '" + rawValue + "'"); return; } region.setFlag(flag, val); @@ -158,7 +157,7 @@ private void loadOwnersAndMembers(ProtectedRegion region) { try { usersStatement = this.conn.prepareStatement( "SELECT " + - "`user`.`name`, " + + "`user`.`name`, `user`.`uuid`, " + "`region_players`.`owner` " + "FROM `" + config.sqlTablePrefix + "region_players` AS `region_players` " + "LEFT JOIN `" + config.sqlTablePrefix + "user` AS `user` ON ( " + @@ -171,10 +170,23 @@ private void loadOwnersAndMembers(ProtectedRegion region) { usersStatement.setString(1, region.getId().toLowerCase()); userSet = usersStatement.executeQuery(); while (userSet.next()) { + DefaultDomain domain; if (userSet.getBoolean("owner")) { - owners.addPlayer(userSet.getString("name")); + domain = owners; } else { - members.addPlayer(userSet.getString("name")); + domain = members; + } + + String name = userSet.getString("name"); + String uuid = userSet.getString("uuid"); + if (name != null) { + domain.addPlayer(name); + } else if (uuid != null) { + try { + domain.addPlayer(UUID.fromString(uuid)); + } catch (IllegalArgumentException e) { + logger.warning("Invalid UUID in database: " + uuid); + } } } } catch (SQLException ex) { diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java new file mode 100644 index 00000000..cf0d95a5 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java @@ -0,0 +1,181 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.protection.database.mysql; + +import com.google.common.collect.Lists; +import com.sk89q.worldguard.protection.databases.RegionDBUtil; +import com.sk89q.worldguard.util.io.Closer; + +import javax.annotation.Nullable; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.google.common.base.Preconditions.checkNotNull; + +abstract class UserToIdCache extends AbstractJob { + + private static final int MAX_NUMBER_PER_QUERY = 100; + + private final Map cache = new HashMap(); + private final String fieldName; + + protected UserToIdCache(MySQLDatabaseImpl database, Connection conn, String fieldName) { + super(database, conn); + this.fieldName = fieldName; + } + + protected abstract String fromType(V o); + + protected abstract V toType(String o); + + protected abstract V toKey(V object); + + @Nullable + public Integer find(V object) { + return cache.get(object); + } + + public void fetch(Collection entries) throws SQLException { + checkNotNull(entries); + + // Get a list of missing entries + List fetchList = new ArrayList(); + for (V entry : entries) { + if (!cache.containsKey(toKey(entry))) { + fetchList.add(entry); + } + } + + if (fetchList.isEmpty()) { + return; // Nothing to do + } + + // Search for entries + for (List partition : Lists.partition(fetchList, MAX_NUMBER_PER_QUERY)) { + Closer closer = Closer.create(); + try { + PreparedStatement statement = closer.register(conn.prepareStatement( + String.format( + "SELECT `user`.`id`, `user`.`" + fieldName + "` " + + "FROM `" + config.sqlTablePrefix + "user` AS `user` " + + "WHERE `" + fieldName + "` IN (%s)", + RegionDBUtil.preparePlaceHolders(partition.size())))); + + String[] values = new String[partition.size()]; + int i = 0; + for (V entry : partition) { + values[i] = fromType(entry); + i++; + } + + RegionDBUtil.setValues(statement, values); + ResultSet results = closer.register(statement.executeQuery()); + while (results.next()) { + cache.put(toKey(toType(results.getString(fieldName))), results.getInt("id")); + } + } finally { + closer.closeQuietly(); + } + } + + List missing = new ArrayList(); + for (V entry : fetchList) { + if (!cache.containsKey(toKey(entry))) { + missing.add(entry); + } + } + + // Insert entries that are missing + if (!missing.isEmpty()) { + Closer closer = Closer.create(); + try { + PreparedStatement statement = closer.register(conn.prepareStatement( + "INSERT INTO `" + config.sqlTablePrefix + "user` (`id`, `" + fieldName + "`) VALUES (null, ?)", + Statement.RETURN_GENERATED_KEYS)); + + for (V entry : missing) { + statement.setString(1, fromType(entry)); + statement.execute(); + + ResultSet generatedKeys = statement.getGeneratedKeys(); + if (generatedKeys.first()) { + cache.put(toKey(entry), generatedKeys.getInt(1)); + } else { + logger.warning("Could not get the database ID for user " + entry); + } + } + } finally { + closer.closeQuietly(); + } + } + } + + static class NameToIdCache extends UserToIdCache { + protected NameToIdCache(MySQLDatabaseImpl database, Connection conn) { + super(database, conn, "name"); + } + + @Override + protected String fromType(String o) { + return o; + } + + @Override + protected String toType(String o) { + return o; + } + + @Override + protected String toKey(String object) { + return object.toLowerCase(); + } + } + + static class UUIDToIdCache extends UserToIdCache { + protected UUIDToIdCache(MySQLDatabaseImpl database, Connection conn) { + super(database, conn, "uuid"); + } + + @Override + protected String fromType(UUID o) { + return o.toString(); + } + + @Override + protected UUID toType(String o) { + return UUID.fromString(o); + } + + @Override + protected UUID toKey(UUID object) { + return object; + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java index 4b362c7d..db944ffd 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java @@ -233,7 +233,7 @@ private DefaultDomain parseDomain(YAMLNode node) { domain.addPlayer(name); } - for (String stringId : node.getStringList("uuids", null)) { + for (String stringId : node.getStringList("unique-ids", null)) { try { domain.addPlayer(UUID.fromString(stringId)); } catch (IllegalArgumentException e) { @@ -333,7 +333,7 @@ private Map getDomainData(DefaultDomain domain) { Map domainData = new HashMap(); setDomainData(domainData, "players", domain.getPlayers()); - setDomainData(domainData, "uuids", domain.getUniqueIds()); + setDomainData(domainData, "unique-ids", domain.getUniqueIds()); setDomainData(domainData, "groups", domain.getGroups()); return domainData; diff --git a/src/main/resources/migrations/region/mysql/V2__Bug_fix_and_UUID.sql b/src/main/resources/migrations/region/mysql/V2__Bug_fix_and_UUID.sql new file mode 100644 index 00000000..46789055 --- /dev/null +++ b/src/main/resources/migrations/region/mysql/V2__Bug_fix_and_UUID.sql @@ -0,0 +1,17 @@ +-- Fix WORLDGUARD-3117 +-- Otherwise, you can't be both an owner and a member of a region + +ALTER TABLE `${tablePrefix}region_players` + DROP PRIMARY KEY, + ADD PRIMARY KEY (`region_id`, `world_id`, `user_id`, `owner`); + +-- Fix WORLDGUARD-3030 +-- Adds UUID support + +ALTER TABLE `${tablePrefix}user` + ALTER `name` DROP DEFAULT; + +ALTER TABLE `${tablePrefix}user` + CHANGE COLUMN `name` `name` VARCHAR(64) NULL COLLATE 'utf8_bin' AFTER `id`, + ADD COLUMN `uuid` CHAR(36) NULL AFTER `name`, + ADD UNIQUE INDEX `uuid` (`uuid`); \ No newline at end of file From 189fd15498fb12b16224bbdec35adb2a7e05a401 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 1 Aug 2014 22:32:13 -0700 Subject: [PATCH 006/133] Close connections properly in MySQLDatabaseImpl.chooseWorldId(). --- .../database/mysql/MySQLDatabaseImpl.java | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java index 5ca26e30..a095db61 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java @@ -189,28 +189,25 @@ private void migrate() throws SQLException, ProtectionDatabaseException { * @throws SQLException on a database access error */ private int chooseWorldId(String worldName) throws SQLException { - Connection conn = getConnection(); - PreparedStatement worldStmt = null; - ResultSet worldResult = null; - PreparedStatement insertWorldStatement = null; - + Closer closer = Closer.create(); try { - worldStmt = conn.prepareStatement( + Connection conn = closer.register(getConnection()); + PreparedStatement worldStmt = closer.register(conn.prepareStatement( "SELECT `id` FROM `" + config.sqlTablePrefix + "world` WHERE `name` = ? LIMIT 0, 1" - ); + )); worldStmt.setString(1, worldName); - worldResult = worldStmt.executeQuery(); + ResultSet worldResult = closer.register(worldStmt.executeQuery()); if (worldResult.first()) { return worldResult.getInt("id"); } else { - insertWorldStatement = conn.prepareStatement( + PreparedStatement insertWorldStatement = closer.register(conn.prepareStatement( "INSERT INTO " + "`" + config.sqlTablePrefix + "world` " + "(`id`, `name`) VALUES (null, ?)", Statement.RETURN_GENERATED_KEYS - ); + )); insertWorldStatement.setString(1, worldName); insertWorldStatement.execute(); @@ -223,9 +220,7 @@ private int chooseWorldId(String worldName) throws SQLException { } } } finally { - AbstractJob.closeQuietly(worldResult); - AbstractJob.closeQuietly(worldStmt); - AbstractJob.closeQuietly(insertWorldStatement); + closer.closeQuietly(); } } From 81a727fe7ff6b4b42f7c372a94d41540d1e24199 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 09:32:30 -0700 Subject: [PATCH 007/133] Add support for UUIDs in region commands. --- pom.xml | 8 + .../worldguard/bukkit/WorldGuardPlugin.java | 88 +++++--- .../bukkit/commands/CommandUtils.java | 188 ++++++++++++++++++ .../bukkit/commands/MessageTimerTask.java | 46 +++++ .../bukkit/commands/RegionCommands.java | 17 +- .../bukkit/commands/RegionMemberCommands.java | 119 ++++++----- .../commands/RegionPrintoutBuilder.java | 88 ++++---- .../worldguard/domains/DefaultDomain.java | 99 ++++++++- .../worldguard/domains/PlayerDomain.java | 10 + .../database/mysql/AbstractJob.java | 8 - .../protection/databases/RegionDBUtil.java | 5 +- .../databases/util/DomainInputResolver.java | 146 ++++++++++++++ .../util/UnresolvedNamesException.java | 41 ++++ 13 files changed, 721 insertions(+), 142 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/MessageTimerTask.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/util/UnresolvedNamesException.java diff --git a/pom.xml b/pom.xml index 45877df9..5c09647f 100644 --- a/pom.xml +++ b/pom.xml @@ -184,6 +184,14 @@ jar + + com.sk89q + squirrelid + 0.1.0-SNAPSHOT + compile + jar + + junit junit diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index e1112f71..70f41bad 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -19,6 +19,8 @@ package com.sk89q.worldguard.bukkit; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import com.sk89q.bukkit.util.CommandsManagerRegistration; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissionsException; @@ -27,6 +29,14 @@ import com.sk89q.minecraft.util.commands.MissingNestedCommandException; import com.sk89q.minecraft.util.commands.SimpleInjector; import com.sk89q.minecraft.util.commands.WrappedCommandException; +import com.sk89q.squirrelid.cache.HashMapCache; +import com.sk89q.squirrelid.cache.ProfileCache; +import com.sk89q.squirrelid.cache.SQLiteCache; +import com.sk89q.squirrelid.resolver.BukkitPlayerService; +import com.sk89q.squirrelid.resolver.CacheForwardingService; +import com.sk89q.squirrelid.resolver.CombinedProfileService; +import com.sk89q.squirrelid.resolver.HttpRepositoryService; +import com.sk89q.squirrelid.resolver.ProfileService; import com.sk89q.wepif.PermissionsResolverManager; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldguard.LocalPlayer; @@ -40,6 +50,7 @@ import com.sk89q.worldguard.protection.GlobalRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; +import com.sk89q.worldguard.util.concurrency.EvenMoreExecutors; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.World; @@ -64,41 +75,23 @@ import java.util.List; import java.util.Set; import java.util.jar.JarFile; +import java.util.logging.Level; import java.util.zip.ZipEntry; /** * The main class for WorldGuard as a Bukkit plugin. - * - * @author sk89q */ public class WorldGuardPlugin extends JavaPlugin { - /** - * Current instance of this plugin. - */ private static WorldGuardPlugin inst; - - /** - * Manager for commands. This automatically handles nested commands, - * permissions checking, and a number of other fancy command things. - * We just set it up and register commands against it. - */ private final CommandsManager commands; - - /** - * Handles the region databases for all worlds. - */ private final GlobalRegionManager globalRegionManager; - - /** - * Handles all configuration. - */ private final ConfigurationManager configuration; - - /** - * Used for scheduling flags. - */ private FlagStateManager flagStateManager; + private final ListeningExecutorService executorService = MoreExecutors.listeningDecorator( + EvenMoreExecutors.newBoundedCachedThreadPool(0, 4, 20)); + private ProfileService profileService; + private ProfileCache profileCache; /** * Construct objects. Actual loading occurs when the plugin is enabled, so @@ -205,6 +198,21 @@ public void run() { } worldListener.registerEvents(); + File cacheDir = new File(getDataFolder(), "cache"); + cacheDir.mkdirs(); + try { + profileCache = new SQLiteCache(new File(cacheDir, "profiles.sqlite")); + } catch (IOException e) { + getLogger().log(Level.WARNING, "Failed to initialize SQLite profile cache"); + profileCache = new HashMapCache(); + } + + profileService = new CacheForwardingService( + new CombinedProfileService( + BukkitPlayerService.getInstance(), + HttpRepositoryService.forMinecraft()), + profileCache); + if (!configuration.hasCommandBookGodMode()) { // Check god mode for existing players, if any for (Player player : getServer().getOnlinePlayers()) { @@ -216,9 +224,6 @@ public void run() { } } - /** - * Called on plugin disable. - */ @Override public void onDisable() { globalRegionManager.unload(); @@ -226,9 +231,6 @@ public void onDisable() { this.getServer().getScheduler().cancelTasks(this); } - /** - * Handle a command. - */ @Override public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { @@ -293,6 +295,34 @@ public ConfigurationManager getGlobalStateManager() { return configuration; } + /** + * Get the global executor service for internal usage (please use your + * own executor service). + * + * @return the global executor service + */ + public ListeningExecutorService getExecutorService() { + return executorService; + } + + /** + * Get the profile lookup service. + * + * @return the profile lookup service + */ + public ProfileService getProfileService() { + return profileService; + } + + /** + * Get the profile cache. + * + * @return the profile cache + */ + public ProfileCache getProfileCache() { + return profileCache; + } + /** * Check whether a player is in a group. * This calls the corresponding method in PermissionsResolverManager diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java new file mode 100644 index 00000000..9fb2e002 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java @@ -0,0 +1,188 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands; + +import com.google.common.base.Function; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import com.sk89q.worldguard.protection.databases.util.UnresolvedNamesException; +import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.CommandSender; + +import javax.annotation.Nullable; +import java.util.Timer; +import java.util.concurrent.CancellationException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Command-related utility methods. + */ +final class CommandUtils { + + private static final Logger log = Logger.getLogger(CommandUtils.class.getCanonicalName()); + private static final Timer timer = new Timer(); + private static final int MESSAGE_DELAY = 1000; + + private CommandUtils() { + } + + /** + * Add a message that gets shown after a delay if the future has not + * completed. + * + * @param future the future + * @param sender the sender to send a message to + * @param message the message to send (will be grey) + */ + static void progressCallback(ListenableFuture future, CommandSender sender, String message) { + checkNotNull(future); + checkNotNull(sender); + checkNotNull(message); + + final MessageTimerTask task = new MessageTimerTask(sender, ChatColor.GRAY + message); + timer.schedule(task, MESSAGE_DELAY); + future.addListener(new Runnable() { + @Override + public void run() { + task.cancel(); + } + }, MoreExecutors.sameThreadExecutor()); + } + + /** + * Create a callback to print a message to the user. + * + * @param sender the sender + * @param successMessage a success message or {@code null} to print nothing + * @param errorMessage an error message + * @param ignored type + * @return a callback + */ + static FutureCallback messageCallback(final CommandSender sender, @Nullable final String successMessage, final String errorMessage) { + checkNotNull(sender); + checkNotNull(errorMessage); + + return new FutureCallback() { + @Override + public void onSuccess(@Nullable T o) { + if (successMessage != null) { + sender.sendMessage(ChatColor.YELLOW + successMessage); + } + } + + @Override + public void onFailure(@Nullable Throwable throwable) { + sender.sendMessage(ChatColor.RED + errorMessage + ": " + convertThrowable(throwable)); + } + }; + } + + /** + * Return a function that accepts a string to send a message to the + * given sender. + * + * @param sender the sender + * @return a function + */ + static Function messageFunction(final CommandSender sender) { + return new Function() { + @Override + public Object apply(@Nullable String s) { + sender.sendMessage(s); + return null; + } + }; + } + + /** + * Return a function to add a prefix and suffix to the input string. + * + * @param prefix the prefix + * @param suffix the suffix + * @return a function + */ + static Function messageAppender(final String prefix, final String suffix) { + return new Function() { + @Override + public String apply(@Nullable String s) { + return prefix + s + suffix; + } + }; + } + + /** + * Create a callback to save a region manager on success. + * + * @param sender the sender + * @param manager the region manager + * @param world the world + * @param silent true to not print a success message + * @param an ignored type + * @return a callback + */ + static FutureCallback saveRegionsCallback(final CommandSender sender, final RegionManager manager, final World world, final boolean silent) { + checkNotNull(sender); + checkNotNull(manager); + checkNotNull(world); + + return new FutureCallback() { + @Override + public void onSuccess(@Nullable T o) { + ListenableFuture future = manager.save(true); + String successMessage = silent ? null : "Successfully saved the region data for '" + world.getName() + "'."; + String failureMessage = "Failed to save the region data for '" + world.getName() + "'"; + Futures.addCallback(future, messageCallback(sender, successMessage, failureMessage)); + } + + @Override + public void onFailure(@Nullable Throwable throwable) { + } + }; + } + + /** + * Convert the throwable into a somewhat friendly message. + * + * @param throwable the throwable + * @return a message + */ + private static String convertThrowable(@Nullable Throwable throwable) { + if (throwable instanceof CancellationException) { + return "Task was cancelled"; + } else if (throwable instanceof InterruptedException) { + return "Task was interrupted"; + } else if (throwable instanceof UnresolvedNamesException) { + return throwable.getMessage(); + } else if (throwable instanceof Exception) { + log.log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable); + return "Unexpected error occurred: " + ((Exception) throwable).getMessage(); + } else { + return "Unknown error"; + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageTimerTask.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageTimerTask.java new file mode 100644 index 00000000..b4554c09 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageTimerTask.java @@ -0,0 +1,46 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands; + +import org.bukkit.command.CommandSender; + +import java.util.TimerTask; + +import static com.google.common.base.Preconditions.checkNotNull; + +class MessageTimerTask extends TimerTask { + + private final CommandSender sender; + private final String message; + + MessageTimerTask(CommandSender sender, String message) { + checkNotNull(sender); + checkNotNull(message); + + this.sender = sender; + this.message = message; + } + + @Override + public void run() { + sender.sendMessage(message); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java index 3c1df0ba..15b519d9 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java @@ -706,7 +706,7 @@ public void select(CommandContext args, CommandSender sender) throws CommandExce */ @Command(aliases = {"info", "i"}, usage = "[id]", - flags = "sw:", + flags = "usw:", desc = "Get information about a region", min = 0, max = 1) public void info(CommandContext args, CommandSender sender) throws CommandException { @@ -734,9 +734,11 @@ public void info(CommandContext args, CommandSender sender) throws CommandExcept } // Print region information - RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing); - printout.appendRegionInfo(); - printout.send(sender); + RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, args.hasFlag('u') ? null : plugin.getProfileCache()); + ListenableFuture future = Futures.transform( + plugin.getExecutorService().submit(printout), + CommandUtils.messageFunction(sender)); + Futures.addCallback(future, CommandUtils.messageCallback(sender, null, "Failed to retrieve region info")); // Let the player also select the region if (args.hasFlag('s')) { @@ -964,7 +966,7 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept commitChanges(sender, regionManager, world, true); // Save to disk // Print region information - RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing); + RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, null); printout.append(ChatColor.GRAY); printout.append("(Current flags: "); printout.appendFlagsList(false); @@ -1044,7 +1046,7 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE child.setParent(parent); } catch (CircularInheritanceException e) { // Tell the user what's wrong - RegionPrintoutBuilder printout = new RegionPrintoutBuilder(parent); + RegionPrintoutBuilder printout = new RegionPrintoutBuilder(parent, null); printout.append(ChatColor.RED); printout.append("Uh oh! Setting '" + parent.getId() + "' to be the parent " + "of '" + child.getId() + "' would cause circular inheritance.\n"); @@ -1060,7 +1062,7 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE commitChanges(sender, regionManager, world, true); // Save to disk // Tell the user the current inheritance - RegionPrintoutBuilder printout = new RegionPrintoutBuilder(child); + RegionPrintoutBuilder printout = new RegionPrintoutBuilder(child, null); printout.append(ChatColor.YELLOW); printout.append("Inheritance set for region '" + child.getId() + "'.\n"); if (parent != null) { @@ -1071,7 +1073,6 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE printout.append(")"); } printout.send(sender); - return; } /** diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java index 14da9449..c95f597b 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java @@ -19,26 +19,29 @@ package com.sk89q.worldguard.bukkit.commands; -import org.bukkit.ChatColor; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.RegionDBUtil; +import com.sk89q.worldguard.protection.databases.util.DomainInputResolver; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import static com.google.common.util.concurrent.Futures.addCallback; +import static com.sk89q.worldguard.bukkit.commands.CommandUtils.*; // @TODO: A lot of code duplication here! Need to fix. public class RegionMemberCommands { + private final WorldGuardPlugin plugin; public RegionMemberCommands(WorldGuardPlugin plugin) { @@ -47,7 +50,7 @@ public RegionMemberCommands(WorldGuardPlugin plugin) { @Command(aliases = {"addmember", "addmember", "addmem", "am"}, usage = " ", - flags = "w:", + flags = "nw:", desc = "Add a member to a region", min = 2) public void addMember(CommandContext args, CommandSender sender) throws CommandException { @@ -89,22 +92,24 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE } } - RegionDBUtil.addToDomain(region.getMembers(), args.getParsedPaddedSlice(1, 0), 0); + // Resolve members asynchronously + DomainInputResolver resolver = new DomainInputResolver( + plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); + resolver.setUseNames(args.hasFlag('n')); - sender.sendMessage(ChatColor.YELLOW - + "Region '" + id + "' updated."); + // Then add it to the members + ListenableFuture future = Futures.transform( + plugin.getExecutorService().submit(resolver), + resolver.createAddAllFunction(region.getMembers())); - try { - mgr.save(); - } catch (ProtectionDatabaseException e) { - throw new CommandException("Failed to write regions: " - + e.getMessage()); - } + progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); + addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to add members")); + addCallback(future, saveRegionsCallback(sender, mgr, world, true)); } @Command(aliases = {"addowner", "addowner", "ao"}, usage = " ", - flags = "w:", + flags = "nw:", desc = "Add an owner to a region", min = 2) public void addOwner(CommandContext args, CommandSender sender) throws CommandException { @@ -159,22 +164,24 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx } } - RegionDBUtil.addToDomain(region.getOwners(), args.getParsedPaddedSlice(1, 0), 0); + // Resolve owners asynchronously + DomainInputResolver resolver = new DomainInputResolver( + plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); + resolver.setUseNames(args.hasFlag('n')); - sender.sendMessage(ChatColor.YELLOW - + "Region '" + id + "' updated."); + // Then add it to the owners + ListenableFuture future = Futures.transform( + plugin.getExecutorService().submit(resolver), + resolver.createAddAllFunction(region.getOwners())); - try { - mgr.save(); - } catch (ProtectionDatabaseException e) { - throw new CommandException("Failed to write regions: " - + e.getMessage()); - } + progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); + addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to add owners")); + addCallback(future, saveRegionsCallback(sender, mgr, world, true)); } @Command(aliases = {"removemember", "remmember", "removemem", "remmem", "rm"}, usage = " ", - flags = "aw:", + flags = "naw:", desc = "Remove an owner to a region", min = 1) public void removeMember(CommandContext args, CommandSender sender) throws CommandException { @@ -216,29 +223,36 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma } } + ListenableFuture future; + if (args.hasFlag('a')) { region.getMembers().removeAll(); + + future = Futures.immediateFuture(null); } else { if (args.argsLength() < 2) { throw new CommandException("List some names to remove, or use -a to remove all."); } - RegionDBUtil.removeFromDomain(region.getMembers(), args.getParsedPaddedSlice(1, 0), 0); + + // Resolve members asynchronously + DomainInputResolver resolver = new DomainInputResolver( + plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); + resolver.setUseNames(args.hasFlag('n')); + + // Then remove it from the members + future = Futures.transform( + plugin.getExecutorService().submit(resolver), + resolver.createRemoveAllFunction(region.getMembers())); } - sender.sendMessage(ChatColor.YELLOW - + "Region '" + id + "' updated."); - - try { - mgr.save(); - } catch (ProtectionDatabaseException e) { - throw new CommandException("Failed to write regions: " - + e.getMessage()); - } + progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); + addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to remove members")); + addCallback(future, saveRegionsCallback(sender, mgr, world, true)); } @Command(aliases = {"removeowner", "remowner", "ro"}, usage = " ", - flags = "aw:", + flags = "naw:", desc = "Remove an owner to a region", min = 1) public void removeOwner(CommandContext args, @@ -281,23 +295,30 @@ public void removeOwner(CommandContext args, } } + ListenableFuture future; + if (args.hasFlag('a')) { region.getOwners().removeAll(); + + future = Futures.immediateFuture(null); } else { if (args.argsLength() < 2) { throw new CommandException("List some names to remove, or use -a to remove all."); } - RegionDBUtil.removeFromDomain(region.getOwners(), args.getParsedPaddedSlice(1, 0), 0); + + // Resolve owners asynchronously + DomainInputResolver resolver = new DomainInputResolver( + plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); + resolver.setUseNames(args.hasFlag('n')); + + // Then remove it from the owners + future = Futures.transform( + plugin.getExecutorService().submit(resolver), + resolver.createRemoveAllFunction(region.getOwners())); } - sender.sendMessage(ChatColor.YELLOW - + "Region '" + id + "' updated."); - - try { - mgr.save(); - } catch (ProtectionDatabaseException e) { - throw new CommandException("Failed to write regions: " - + e.getMessage()); - } + progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); + addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to remove owners")); + addCallback(future, saveRegionsCallback(sender, mgr, world, true)); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java index c193e486..5ada6da0 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java @@ -19,36 +19,42 @@ package com.sk89q.worldguard.bukkit.commands; -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; - -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; - +import com.sk89q.squirrelid.cache.ProfileCache; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldguard.domains.DefaultDomain; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.RegionGroupFlag; import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; +import java.util.concurrent.Callable; /** * Create a region printout, as used in /region info to show information about * a region. */ -public class RegionPrintoutBuilder { +class RegionPrintoutBuilder implements Callable { private final ProtectedRegion region; + @Nullable + private final ProfileCache cache; private final StringBuilder builder = new StringBuilder(); /** * Create a new instance with a region to report on. - * + * * @param region the region + * @param cache a profile cache, or {@code null} */ - public RegionPrintoutBuilder(ProtectedRegion region) { + RegionPrintoutBuilder(ProtectedRegion region, @Nullable ProfileCache cache) { this.region = region; + this.cache = cache; } /** @@ -122,11 +128,11 @@ public void appendFlagsList(boolean useColors) { if(group == null) { builder.append(flag.getName()).append(": ") - .append(String.valueOf(val)); + .append(val); } else { builder.append(flag.getName()).append(" -g ") - .append(String.valueOf(group)).append(": ") - .append(String.valueOf(val)); + .append(group).append(": ") + .append(val); } hasFlags = true; @@ -192,7 +198,7 @@ public void appendParentTree(boolean useColors) { if (useColors) { builder.append(ChatColor.GRAY); } - builder.append(" (parent, priority=" + cur.getPriority() + ")"); + builder.append(" (parent, priority=").append(cur.getPriority()).append(")"); } indent++; @@ -206,29 +212,23 @@ public void appendParentTree(boolean useColors) { public void appendDomain() { builder.append(ChatColor.BLUE); builder.append("Owners: "); - DefaultDomain owners = region.getOwners(); - if (owners.size() != 0) { - builder.append(ChatColor.YELLOW); - builder.append(owners.toUserFriendlyString()); - } else { - builder.append(ChatColor.RED); - builder.append("(no owners)"); - } - + addDomainString(region.getOwners()); newLine(); builder.append(ChatColor.BLUE); builder.append("Members: "); - DefaultDomain members = region.getMembers(); - if (members.size() != 0) { + addDomainString(region.getMembers()); + newLine(); + } + + private void addDomainString(DefaultDomain domain) { + if (domain.size() != 0) { builder.append(ChatColor.YELLOW); - builder.append(members.toUserFriendlyString()); + builder.append(domain.toUserFriendlyString(cache)); } else { builder.append(ChatColor.RED); - builder.append("(no members)"); + builder.append("(none)"); } - - newLine(); } /** @@ -240,16 +240,13 @@ public void appendBounds() { builder.append(ChatColor.BLUE); builder.append("Bounds:"); builder.append(ChatColor.YELLOW); - builder.append(" (" + min.getBlockX() + "," + min.getBlockY() + "," + min.getBlockZ() + ")"); - builder.append(" -> (" + max.getBlockX() + "," + max.getBlockY() + "," + max.getBlockZ() + ")"); + builder.append(" (").append(min.getBlockX()).append(",").append(min.getBlockY()).append(",").append(min.getBlockZ()).append(")"); + builder.append(" -> (").append(max.getBlockX()).append(",").append(max.getBlockY()).append(",").append(max.getBlockZ()).append(")"); newLine(); } - - /** - * Append all the default fields used for /rg info. - */ - public void appendRegionInfo() { + + private void appendRegionInformation() { builder.append(ChatColor.GRAY); builder.append("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"); builder.append(" Region Info "); @@ -260,17 +257,28 @@ public void appendRegionInfo() { appendParents(); appendDomain(); appendBounds(); + + if (cache != null) { + builder.append(ChatColor.GRAY).append("Any names suffixed by * are 'last seen names' and may not be up to date."); + newLine(); + } } - + + @Override + public String call() throws Exception { + appendRegionInformation(); + return builder.toString(); + } + /** * Send the report to a {@link CommandSender}. - * - * @param sender the recepient + * + * @param sender the recipient */ public void send(CommandSender sender) { sender.sendMessage(toString()); } - + public StringBuilder append(boolean b) { return builder.append(b); } diff --git a/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java b/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java index e8093784..678125eb 100644 --- a/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java +++ b/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java @@ -19,8 +19,12 @@ package com.sk89q.worldguard.domains; +import com.google.common.collect.ImmutableMap; +import com.sk89q.squirrelid.Profile; +import com.sk89q.squirrelid.cache.ProfileCache; import com.sk89q.worldguard.LocalPlayer; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -28,6 +32,8 @@ import java.util.Set; import java.util.UUID; +import static com.google.common.base.Preconditions.checkNotNull; + /** * A combination of a {@link PlayerDomain} and a {@link GroupDomain}. */ @@ -58,6 +64,15 @@ public void removePlayer(String name) { playerDomain.removePlayer(name); } + /** + * Remove the given player from the domain, identified by the player's UUID. + * + * @param uuid the UUID of the player + */ + public void removePlayer(UUID uuid) { + playerDomain.removePlayer(uuid); + } + /** * Add the given player to the domain, identified by the player's UUID. * @@ -86,6 +101,42 @@ public void addPlayer(LocalPlayer player) { playerDomain.addPlayer(player); } + /** + * Add all the entries from another domain. + * + * @param other the other domain + */ + public void addAll(DefaultDomain other) { + checkNotNull(other); + for (String player : other.getPlayers()) { + addPlayer(player); + } + for (UUID uuid : other.getUniqueIds()) { + addPlayer(uuid); + } + for (String group : other.getGroups()) { + addGroup(group); + } + } + + /** + * Remove all the entries from another domain. + * + * @param other the other domain + */ + public void removeAll(DefaultDomain other) { + checkNotNull(other); + for (String player : other.getPlayers()) { + removePlayer(player); + } + for (UUID uuid : other.getUniqueIds()) { + removePlayer(uuid); + } + for (String group : other.getGroups()) { + removeGroup(group); + } + } + /** * Get the set of player names. * @@ -165,11 +216,29 @@ public void removeAll() { @SuppressWarnings("deprecation") public String toPlayersString() { + return toPlayersString(null); + } + + @SuppressWarnings("deprecation") + public String toPlayersString(@Nullable ProfileCache cache) { StringBuilder str = new StringBuilder(); List output = new ArrayList(); output.addAll(playerDomain.getPlayers()); - for (UUID uuid : playerDomain.getUniqueIds()) { - output.add("uuid:" + uuid); + + if (cache != null) { + ImmutableMap results = cache.getAllPresent(playerDomain.getUniqueIds()); + for (UUID uuid : playerDomain.getUniqueIds()) { + Profile profile = results.get(uuid); + if (profile != null) { + output.add(profile.getName() + "*"); + } else { + output.add("uuid:" + uuid); + } + } + } else { + for (UUID uuid : playerDomain.getUniqueIds()) { + output.add("uuid:" + uuid); + } } Collections.sort(output, String.CASE_INSENSITIVE_ORDER); for (Iterator it = output.iterator(); it.hasNext();) { @@ -192,22 +261,40 @@ public String toGroupsString() { } return str.toString(); } - + public String toUserFriendlyString() { StringBuilder str = new StringBuilder(); if (playerDomain.size() > 0) { str.append(toPlayersString()); } - + if (groupDomain.size() > 0) { if (str.length() > 0) { str.append("; "); } - + str.append(toGroupsString()); } - + + return str.toString(); + } + + public String toUserFriendlyString(ProfileCache cache) { + StringBuilder str = new StringBuilder(); + + if (playerDomain.size() > 0) { + str.append(toPlayersString(cache)); + } + + if (groupDomain.size() > 0) { + if (str.length() > 0) { + str.append("; "); + } + + str.append(toGroupsString()); + } + return str.toString(); } diff --git a/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java b/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java index d7ae0d60..3da2e3c6 100644 --- a/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java +++ b/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java @@ -98,6 +98,16 @@ public void removePlayer(String name) { names.remove(name.toLowerCase()); } + /** + * Remove the given player from the domain, identified by the player's UUID. + * + * @param uuid the UUID of the player + */ + public void removePlayer(UUID uuid) { + checkNotNull(uuid); + uniqueIds.remove(uuid); + } + /** * Remove the given player from the domain, identified by either the * player's name, the player's unique ID, or both. diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java index 6987970d..44e6e3ec 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java +++ b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java @@ -47,14 +47,6 @@ protected AbstractJob(MySQLDatabaseImpl database, Connection conn) { this.logger = database.getLogger(); } - static void closeQuietly(Connection connection) { - if (connection != null) { - try { - connection.close(); - } catch (SQLException ignored) {} - } - } - static void closeQuietly(ResultSet rs) { if (rs != null) { try { diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java b/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java index 14241efd..9d587e9b 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java @@ -19,19 +19,20 @@ package com.sk89q.worldguard.protection.databases; +import com.sk89q.worldguard.domains.DefaultDomain; + import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.sk89q.worldguard.domains.DefaultDomain; - /** * Various utility functions for parsing region databases. * * @author sk89q */ public class RegionDBUtil { + private static Pattern groupPattern = Pattern.compile("(?i)^[G]:(.+)$"); private RegionDBUtil() { diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java b/src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java new file mode 100644 index 00000000..55bda8c6 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java @@ -0,0 +1,146 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.util; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.sk89q.squirrelid.Profile; +import com.sk89q.squirrelid.resolver.ProfileService; +import com.sk89q.squirrelid.util.UUIDs; +import com.sk89q.worldguard.domains.DefaultDomain; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Resolves input for a domain (i.e. "player1 player2 <uuid> g:group"). + * + *

Unless {@link #getUseNames()} is true, names will be resolved into + * UUIDs.

+ */ +public class DomainInputResolver implements Callable { + + private static final Pattern GROUP_PATTERN = Pattern.compile("(?i)^[G]:(.+)$"); + + private final ProfileService profileService; + private final String[] input; + private boolean useNames = false; + + /** + * Create a new instance. + * + * @param profileService the profile service + * @param input the input to parse + */ + public DomainInputResolver(ProfileService profileService, String[] input) { + checkNotNull(profileService); + checkNotNull(input); + this.profileService = profileService; + this.input = input; + } + + /** + * Get whether names should be used rather than UUIDs. + * + * @return true to use names + */ + public boolean getUseNames() { + return useNames; + } + + /** + * Set whether names should be used rather than UUIDs. + * + * @param useNames true to use names + */ + public void setUseNames(boolean useNames) { + this.useNames = useNames; + } + + @Override + public DefaultDomain call() throws UnresolvedNamesException { + DefaultDomain domain = new DefaultDomain(); + List namesToQuery = new ArrayList(); + + for (String s : input) { + Matcher m = GROUP_PATTERN.matcher(s); + if (m.matches()) { + domain.addGroup(m.group(1)); + } else { + try { + domain.addPlayer(UUID.fromString(UUIDs.addDashes(s))); + } catch (IllegalArgumentException e) { + if (useNames) { + domain.addPlayer(s); + } else { + namesToQuery.add(s.toLowerCase()); + } + } + } + } + + if (!namesToQuery.isEmpty()) { + try { + for (Profile profile : profileService.findAllByName(namesToQuery)) { + namesToQuery.remove(profile.getName().toLowerCase()); + domain.addPlayer(profile.getUniqueId()); + } + } catch (IOException e) { + throw new UnresolvedNamesException("The UUID lookup service failed so the names entered could not be turned into UUIDs"); + } catch (InterruptedException e) { + throw new UnresolvedNamesException("UUID lookup was interrupted"); + } + } + + if (!namesToQuery.isEmpty()) { + throw new UnresolvedNamesException("Unable to resolve the names " + Joiner.on(", ").join(namesToQuery)); + } + + return domain; + } + + public Function createAddAllFunction(final DefaultDomain target) { + return new Function() { + @Override + public DefaultDomain apply(@Nullable DefaultDomain domain) { + target.addAll(domain); + return domain; + } + }; + } + + public Function createRemoveAllFunction(final DefaultDomain target) { + return new Function() { + @Override + public DefaultDomain apply(@Nullable DefaultDomain domain) { + target.removeAll(domain); + return domain; + } + }; + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/util/UnresolvedNamesException.java b/src/main/java/com/sk89q/worldguard/protection/databases/util/UnresolvedNamesException.java new file mode 100644 index 00000000..25f586c7 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/databases/util/UnresolvedNamesException.java @@ -0,0 +1,41 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.util; + +/** + * Thrown when there are unresolved names. + */ +public class UnresolvedNamesException extends Exception { + + public UnresolvedNamesException() { + } + + public UnresolvedNamesException(String message) { + super(message); + } + + public UnresolvedNamesException(String message, Throwable cause) { + super(message, cause); + } + + public UnresolvedNamesException(Throwable cause) { + super(cause); + } +} From a1d59373ace504d1b5cc94a693e2233edc8738f5 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 09:49:52 -0700 Subject: [PATCH 008/133] Re-configure how libraries are shaded. --- pom.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pom.xml b/pom.xml index 5c09647f..6197fada 100644 --- a/pom.xml +++ b/pom.xml @@ -383,8 +383,23 @@ org.khelekore:prtree com.jolbox:bonecp org.flywaydb:flyway-core + com.sk89q:squirrelid + + + com.jolbox.bonecp + com.sk89q.worldguard.internal.bonecp + + + org.flywaydb + com.sk89q.worldguard.internal.flywaydb + + + com.sk89q.squirrelid + com.sk89q.worldguard.util.profile + + From 9b69c0766341c1b95a45e661fde22c38bf236319 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 09:53:35 -0700 Subject: [PATCH 009/133] Show name: for legacy names in region domains. --- .../java/com/sk89q/worldguard/domains/DefaultDomain.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java b/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java index 678125eb..05c77830 100644 --- a/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java +++ b/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java @@ -223,7 +223,10 @@ public String toPlayersString() { public String toPlayersString(@Nullable ProfileCache cache) { StringBuilder str = new StringBuilder(); List output = new ArrayList(); - output.addAll(playerDomain.getPlayers()); + + for (String name : playerDomain.getPlayers()) { + output.add("name:" + name); + } if (cache != null) { ImmutableMap results = cache.getAllPresent(playerDomain.getUniqueIds()); @@ -240,6 +243,7 @@ public String toPlayersString(@Nullable ProfileCache cache) { output.add("uuid:" + uuid); } } + Collections.sort(output, String.CASE_INSENSITIVE_ORDER); for (Iterator it = output.iterator(); it.hasNext();) { str.append(it.next()); From 387431c9c7d5f4ae99d6de2dd6d0e6c8fae820d0 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 10:03:41 -0700 Subject: [PATCH 010/133] Remove both UUID and names from domains for removal commands. --- .../bukkit/commands/RegionMemberCommands.java | 9 ++-- .../databases/util/DomainInputResolver.java | 48 ++++++++++++------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java index c95f597b..e981d2a8 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java @@ -28,6 +28,7 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.domains.DefaultDomain; import com.sk89q.worldguard.protection.databases.util.DomainInputResolver; +import com.sk89q.worldguard.protection.databases.util.DomainInputResolver.UserLocatorPolicy; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -95,7 +96,7 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE // Resolve members asynchronously DomainInputResolver resolver = new DomainInputResolver( plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); - resolver.setUseNames(args.hasFlag('n')); + resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); // Then add it to the members ListenableFuture future = Futures.transform( @@ -167,7 +168,7 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx // Resolve owners asynchronously DomainInputResolver resolver = new DomainInputResolver( plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); - resolver.setUseNames(args.hasFlag('n')); + resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); // Then add it to the owners ListenableFuture future = Futures.transform( @@ -237,7 +238,7 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma // Resolve members asynchronously DomainInputResolver resolver = new DomainInputResolver( plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); - resolver.setUseNames(args.hasFlag('n')); + resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME); // Then remove it from the members future = Futures.transform( @@ -309,7 +310,7 @@ public void removeOwner(CommandContext args, // Resolve owners asynchronously DomainInputResolver resolver = new DomainInputResolver( plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); - resolver.setUseNames(args.hasFlag('n')); + resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME); // Then remove it from the owners future = Futures.transform( diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java b/src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java index 55bda8c6..c4ff7a75 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java @@ -39,17 +39,23 @@ /** * Resolves input for a domain (i.e. "player1 player2 <uuid> g:group"). - * - *

Unless {@link #getUseNames()} is true, names will be resolved into - * UUIDs.

*/ public class DomainInputResolver implements Callable { private static final Pattern GROUP_PATTERN = Pattern.compile("(?i)^[G]:(.+)$"); + /** + * The policy for locating users. + */ + public enum UserLocatorPolicy { + UUID_ONLY, + NAME_ONLY, + UUID_AND_NAME + } + private final ProfileService profileService; private final String[] input; - private boolean useNames = false; + private UserLocatorPolicy locatorPolicy = UserLocatorPolicy.UUID_ONLY; /** * Create a new instance. @@ -65,21 +71,22 @@ public DomainInputResolver(ProfileService profileService, String[] input) { } /** - * Get whether names should be used rather than UUIDs. + * Get the policy used for identifying users. * - * @return true to use names + * @return the policy */ - public boolean getUseNames() { - return useNames; + public UserLocatorPolicy getLocatorPolicy() { + return locatorPolicy; } /** - * Set whether names should be used rather than UUIDs. + * Set the policy used for identifying users. * - * @param useNames true to use names + * @param locatorPolicy the policy */ - public void setUseNames(boolean useNames) { - this.useNames = useNames; + public void setLocatorPolicy(UserLocatorPolicy locatorPolicy) { + checkNotNull(locatorPolicy); + this.locatorPolicy = locatorPolicy; } @Override @@ -93,12 +100,19 @@ public DefaultDomain call() throws UnresolvedNamesException { domain.addGroup(m.group(1)); } else { try { - domain.addPlayer(UUID.fromString(UUIDs.addDashes(s))); + // Try to add any UUIDs given + domain.addPlayer(UUID.fromString(UUIDs.addDashes(s.replaceAll("^uuid:", "")))); } catch (IllegalArgumentException e) { - if (useNames) { - domain.addPlayer(s); - } else { - namesToQuery.add(s.toLowerCase()); + switch (locatorPolicy) { + case NAME_ONLY: + domain.addPlayer(s); + break; + case UUID_ONLY: + namesToQuery.add(s.toLowerCase()); + break; + case UUID_AND_NAME: + domain.addPlayer(s); + namesToQuery.add(s.toLowerCase()); } } } From bf48361b10716ba394b2619adc4e30a546dd11c9 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 11:24:25 -0700 Subject: [PATCH 011/133] Switch to Odeum for executors. --- pom.xml | 13 +++++ .../worldguard/bukkit/WorldGuardPlugin.java | 2 +- .../AbstractAsynchronousDatabase.java | 2 +- .../util/concurrency/EvenMoreExecutors.java | 52 ------------------- 4 files changed, 15 insertions(+), 54 deletions(-) delete mode 100644 src/main/java/com/sk89q/worldguard/util/concurrency/EvenMoreExecutors.java diff --git a/pom.xml b/pom.xml index 6197fada..5e38dc99 100644 --- a/pom.xml +++ b/pom.xml @@ -192,6 +192,14 @@ jar
+ + com.sk89q + odeum + 0.2.0-SNAPSHOT + compile + jar + + junit junit @@ -384,6 +392,7 @@ com.jolbox:bonecp org.flywaydb:flyway-core com.sk89q:squirrelid + com.sk89q:odeum @@ -399,6 +408,10 @@ com.sk89q.squirrelid com.sk89q.worldguard.util.profile + + com.sk89q.odeum + com.sk89q.worldguard.internal.odeum + diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 70f41bad..deeae98d 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -29,6 +29,7 @@ import com.sk89q.minecraft.util.commands.MissingNestedCommandException; import com.sk89q.minecraft.util.commands.SimpleInjector; import com.sk89q.minecraft.util.commands.WrappedCommandException; +import com.sk89q.odeum.concurrency.EvenMoreExecutors; import com.sk89q.squirrelid.cache.HashMapCache; import com.sk89q.squirrelid.cache.ProfileCache; import com.sk89q.squirrelid.cache.SQLiteCache; @@ -50,7 +51,6 @@ import com.sk89q.worldguard.protection.GlobalRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; -import com.sk89q.worldguard.util.concurrency.EvenMoreExecutors; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.World; diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java index 0eac245d..65fce0b7 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java @@ -22,9 +22,9 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; +import com.sk89q.odeum.concurrency.EvenMoreExecutors; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.util.concurrency.EvenMoreExecutors; import javax.annotation.Nullable; import java.util.HashMap; diff --git a/src/main/java/com/sk89q/worldguard/util/concurrency/EvenMoreExecutors.java b/src/main/java/com/sk89q/worldguard/util/concurrency/EvenMoreExecutors.java deleted file mode 100644 index 8eac52e6..00000000 --- a/src/main/java/com/sk89q/worldguard/util/concurrency/EvenMoreExecutors.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.util.concurrency; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * Even more {@code ExecutorService} factory methods. - */ -public final class EvenMoreExecutors { - - private EvenMoreExecutors() { - } - - /** - * Creates a thread pool that creates new threads as needed up to - * a maximum number of threads, but will reuse previously constructed - * threads when they are available. - * - * @return the newly created thread pool - */ - public static ExecutorService newBoundedCachedThreadPool(int minThreads, int maxThreads, int maxPoolSize) { - ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( - minThreads, maxThreads, - 60L, TimeUnit.SECONDS, - new LinkedBlockingQueue(maxPoolSize)); - threadPoolExecutor.allowCoreThreadTimeOut(true); - return threadPoolExecutor; - } - - -} From 32e39f5c7c3c47740d299218c692e3c48a12a3ee Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 11:25:27 -0700 Subject: [PATCH 012/133] Make some dependencies optional=true. --- pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pom.xml b/pom.xml index 5e38dc99..c42e9773 100644 --- a/pom.xml +++ b/pom.xml @@ -136,6 +136,7 @@ 5.0 compile jar + true @@ -144,6 +145,7 @@ 0.8.0.RELEASE compile jar + true @@ -158,6 +160,8 @@ com.google.code.findbugs jsr305 1.3.9 + compile + jar @@ -174,6 +178,7 @@ 2.0 compile jar + true @@ -182,6 +187,7 @@ 3.0 compile jar + true @@ -190,6 +196,7 @@ 0.1.0-SNAPSHOT compile jar + true @@ -198,6 +205,7 @@ 0.2.0-SNAPSHOT compile jar + true From 471b4496c947156058da50268e9108604a86984e Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 18:14:25 -0700 Subject: [PATCH 013/133] Add task management and improve threaded code. --- .../worldguard/bukkit/WorldGuardPlugin.java | 77 ++++- .../bukkit/commands/AsyncCommandHelper.java | 147 ++++++++ .../bukkit/commands/CommandUtils.java | 149 +------- .../commands/FutureProgressListener.java | 55 +++ .../commands/MessageFutureCallback.java | 103 ++++++ .../bukkit/commands/RegionCommands.java | 317 +++++++----------- .../bukkit/commands/RegionMemberCommands.java | 57 ++-- .../bukkit/commands/WorldGuardCommands.java | 74 +++- .../database/mysql/UserToIdCache.java | 181 ---------- .../internal/util/sql/StatementUtils.java | 55 +++ .../protection/GlobalRegionManager.java | 2 +- .../protection/databases/CSVDatabase.java | 24 +- .../InvalidTableFormatException.java | 39 --- .../protection/databases/MySQLDatabase.java | 16 +- .../databases/ProtectionDatabase.java | 10 +- .../protection/databases/RegionDBUtil.java | 51 +-- .../protection/databases/YAMLDatabase.java | 77 +++-- .../AbstractDatabaseMigrator.java | 124 +++---- .../DatabaseMigrator.java | 62 ++-- .../MigrationException.java | 80 ++--- .../{migrators => migrator}/MigratorKey.java | 84 ++--- .../MySQLToYAMLMigrator.java | 194 +++++------ .../YAMLToMySQLMigrator.java | 172 +++++----- .../databases}/mysql/AbstractJob.java | 2 +- .../databases}/mysql/MySQLDatabaseImpl.java | 7 +- .../databases}/mysql/RegionLoader.java | 8 +- .../databases/mysql/RegionWriter.java} | 30 +- .../databases/mysql/UserRowCache.java | 184 ++++++++++ .../managers/FlatRegionManager.java | 16 +- .../managers/PRTreeRegionManager.java | 29 +- .../protection/managers/RegionManager.java | 1 + .../protection/regions/ProtectedRegion.java | 32 +- .../com/sk89q/worldguard/util/RegionUtil.java | 4 +- 33 files changed, 1344 insertions(+), 1119 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java delete mode 100644 src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java create mode 100644 src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/InvalidTableFormatException.java rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/AbstractDatabaseMigrator.java (95%) rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/DatabaseMigrator.java (91%) rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/MigrationException.java (92%) rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/MigratorKey.java (92%) rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/MySQLToYAMLMigrator.java (95%) rename src/main/java/com/sk89q/worldguard/protection/databases/{migrators => migrator}/YAMLToMySQLMigrator.java (95%) rename src/main/java/com/sk89q/worldguard/{internal/protection/database => protection/databases}/mysql/AbstractJob.java (97%) rename src/main/java/com/sk89q/worldguard/{internal/protection/database => protection/databases}/mysql/MySQLDatabaseImpl.java (97%) rename src/main/java/com/sk89q/worldguard/{internal/protection/database => protection/databases}/mysql/RegionLoader.java (98%) rename src/main/java/com/sk89q/worldguard/{internal/protection/database/mysql/RegionCommitter.java => protection/databases/mysql/RegionWriter.java} (96%) create mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index deeae98d..a7213061 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -30,6 +30,9 @@ import com.sk89q.minecraft.util.commands.SimpleInjector; import com.sk89q.minecraft.util.commands.WrappedCommandException; import com.sk89q.odeum.concurrency.EvenMoreExecutors; +import com.sk89q.odeum.task.SimpleSupervisor; +import com.sk89q.odeum.task.Supervisor; +import com.sk89q.odeum.task.Task; import com.sk89q.squirrelid.cache.HashMapCache; import com.sk89q.squirrelid.cache.ProfileCache; import com.sk89q.squirrelid.cache.SQLiteCache; @@ -49,6 +52,7 @@ import com.sk89q.worldguard.internal.listener.ChestProtectionListener; import com.sk89q.worldguard.internal.listener.RegionProtectionListener; import com.sk89q.worldguard.protection.GlobalRegionManager; +import com.sk89q.worldguard.protection.databases.util.UnresolvedNamesException; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; import org.bukkit.ChatColor; @@ -64,6 +68,7 @@ import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; +import javax.annotation.Nullable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -74,6 +79,9 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; import java.util.jar.JarFile; import java.util.logging.Level; import java.util.zip.ZipEntry; @@ -88,8 +96,8 @@ public class WorldGuardPlugin extends JavaPlugin { private final GlobalRegionManager globalRegionManager; private final ConfigurationManager configuration; private FlagStateManager flagStateManager; - private final ListeningExecutorService executorService = MoreExecutors.listeningDecorator( - EvenMoreExecutors.newBoundedCachedThreadPool(0, 4, 20)); + private final Supervisor supervisor = new SimpleSupervisor(); + private ListeningExecutorService executorService; private ProfileService profileService; private ProfileCache profileCache; @@ -124,6 +132,7 @@ public static WorldGuardPlugin inst() { @Override @SuppressWarnings("deprecation") public void onEnable() { + executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20)); // Set the proper command injector commands.setInjector(new SimpleInjector(this)); @@ -226,14 +235,33 @@ public void run() { @Override public void onDisable() { + executorService.shutdownNow(); + + try { + getLogger().log(Level.INFO, "Shutting down executor and waiting for any pending tasks..."); + + List> tasks = supervisor.getTasks(); + if (!tasks.isEmpty()) { + StringBuilder builder = new StringBuilder("Known tasks:"); + for (Task task : tasks) { + builder.append("\n"); + builder.append(task.getName()); + } + getLogger().log(Level.INFO, builder.toString()); + } + + executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.DAYS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + globalRegionManager.unload(); configuration.unload(); this.getServer().getScheduler().cancelTasks(this); } @Override - public boolean onCommand(CommandSender sender, Command cmd, String label, - String[] args) { + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { try { commands.execute(cmd.getName(), args, sender, sender); } catch (CommandPermissionsException e) { @@ -244,12 +272,7 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, sender.sendMessage(ChatColor.RED + e.getMessage()); sender.sendMessage(ChatColor.RED + e.getUsage()); } catch (WrappedCommandException e) { - if (e.getCause() instanceof NumberFormatException) { - sender.sendMessage(ChatColor.RED + "Number expected, string received instead."); - } else { - sender.sendMessage(ChatColor.RED + "An error has occurred. See console."); - e.printStackTrace(); - } + sender.sendMessage(ChatColor.RED + convertThrowable(e.getCause())); } catch (CommandException e) { sender.sendMessage(ChatColor.RED + e.getMessage()); } @@ -257,6 +280,31 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, return true; } + /** + * Convert the throwable into a somewhat friendly message. + * + * @param throwable the throwable + * @return a message + */ + public String convertThrowable(@Nullable Throwable throwable) { + if (throwable instanceof NumberFormatException) { + return "Number expected, string received instead."; + } else if (throwable instanceof RejectedExecutionException) { + return "There are currently too many tasks queued to add yours. Use /wg running to list queued and running tasks."; + } else if (throwable instanceof CancellationException) { + return "Task was cancelled"; + } else if (throwable instanceof InterruptedException) { + return "Task was interrupted"; + } else if (throwable instanceof UnresolvedNamesException) { + return throwable.getMessage(); + } else if (throwable instanceof Exception) { + getLogger().log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable); + return "Unexpected error occurred: " + ((Exception) throwable).getMessage(); + } else { + return "Unknown error"; + } + } + /** * Get the GlobalRegionManager. * @@ -295,6 +343,15 @@ public ConfigurationManager getGlobalStateManager() { return configuration; } + /** + * Get the supervisor. + * + * @return the supervisor + */ + public Supervisor getSupervisor() { + return supervisor; + } + /** * Get the global executor service for internal usage (please use your * own executor service). diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java new file mode 100644 index 00000000..184e9306 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java @@ -0,0 +1,147 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.sk89q.odeum.task.FutureForwardingTask; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.World; +import org.bukkit.command.CommandSender; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +class AsyncCommandHelper { + + private final ListenableFuture future; + private final WorldGuardPlugin plugin; + private final CommandSender sender; + @Nullable + private Object[] formatArgs; + + private AsyncCommandHelper(ListenableFuture future, WorldGuardPlugin plugin, CommandSender sender) { + checkNotNull(future); + checkNotNull(plugin); + checkNotNull(sender); + + this.future = future; + this.plugin = plugin; + this.sender = sender; + } + + public AsyncCommandHelper formatUsing(Object... args) { + this.formatArgs = args; + return this; + } + + private String format(String message) { + if (formatArgs != null) { + return String.format(message, formatArgs); + } else { + return message; + } + } + + public AsyncCommandHelper registerWithSupervisor(String description) { + plugin.getSupervisor().monitor( + FutureForwardingTask.create( + future, format(description), sender)); + return this; + } + + public AsyncCommandHelper sendMessageAfterDelay(String message) { + FutureProgressListener.addProgressListener(future, sender, format(message)); + return this; + } + + public AsyncCommandHelper thenRespondWith(String success, String failure) { + // Send a response message + Futures.addCallback( + future, + new MessageFutureCallback.Builder(plugin, sender) + .onSuccess(format(success)) + .onFailure(format(failure)) + .build()); + return this; + } + + public AsyncCommandHelper thenTellErrorsOnly(String failure) { + // Send a response message + Futures.addCallback( + future, + new MessageFutureCallback.Builder(plugin, sender) + .onFailure(format(failure)) + .build()); + return this; + } + + public AsyncCommandHelper forRegionDataLoad(World world, boolean silent) { + checkNotNull(world); + + formatUsing(world.getName()); + registerWithSupervisor("Loading region data for '%s'"); + if (silent) { + thenTellErrorsOnly("Failed to load regions '%s'"); + } else { + sendMessageAfterDelay("(Please wait... loading the region data for '%s')"); + thenRespondWith( + "Loaded region data for '%s'", + "Failed to load regions '%s'"); + } + + return this; + } + + public AsyncCommandHelper forRegionDataSave(World world, boolean silent) { + checkNotNull(world); + + formatUsing(world.getName()); + registerWithSupervisor("Saving region data for '%s'"); + if (silent) { + thenTellErrorsOnly("Failed to save regions '%s'"); + } else { + sendMessageAfterDelay("(Please wait... saving the region data for '%s')"); + thenRespondWith( + "Saved region data for '%s'", + "Failed to load regions '%s'"); + } + + return this; + } + + public AsyncCommandHelper thenSaveRegionData(RegionManager manager, World world) { + checkNotNull(manager); + checkNotNull(world); + + ListenableFuture future = manager.save(true); + + AsyncCommandHelper.wrap(future, plugin, sender).forRegionDataSave(world, true); + + return this; + } + + public static AsyncCommandHelper wrap(ListenableFuture future, WorldGuardPlugin plugin, CommandSender sender) { + return new AsyncCommandHelper(future, plugin, sender); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java index 9fb2e002..1d0f7080 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java @@ -20,85 +20,39 @@ package com.sk89q.worldguard.bukkit.commands; import com.google.common.base.Function; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; -import com.sk89q.worldguard.protection.databases.util.UnresolvedNamesException; -import com.sk89q.worldguard.protection.managers.RegionManager; -import org.bukkit.ChatColor; -import org.bukkit.World; +import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; import javax.annotation.Nullable; -import java.util.Timer; -import java.util.concurrent.CancellationException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static com.google.common.base.Preconditions.checkNotNull; /** * Command-related utility methods. */ final class CommandUtils { - private static final Logger log = Logger.getLogger(CommandUtils.class.getCanonicalName()); - private static final Timer timer = new Timer(); - private static final int MESSAGE_DELAY = 1000; - private CommandUtils() { } /** - * Add a message that gets shown after a delay if the future has not - * completed. + * Get the name of the given owner object. * - * @param future the future - * @param sender the sender to send a message to - * @param message the message to send (will be grey) + * @param owner the owner object + * @return a name */ - static void progressCallback(ListenableFuture future, CommandSender sender, String message) { - checkNotNull(future); - checkNotNull(sender); - checkNotNull(message); - - final MessageTimerTask task = new MessageTimerTask(sender, ChatColor.GRAY + message); - timer.schedule(task, MESSAGE_DELAY); - future.addListener(new Runnable() { - @Override - public void run() { - task.cancel(); - } - }, MoreExecutors.sameThreadExecutor()); - } - - /** - * Create a callback to print a message to the user. - * - * @param sender the sender - * @param successMessage a success message or {@code null} to print nothing - * @param errorMessage an error message - * @param ignored type - * @return a callback - */ - static FutureCallback messageCallback(final CommandSender sender, @Nullable final String successMessage, final String errorMessage) { - checkNotNull(sender); - checkNotNull(errorMessage); - - return new FutureCallback() { - @Override - public void onSuccess(@Nullable T o) { - if (successMessage != null) { - sender.sendMessage(ChatColor.YELLOW + successMessage); - } - } - - @Override - public void onFailure(@Nullable Throwable throwable) { - sender.sendMessage(ChatColor.RED + errorMessage + ": " + convertThrowable(throwable)); - } - }; + static String getOwnerName(@Nullable Object owner) { + if (owner == null) { + return "?"; + } else if (owner instanceof Player) { + return ((Player) owner).getName(); + } else if (owner instanceof ConsoleCommandSender) { + return "*CONSOLE*"; + } else if (owner instanceof BlockCommandSender) { + return ((BlockCommandSender) owner).getBlock().getLocation().toString(); + } else { + return "?"; + } } /** @@ -118,71 +72,4 @@ public Object apply(@Nullable String s) { }; } - /** - * Return a function to add a prefix and suffix to the input string. - * - * @param prefix the prefix - * @param suffix the suffix - * @return a function - */ - static Function messageAppender(final String prefix, final String suffix) { - return new Function() { - @Override - public String apply(@Nullable String s) { - return prefix + s + suffix; - } - }; - } - - /** - * Create a callback to save a region manager on success. - * - * @param sender the sender - * @param manager the region manager - * @param world the world - * @param silent true to not print a success message - * @param an ignored type - * @return a callback - */ - static FutureCallback saveRegionsCallback(final CommandSender sender, final RegionManager manager, final World world, final boolean silent) { - checkNotNull(sender); - checkNotNull(manager); - checkNotNull(world); - - return new FutureCallback() { - @Override - public void onSuccess(@Nullable T o) { - ListenableFuture future = manager.save(true); - String successMessage = silent ? null : "Successfully saved the region data for '" + world.getName() + "'."; - String failureMessage = "Failed to save the region data for '" + world.getName() + "'"; - Futures.addCallback(future, messageCallback(sender, successMessage, failureMessage)); - } - - @Override - public void onFailure(@Nullable Throwable throwable) { - } - }; - } - - /** - * Convert the throwable into a somewhat friendly message. - * - * @param throwable the throwable - * @return a message - */ - private static String convertThrowable(@Nullable Throwable throwable) { - if (throwable instanceof CancellationException) { - return "Task was cancelled"; - } else if (throwable instanceof InterruptedException) { - return "Task was interrupted"; - } else if (throwable instanceof UnresolvedNamesException) { - return throwable.getMessage(); - } else if (throwable instanceof Exception) { - log.log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable); - return "Unexpected error occurred: " + ((Exception) throwable).getMessage(); - } else { - return "Unknown error"; - } - } - } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java new file mode 100644 index 00000000..549aaa05 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java @@ -0,0 +1,55 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import java.util.Timer; + +import static com.google.common.base.Preconditions.checkNotNull; + +class FutureProgressListener implements Runnable { + + private static final Timer timer = new Timer(); + private static final int MESSAGE_DELAY = 1000; + + private final MessageTimerTask task; + + FutureProgressListener(CommandSender sender, String message) { + checkNotNull(sender); + checkNotNull(message); + + task = new MessageTimerTask(sender, ChatColor.GRAY + message); + timer.schedule(task, MESSAGE_DELAY); + } + + @Override + public void run() { + task.cancel(); + } + + public static void addProgressListener(ListenableFuture future, CommandSender sender, String message) { + future.addListener(new FutureProgressListener(sender, message), MoreExecutors.sameThreadExecutor()); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java new file mode 100644 index 00000000..cb25126a --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java @@ -0,0 +1,103 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands; + +import com.google.common.util.concurrent.FutureCallback; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +class MessageFutureCallback implements FutureCallback { + + private final WorldGuardPlugin plugin; + private final CommandSender sender; + @Nullable + private final String success; + @Nullable + private final String failure; + + private MessageFutureCallback(WorldGuardPlugin plugin, CommandSender sender, @Nullable String success, @Nullable String failure) { + this.plugin = plugin; + this.sender = sender; + this.success = success; + this.failure = failure; + } + + @Override + public void onSuccess(@Nullable V v) { + if (success != null) { + sender.sendMessage(ChatColor.YELLOW + success); + } + } + + @Override + public void onFailure(@Nullable Throwable throwable) { + String failure = this.failure != null ? this.failure : "An error occurred"; + sender.sendMessage(ChatColor.RED + failure + ": " + plugin.convertThrowable(throwable)); + } + + static class Builder { + private final WorldGuardPlugin plugin; + private final CommandSender sender; + @Nullable + private String success; + @Nullable + private String failure; + + Builder(WorldGuardPlugin plugin, CommandSender sender) { + checkNotNull(plugin); + checkNotNull(sender); + + this.plugin = plugin; + this.sender = sender; + } + + public Builder onSuccess(@Nullable String message) { + this.success = message; + return this; + } + + public Builder onFailure(@Nullable String message) { + this.failure = message; + return this; + } + + public MessageFutureCallback build() { + return new MessageFutureCallback(plugin, sender, success, failure); + } + } + + public static MessageFutureCallback createRegionLoadCallback(WorldGuardPlugin plugin, CommandSender sender) { + return new Builder(plugin, sender) + .onSuccess("Successfully load the region data.") + .build(); + } + + public static MessageFutureCallback createRegionSaveCallback(WorldGuardPlugin plugin, CommandSender sender) { + return new Builder(plugin, sender) + .onSuccess("Successfully saved the region data.") + .build(); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java index 15b519d9..4bf89246 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java @@ -19,7 +19,6 @@ package com.sk89q.worldguard.bukkit.commands; -import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.sk89q.minecraft.util.commands.Command; @@ -40,9 +39,9 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.databases.RegionDBUtil; -import com.sk89q.worldguard.protection.databases.migrators.AbstractDatabaseMigrator; -import com.sk89q.worldguard.protection.databases.migrators.MigrationException; -import com.sk89q.worldguard.protection.databases.migrators.MigratorKey; +import com.sk89q.worldguard.protection.databases.migrator.AbstractDatabaseMigrator; +import com.sk89q.worldguard.protection.databases.migrator.MigrationException; +import com.sk89q.worldguard.protection.databases.migrator.MigratorKey; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; @@ -54,6 +53,7 @@ import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.command.CommandSender; @@ -65,8 +65,6 @@ import java.util.Date; import java.util.List; import java.util.Map; -import java.util.concurrent.RejectedExecutionException; -import java.util.logging.Level; /** * Implements the /region commands for WorldGuard. @@ -91,7 +89,18 @@ public RegionCommands(WorldGuardPlugin plugin) { private static RegionPermissionModel getPermissionModel(CommandSender sender) { return new RegionPermissionModel(WorldGuardPlugin.inst(), sender); } - + + /** + * Save the regions asynchronously and alert the sender if any errors + * occur during save. + * + * @param manager the manager to save + * @param sender the sender + */ + private void saveRegionsSilently(RegionManager manager, World world, CommandSender sender) { + AsyncCommandHelper.wrap(Futures.immediateFuture(null), plugin, sender).thenSaveRegionData(manager, world); + } + /** * Gets the world from the given flag, or falling back to the the current player * if the sender is a player, otherwise reporting an error. @@ -102,8 +111,7 @@ private static RegionPermissionModel getPermissionModel(CommandSender sender) { * @return a world * @throws CommandException on error */ - private static World getWorld(CommandContext args, CommandSender sender, char flag) - throws CommandException { + private static World getWorld(CommandContext args, CommandSender sender, char flag) throws CommandException { if (args.hasFlag(flag)) { return WorldGuardPlugin.inst().matchWorld(sender, args.getFlag(flag)); } else { @@ -150,9 +158,7 @@ private static String validateRegionId(String id, boolean allowGlobal) * @param allowGlobal true to allow selecting __global__ * @throws CommandException thrown if no region is found by the given name */ - private static ProtectedRegion findExistingRegion( - RegionManager regionManager, String id, boolean allowGlobal) - throws CommandException { + private static ProtectedRegion findExistingRegion(RegionManager regionManager, String id, boolean allowGlobal) throws CommandException { // Validate the id validateRegionId(id, allowGlobal); @@ -186,8 +192,7 @@ private static ProtectedRegion findExistingRegion( * @return a region * @throws CommandException thrown if no region was found */ - private static ProtectedRegion findRegionStandingIn( - RegionManager regionManager, Player player) throws CommandException { + private static ProtectedRegion findRegionStandingIn(RegionManager regionManager, Player player) throws CommandException { return findRegionStandingIn(regionManager, player, false); } @@ -206,8 +211,7 @@ private static ProtectedRegion findRegionStandingIn( * @return a region * @throws CommandException thrown if no region was found */ - private static ProtectedRegion findRegionStandingIn( - RegionManager regionManager, Player player, boolean allowGlobal) throws CommandException { + private static ProtectedRegion findRegionStandingIn(RegionManager regionManager, Player player, boolean allowGlobal) throws CommandException { ApplicableRegionSet set = regionManager.getApplicableRegions( player.getLocation()); @@ -272,9 +276,7 @@ private static Selection getSelection(Player player) throws CommandException { * @return a new region * @throws CommandException thrown on an error */ - private static ProtectedRegion createRegionFromSelection(Player player, String id) - throws CommandException { - + private static ProtectedRegion createRegionFromSelection(Player player, String id) throws CommandException { Selection selection = getSelection(player); // Detect the type of region from WorldEdit @@ -293,84 +295,6 @@ private static ProtectedRegion createRegionFromSelection(Player player, String i } } - /** - * Save the region database. - * - * @param sender the sender - * @param regionManager the region manager - * @param silent whether to suppress messages sent to the player - * @throws CommandException throw on an error - */ - private ListenableFuture commitChanges(final CommandSender sender, RegionManager regionManager, final World world, final boolean silent) throws CommandException { - ListenableFuture future; - - try { - future = regionManager.save(true); - } catch (RejectedExecutionException e) { - throw new CommandException("There are too many interleaved load and save tasks currently queued. Please try again later."); - } - - if (!silent) { - sender.sendMessage(ChatColor.GRAY + "(A save of the region data has been queued.)"); - } - - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(Object o) { - if (!silent) { - sender.sendMessage(ChatColor.YELLOW + "Successfully saved the region data for the '" + world.getName() + "' world."); - } - } - - @Override - public void onFailure(Throwable throwable) { - sender.sendMessage(ChatColor.RED + "Failed to save region data for the '" + world.getName() + "' world: " + throwable.getMessage()); - plugin.getLogger().log(Level.WARNING, "Failed to save region data", throwable); - } - }); - - return future; - } - - /** - * Load the region database. - * - * @param sender the sender - * @param regionManager the region manager - * @param silent whether to suppress messages sent to the player - * @throws CommandException throw on an error - */ - private ListenableFuture reloadChanges(final CommandSender sender, RegionManager regionManager, final World world, final boolean silent) throws CommandException { - ListenableFuture future; - - try { - future = regionManager.load(true); - } catch (RejectedExecutionException e) { - throw new CommandException("There are too many interleaved load and save tasks currently queued. Please try again later."); - } - - if (!silent) { - sender.sendMessage(ChatColor.GRAY + "(A load of the region data has been queued.)"); - } - - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(Object o) { - if (!silent) { - sender.sendMessage(ChatColor.YELLOW + "Successfully loaded the region data for the '" + world.getName() + "' world."); - } - } - - @Override - public void onFailure(Throwable throwable) { - sender.sendMessage(ChatColor.RED + "Failed to load region data for the '" + world.getName() + "' world: " + throwable.getMessage()); - plugin.getLogger().log(Level.WARNING, "Failed to load region data", throwable); - } - }); - - return future; - } - /** * Set a player's selection to a given region. * @@ -422,9 +346,7 @@ private static void setPlayerSelection(Player player, ProtectedRegion region) * @param value the value * @throws InvalidFlagFormat thrown if the value is invalid */ - private static void setFlag(ProtectedRegion region, - Flag flag, CommandSender sender, String value) - throws InvalidFlagFormat { + private static void setFlag(ProtectedRegion region, Flag flag, CommandSender sender, String value) throws InvalidFlagFormat { region.setFlag(flag, flag.parseInput(WorldGuardPlugin.inst(), sender, value)); } @@ -439,8 +361,7 @@ private static void setFlag(ProtectedRegion region, usage = " [ [ []]]", desc = "Defines a region", min = 1) - public void define(CommandContext args, CommandSender sender) - throws CommandException { + public void define(CommandContext args, CommandSender sender) throws CommandException { Player player = plugin.checkPlayer(sender); // Check permissions @@ -466,9 +387,6 @@ public void define(CommandContext args, CommandSender sender) if (args.argsLength() > 1) { region.setOwners(RegionDBUtil.parseDomainString(args.getSlice(1), 1)); } - - regionManager.addRegion(region); - commitChanges(sender, regionManager, player.getWorld(), true); // Save to disk // Issue a warning about height int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); @@ -488,6 +406,12 @@ public void define(CommandContext args, CommandSender sender) // Tell the user sender.sendMessage(ChatColor.YELLOW + "A new region has been made named '" + id + "'."); + + // Add region + regionManager.addRegion(region); + + // Save regions + saveRegionsSilently(regionManager, player.getWorld(), sender); } /** @@ -501,8 +425,7 @@ public void define(CommandContext args, CommandSender sender) usage = "", desc = "Re-defines the shape of a region", min = 1, max = 1) - public void redefine(CommandContext args, CommandSender sender) - throws CommandException { + public void redefine(CommandContext args, CommandSender sender) throws CommandException { Player player = plugin.checkPlayer(sender); World world = player.getWorld(); @@ -532,9 +455,6 @@ public void redefine(CommandContext args, CommandSender sender) // This should not be thrown } - regionManager.addRegion(region); // Replace region - commitChanges(sender, regionManager, player.getWorld(), true); // Save to disk - // Issue a warning about height int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); if (height <= 2) { @@ -543,6 +463,11 @@ public void redefine(CommandContext args, CommandSender sender) } sender.sendMessage(ChatColor.YELLOW + "Region '" + id + "' updated with new area."); + + regionManager.addRegion(region); // Replace region + + // Save regions + saveRegionsSilently(regionManager, world, sender); } /** @@ -573,8 +498,8 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep String id = validateRegionId(args.getString(0), false); // Can't replace existing regions - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - if (mgr.hasRegion(id)) { + RegionManager regionManager = plugin.getGlobalRegionManager().get(player.getWorld()); + if (regionManager.hasRegion(id)) { throw new CommandException( "That region already exists. Please choose a different name."); } @@ -593,13 +518,13 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep if (!permModel.mayClaimRegionsUnbounded()) { int maxRegionCount = wcfg.getMaxRegionCount(player); if (maxRegionCount >= 0 - && mgr.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) { + && regionManager.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) { throw new CommandException( "You own too many regions, delete one first to claim a new one."); } } - ProtectedRegion existing = mgr.getRegionExact(id); + ProtectedRegion existing = regionManager.getRegionExact(id); // Check for an existing region if (existing != null) { @@ -610,7 +535,7 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep } // We have to check whether this region violates the space of any other reion - ApplicableRegionSet regions = mgr.getApplicableRegions(region); + ApplicableRegionSet regions = regionManager.getApplicableRegions(region); // Check if this region overlaps any other region if (regions.size() > 0) { @@ -634,34 +559,15 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep } } - /*if (plugin.getGlobalConfiguration().getiConomy() != null && wcfg.useiConomy && wcfg.buyOnClaim) { - if (iConomy.getBank().hasAccount(player.getName())) { - Account account = iConomy.getBank().getAccount(player.getName()); - double balance = account.getBalance(); - double regionCosts = region.countBlocks() * wcfg.buyOnClaimPrice; - if (balance >= regionCosts) { - account.subtract(regionCosts); - player.sendMessage(ChatColor.YELLOW + "You have bought that region for " - + iConomy.getBank().format(regionCosts)); - account.save(); - } else { - player.sendMessage(ChatColor.RED + "You have not enough money."); - player.sendMessage(ChatColor.RED + "The region you want to claim costs " - + iConomy.getBank().format(regionCosts)); - player.sendMessage(ChatColor.RED + "You have " + iConomy.getBank().format(balance)); - return; - } - } else { - player.sendMessage(ChatColor.YELLOW + "You have not enough money."); - return; - } - }*/ - region.getOwners().addPlayer(player.getName()); - - mgr.addRegion(region); - commitChanges(sender, mgr, player.getWorld(), true); // Save to disk + sender.sendMessage(ChatColor.YELLOW + "Region '" + id + "' updated with new area."); + + // Replace region + regionManager.addRegion(region); + + // Save regions + saveRegionsSilently(regionManager, player.getWorld(), sender); } /** @@ -733,22 +639,31 @@ public void info(CommandContext args, CommandSender sender) throws CommandExcept throw new CommandPermissionsException(); } - // Print region information - RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, args.hasFlag('u') ? null : plugin.getProfileCache()); - ListenableFuture future = Futures.transform( - plugin.getExecutorService().submit(printout), - CommandUtils.messageFunction(sender)); - Futures.addCallback(future, CommandUtils.messageCallback(sender, null, "Failed to retrieve region info")); - - // Let the player also select the region + // Let the player select the region if (args.hasFlag('s')) { // Check permissions if (!permModel.maySelect(existing)) { throw new CommandPermissionsException(); } - + setPlayerSelection(plugin.checkPlayer(sender), existing); } + + // Print region information + RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, args.hasFlag('u') ? null : plugin.getProfileCache()); + ListenableFuture future = Futures.transform( + plugin.getExecutorService().submit(printout), + CommandUtils.messageFunction(sender)); + + // If it takes too long... + FutureProgressListener.addProgressListener( + future, sender, "(Please wait... fetching region information...)"); + + // Send a response message + Futures.addCallback(future, + new MessageFutureCallback.Builder(plugin, sender) + .onFailure("Failed to fetch region information") + .build()); } /** @@ -963,8 +878,6 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept } } - commitChanges(sender, regionManager, world, true); // Save to disk - // Print region information RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, null); printout.append(ChatColor.GRAY); @@ -972,6 +885,9 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept printout.appendFlagsList(false); printout.append(")"); printout.send(sender); + + // Save regions + saveRegionsSilently(regionManager, world, sender); } /** @@ -1002,11 +918,13 @@ public void setPriority(CommandContext args, CommandSender sender) } existing.setPriority(priority); - commitChanges(sender, regionManager, world, true); // Save to disk sender.sendMessage(ChatColor.YELLOW + "Priority of '" + existing.getId() + "' set to " + priority + " (higher numbers override)."); + + // Save regions + saveRegionsSilently(regionManager, world, sender); } /** @@ -1058,8 +976,6 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE printout.send(sender); return; } - - commitChanges(sender, regionManager, world, true); // Save to disk // Tell the user the current inheritance RegionPrintoutBuilder printout = new RegionPrintoutBuilder(child, null); @@ -1073,6 +989,9 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE printout.append(")"); } printout.send(sender); + + // Save regions + saveRegionsSilently(regionManager, world, sender); } /** @@ -1101,10 +1020,11 @@ public void remove(CommandContext args, CommandSender sender) throws CommandExce } regionManager.removeRegion(existing.getId()); - commitChanges(sender, regionManager, world, true); // Save to disk - sender.sendMessage(ChatColor.YELLOW - + "Region '" + existing.getId() + "' removed."); + sender.sendMessage(ChatColor.YELLOW + "Region '" + existing.getId() + "' removed."); + + // Save regions + saveRegionsSilently(regionManager, world, sender); } /** @@ -1132,33 +1052,33 @@ public void load(CommandContext args, final CommandSender sender) throws Command } if (world != null) { - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - if (regionManager == null) { + RegionManager manager = plugin.getGlobalRegionManager().get(world); + + if (manager == null) { throw new CommandException("No region manager exists for world '" + world.getName() + "'."); } - reloadChanges(sender, regionManager, world, false); + + ListenableFuture future = manager.load(true); + + AsyncCommandHelper.wrap(future, plugin, sender) + .forRegionDataLoad(world, false); } else { + // Load regions for all worlds List> futures = new ArrayList>(); - for (World w : plugin.getServer().getWorlds()) { - RegionManager regionManager = plugin.getGlobalRegionManager().get(w); - if (regionManager == null) { - continue; + for (World w : Bukkit.getServer().getWorlds()) { + RegionManager manager = plugin.getGlobalRegionManager().get(w); + if (manager != null) { + futures.add(manager.load(true)); } - futures.add(reloadChanges(sender, regionManager, world, true)); } + ListenableFuture future = Futures.successfulAsList(futures); - Futures.addCallback(Futures.allAsList(futures), new FutureCallback() { - @Override - public void onSuccess(Object o) { - sender.sendMessage(ChatColor.YELLOW + "Successfully loaded region data for all worlds."); - } - - @Override - public void onFailure(Throwable throwable) { - sender.sendMessage(ChatColor.RED + "Failed to load region data for all worlds: " + throwable.getMessage()); - plugin.getLogger().log(Level.WARNING, "Failed to load region data", throwable); - } - }); + AsyncCommandHelper.wrap(future, plugin, sender) + .registerWithSupervisor("Loading regions for all worlds") + .sendMessageAfterDelay("(Please wait... loading region data for all worlds...)") + .thenRespondWith( + "Successfully load the region data for all worlds.", + "Failed to load regions for all worlds"); } } @@ -1187,34 +1107,33 @@ public void save(CommandContext args, final CommandSender sender) throws Command } if (world != null) { - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - if (regionManager == null) { + RegionManager manager = plugin.getGlobalRegionManager().get(world); + + if (manager == null) { throw new CommandException("No region manager exists for world '" + world.getName() + "'."); } - commitChanges(sender, regionManager, world, false); + + ListenableFuture future = manager.save(true); + + AsyncCommandHelper.wrap(future, plugin, sender) + .forRegionDataSave(world, false); } else { - sender.sendMessage(ChatColor.YELLOW + "Saving all region databases... This might take a bit."); + // Save for all worlds List> futures = new ArrayList>(); - for (World w : plugin.getServer().getWorlds()) { - RegionManager regionManager = plugin.getGlobalRegionManager().get(w); - if (regionManager == null) { - continue; + for (World w : Bukkit.getServer().getWorlds()) { + RegionManager manager = plugin.getGlobalRegionManager().get(w); + if (manager != null) { + futures.add(manager.save(true)); } - futures.add(commitChanges(sender, regionManager, world, true)); } + ListenableFuture future = Futures.successfulAsList(futures); - Futures.addCallback(Futures.allAsList(futures), new FutureCallback() { - @Override - public void onSuccess(Object o) { - sender.sendMessage(ChatColor.YELLOW + "Successfully saved region data for all worlds."); - } - - @Override - public void onFailure(Throwable throwable) { - sender.sendMessage(ChatColor.RED + "Failed to save region data for all worlds: " + throwable.getMessage()); - plugin.getLogger().log(Level.WARNING, "Failed to save region data", throwable); - } - }); + AsyncCommandHelper.wrap(future, plugin, sender) + .registerWithSupervisor("Saving regions for all worlds") + .sendMessageAfterDelay("(Please wait... saving region data for all worlds...)") + .thenRespondWith( + "Successfully saved the region data for all worlds.", + "Failed to save regions for all worlds"); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java index e981d2a8..a817a05b 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java @@ -36,9 +36,6 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import static com.google.common.util.concurrent.Futures.addCallback; -import static com.sk89q.worldguard.bukkit.commands.CommandUtils.*; - // @TODO: A lot of code duplication here! Need to fix. public class RegionMemberCommands { @@ -74,8 +71,8 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE String id = args.getString(0); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = mgr.getRegion(id); + RegionManager manager = plugin.getGlobalRegionManager().get(world); + ProtectedRegion region = manager.getRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -103,9 +100,12 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE plugin.getExecutorService().submit(resolver), resolver.createAddAllFunction(region.getMembers())); - progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); - addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to add members")); - addCallback(future, saveRegionsCallback(sender, mgr, world, true)); + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Adding members to the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with new members.", "Failed to add new members") + .thenSaveRegionData(manager, world); } @Command(aliases = {"addowner", "addowner", "ao"}, @@ -133,8 +133,8 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx String id = args.getString(0); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = mgr.getRegion(id); + RegionManager manager = plugin.getGlobalRegionManager().get(world); + ProtectedRegion region = manager.getRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -148,7 +148,7 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx if (flag != null && flag && owners != null && owners.size() == 0) { if (!plugin.hasPermission(player, "worldguard.region.unlimited")) { int maxRegionCount = plugin.getGlobalStateManager().get(world).getMaxRegionCount(player); - if (maxRegionCount >= 0 && mgr.getRegionCountOfPlayer(localPlayer) + if (maxRegionCount >= 0 && manager.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) { throw new CommandException("You already own the maximum allowed amount of regions."); } @@ -175,9 +175,12 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx plugin.getExecutorService().submit(resolver), resolver.createAddAllFunction(region.getOwners())); - progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); - addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to add owners")); - addCallback(future, saveRegionsCallback(sender, mgr, world, true)); + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Adding owners to the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with new owners.", "Failed to add new owners") + .thenSaveRegionData(manager, world); } @Command(aliases = {"removemember", "remmember", "removemem", "remmem", "rm"}, @@ -205,8 +208,8 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma String id = args.getString(0); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = mgr.getRegion(id); + RegionManager manager = plugin.getGlobalRegionManager().get(world); + ProtectedRegion region = manager.getRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -246,9 +249,12 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma resolver.createRemoveAllFunction(region.getMembers())); } - progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); - addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to remove members")); - addCallback(future, saveRegionsCallback(sender, mgr, world, true)); + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Removing members from the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with members removed.", "Failed to remove members") + .thenSaveRegionData(manager, world); } @Command(aliases = {"removeowner", "remowner", "ro"}, @@ -277,8 +283,8 @@ public void removeOwner(CommandContext args, String id = args.getString(0); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = mgr.getRegion(id); + RegionManager manager = plugin.getGlobalRegionManager().get(world); + ProtectedRegion region = manager.getRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -318,8 +324,11 @@ public void removeOwner(CommandContext args, resolver.createRemoveAllFunction(region.getOwners())); } - progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)"); - addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to remove owners")); - addCallback(future, saveRegionsCallback(sender, mgr, world, true)); + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Removing owners from the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with owners removed.", "Failed to remove owners") + .thenSaveRegionData(manager, world); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java index 9e7fab21..41690de1 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java @@ -19,24 +19,27 @@ package com.sk89q.worldguard.bukkit.commands; -import java.io.File; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; +import com.sk89q.odeum.task.Task; +import com.sk89q.odeum.task.TaskStateComparator; import com.sk89q.worldguard.bukkit.LoggerToChatHandler; import com.sk89q.worldguard.bukkit.ReportWriter; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.util.PastebinPoster; import com.sk89q.worldguard.util.PastebinPoster.PasteCallback; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; public class WorldGuardCommands { private final WorldGuardPlugin plugin; @@ -56,6 +59,11 @@ public void version(CommandContext args, CommandSender sender) throws CommandExc @Command(aliases = {"reload"}, desc = "Reload WorldGuard configuration", max = 0) @CommandPermissions({"worldguard.reload"}) public void reload(CommandContext args, CommandSender sender) throws CommandException { + // TODO: This is subject to a race condition, but at least other commands are not being processed concurrently + List> tasks = plugin.getSupervisor().getTasks(); + if (!tasks.isEmpty()) { + throw new CommandException("There are currently pending tasks. Use /wg running and /wg cancelall to monitor and cancel these tasks first."); + } LoggerToChatHandler handler = null; Logger minecraftLogger = null; @@ -135,4 +143,52 @@ public void flushStates(CommandContext args, CommandSender sender) throws Comman } } + @Command(aliases = {"cancelall"}, flags = "f", desc = "Cancel all running tasks", max = 0) + @CommandPermissions("worldguard.cancel.all") + public void cancelAllTasks(CommandContext args, CommandSender sender) throws CommandException { + List> tasks = plugin.getSupervisor().getTasks(); + + boolean force = args.hasFlag('f'); + if (force && !plugin.hasPermission(sender, "worldguard.cancel.force")) { + throw new CommandException("You are not permitted to force cancel."); + } + + if (!tasks.isEmpty()) { + for (Task task : tasks) { + task.cancel(force); + sender.sendMessage(ChatColor.YELLOW + "Cancelled: " + ChatColor.WHITE + task.getName()); + } + } else { + sender.sendMessage(ChatColor.YELLOW + "There are currently no running tasks."); + } + } + + @Command(aliases = {"running", "queue"}, desc = "List running tasks", max = 0) + @CommandPermissions("worldguard.running") + public void listRunningTasks(CommandContext args, CommandSender sender) throws CommandException { + List> tasks = plugin.getSupervisor().getTasks(); + + if (!tasks.isEmpty()) { + Collections.sort(tasks, new TaskStateComparator()); + StringBuilder builder = new StringBuilder(); + builder.append(ChatColor.GRAY); + builder.append("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"); + builder.append(" Running tasks "); + builder.append("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"); + builder.append("\n").append(ChatColor.GRAY).append("Note: Some 'running' tasks may be waiting to be start."); + for (Task task : tasks) { + builder.append("\n"); + builder.append(ChatColor.BLUE).append("(").append(task.getState().name()).append(") "); + builder.append(ChatColor.YELLOW); + builder.append(CommandUtils.getOwnerName(task.getOwner())); + builder.append(": "); + builder.append(ChatColor.WHITE); + builder.append(task.getName()); + } + sender.sendMessage(builder.toString()); + } else { + sender.sendMessage(ChatColor.YELLOW + "There are currently no running tasks."); + } + } + } diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java b/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java deleted file mode 100644 index cf0d95a5..00000000 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/UserToIdCache.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.internal.protection.database.mysql; - -import com.google.common.collect.Lists; -import com.sk89q.worldguard.protection.databases.RegionDBUtil; -import com.sk89q.worldguard.util.io.Closer; - -import javax.annotation.Nullable; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static com.google.common.base.Preconditions.checkNotNull; - -abstract class UserToIdCache extends AbstractJob { - - private static final int MAX_NUMBER_PER_QUERY = 100; - - private final Map cache = new HashMap(); - private final String fieldName; - - protected UserToIdCache(MySQLDatabaseImpl database, Connection conn, String fieldName) { - super(database, conn); - this.fieldName = fieldName; - } - - protected abstract String fromType(V o); - - protected abstract V toType(String o); - - protected abstract V toKey(V object); - - @Nullable - public Integer find(V object) { - return cache.get(object); - } - - public void fetch(Collection entries) throws SQLException { - checkNotNull(entries); - - // Get a list of missing entries - List fetchList = new ArrayList(); - for (V entry : entries) { - if (!cache.containsKey(toKey(entry))) { - fetchList.add(entry); - } - } - - if (fetchList.isEmpty()) { - return; // Nothing to do - } - - // Search for entries - for (List partition : Lists.partition(fetchList, MAX_NUMBER_PER_QUERY)) { - Closer closer = Closer.create(); - try { - PreparedStatement statement = closer.register(conn.prepareStatement( - String.format( - "SELECT `user`.`id`, `user`.`" + fieldName + "` " + - "FROM `" + config.sqlTablePrefix + "user` AS `user` " + - "WHERE `" + fieldName + "` IN (%s)", - RegionDBUtil.preparePlaceHolders(partition.size())))); - - String[] values = new String[partition.size()]; - int i = 0; - for (V entry : partition) { - values[i] = fromType(entry); - i++; - } - - RegionDBUtil.setValues(statement, values); - ResultSet results = closer.register(statement.executeQuery()); - while (results.next()) { - cache.put(toKey(toType(results.getString(fieldName))), results.getInt("id")); - } - } finally { - closer.closeQuietly(); - } - } - - List missing = new ArrayList(); - for (V entry : fetchList) { - if (!cache.containsKey(toKey(entry))) { - missing.add(entry); - } - } - - // Insert entries that are missing - if (!missing.isEmpty()) { - Closer closer = Closer.create(); - try { - PreparedStatement statement = closer.register(conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "user` (`id`, `" + fieldName + "`) VALUES (null, ?)", - Statement.RETURN_GENERATED_KEYS)); - - for (V entry : missing) { - statement.setString(1, fromType(entry)); - statement.execute(); - - ResultSet generatedKeys = statement.getGeneratedKeys(); - if (generatedKeys.first()) { - cache.put(toKey(entry), generatedKeys.getInt(1)); - } else { - logger.warning("Could not get the database ID for user " + entry); - } - } - } finally { - closer.closeQuietly(); - } - } - } - - static class NameToIdCache extends UserToIdCache { - protected NameToIdCache(MySQLDatabaseImpl database, Connection conn) { - super(database, conn, "name"); - } - - @Override - protected String fromType(String o) { - return o; - } - - @Override - protected String toType(String o) { - return o; - } - - @Override - protected String toKey(String object) { - return object.toLowerCase(); - } - } - - static class UUIDToIdCache extends UserToIdCache { - protected UUIDToIdCache(MySQLDatabaseImpl database, Connection conn) { - super(database, conn, "uuid"); - } - - @Override - protected String fromType(UUID o) { - return o.toString(); - } - - @Override - protected UUID toType(String o) { - return UUID.fromString(o); - } - - @Override - protected UUID toKey(UUID object) { - return object; - } - } - -} diff --git a/src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java b/src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java new file mode 100644 index 00000000..6f3eea39 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java @@ -0,0 +1,55 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.util.sql; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public class StatementUtils { + /** + * Creates a comma separated list of PreparedStatement place holders + * + * @param length The number of wildcards to create + * @return A string with {@code length} wildcards for usage in a PreparedStatement + */ + public static String preparePlaceHolders(int length) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < length;) { + builder.append("?"); + if (++i < length) { + builder.append(","); + } + } + return builder.toString(); + } + + /** + * Adds all of the parsed values to the PreparedStatement + * + * @param preparedStatement The preparedStanement to add to + * @param values The values to set + * @throws SQLException see {@link PreparedStatement#setString(int, String)} + */ + public static void setValues(PreparedStatement preparedStatement, String... values) throws SQLException { + for (int i = 0; i < values.length; i++) { + preparedStatement.setString(i + 1, values[i]); + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index dd23de6a..cf3c4859 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -25,9 +25,9 @@ import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.databases.MySQLDatabase; +import com.sk89q.worldguard.protection.databases.YAMLDatabase; import com.sk89q.worldguard.protection.databases.ProtectionDatabase; import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.YAMLDatabase; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.PRTreeRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java index 9e9fd196..75a72be7 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java @@ -19,18 +19,7 @@ package com.sk89q.worldguard.protection.databases; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import au.com.bytecode.opencsv.CSVReader; - import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.Vector; import com.sk89q.worldguard.domains.DefaultDomain; @@ -42,11 +31,22 @@ import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; import com.sk89q.worldguard.util.ArrayReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * Represents a protected area database that uses CSV files. * - * @author sk89q + * @deprecated no longer maintained - use {@link YAMLDatabase} */ +@Deprecated public class CSVDatabase extends AbstractProtectionDatabase { private static final Map legacyFlagCodes = new HashMap(); diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/InvalidTableFormatException.java b/src/main/java/com/sk89q/worldguard/protection/databases/InvalidTableFormatException.java deleted file mode 100644 index 7b6e53d2..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/InvalidTableFormatException.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases; - -import com.sk89q.worldguard.util.FatalConfigurationLoadingException; - -public class InvalidTableFormatException extends FatalConfigurationLoadingException { - private static final long serialVersionUID = 1L; - - protected String updateFile; - - public InvalidTableFormatException(String updateFile) { - super(); - - this.updateFile = updateFile; - } - - public String toString() { - return "You need to update your database to the latest version.\n" + - "\t\tPlease see " + this.updateFile; - } -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java index e237a22e..aff2c284 100755 --- a/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java @@ -19,14 +19,26 @@ package com.sk89q.worldguard.protection.databases; - +import com.google.common.util.concurrent.ListeningExecutorService; import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.internal.protection.database.mysql.MySQLDatabaseImpl; +import com.sk89q.worldguard.protection.databases.mysql.MySQLDatabaseImpl; import java.util.logging.Logger; +/** + * A store that persists regions in a MySQL database. + */ public class MySQLDatabase extends MySQLDatabaseImpl { + /** + * Create a new instance. + * + * @param executor the executor to perform loads and saves in + * @param config the configuration + * @param worldName the world name + * @param logger a logger + * @throws ProtectionDatabaseException thrown on error + */ public MySQLDatabase(ConfigurationManager config, String worldName, Logger logger) throws ProtectionDatabaseException { super(config, worldName, logger); } diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java index 5339d805..88ade282 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java @@ -42,7 +42,7 @@ public interface ProtectionDatabase { /** * Load the list of regions into a region manager. * - *

This call will block and it must be run from the main server thread.

+ *

This call will block.

* * @param manager The manager to load regions into * @throws ProtectionDatabaseException when an error occurs @@ -57,8 +57,6 @@ public interface ProtectionDatabase { *

{@code async} is merely a suggestion and it may be ignored by * implementations if it is not supported.

* - *

This method must be run from the main server thread.

- * * @param manager The manager to load regions into * @param async true to attempt to save the data asynchronously if it is supported */ @@ -74,7 +72,7 @@ public interface ProtectionDatabase { /** * Save the list of regions from a region manager. * - *

This call will block and it must be run from the main server thread.

+ *

This call will block.

* * @param manager The manager to load regions into * @throws ProtectionDatabaseException when an error occurs @@ -89,11 +87,9 @@ public interface ProtectionDatabase { *

{@code async} is merely a suggestion and it may be ignored by * implementations if it is not supported.

* - *

This method must be run from the main server thread.

- * * @param manager The manager to load regions into * @param async true to attempt to save the data asynchronously if it is supported - * @throws ProtectionDatabaseException when an error occurs + * @throws RejectedExecutionException on rejection */ public ListenableFuture save(RegionManager manager, boolean async) throws RejectedExecutionException; diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java b/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java index 9d587e9b..158d6567 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java @@ -20,18 +20,18 @@ package com.sk89q.worldguard.protection.databases; import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.databases.util.DomainInputResolver; -import java.sql.PreparedStatement; -import java.sql.SQLException; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Various utility functions for parsing region databases. - * - * @author sk89q + * + * @deprecated use {@link DomainInputResolver} */ -public class RegionDBUtil { +@Deprecated +public final class RegionDBUtil { private static Pattern groupPattern = Pattern.compile("(?i)^[G]:(.+)$"); @@ -45,9 +45,10 @@ private RegionDBUtil() { * @param domain The domain to add to * @param split The {@link String[]} containing names to add to {@code domain} * @param startIndex The beginning index in the array + * @deprecated use {@link DomainInputResolver} */ - public static void addToDomain(DefaultDomain domain, String[] split, - int startIndex) { + @Deprecated + public static void addToDomain(DefaultDomain domain, String[] split, int startIndex) { for (int i = startIndex; i < split.length; i++) { String s = split[i]; Matcher m = groupPattern.matcher(s); @@ -65,9 +66,10 @@ public static void addToDomain(DefaultDomain domain, String[] split, * @param domain The domain to remove from * @param split The {@link String[]} containing names to remove from {@code domain} * @param startIndex The beginning index in the array + * @deprecated use {@link DomainInputResolver} */ - public static void removeFromDomain(DefaultDomain domain, String[] split, - int startIndex) { + @Deprecated + public static void removeFromDomain(DefaultDomain domain, String[] split, int startIndex) { for (int i = startIndex; i < split.length; i++) { String s = split[i]; Matcher m = groupPattern.matcher(s); @@ -85,7 +87,9 @@ public static void removeFromDomain(DefaultDomain domain, String[] split, * @param split The array of names to add * @param startIndex The beginning index in the array * @return The resulting DefaultDomain + * @deprecated use {@link DomainInputResolver} */ + @Deprecated public static DefaultDomain parseDomainString(String[] split, int startIndex) { DefaultDomain domain = new DefaultDomain(); @@ -101,34 +105,5 @@ public static DefaultDomain parseDomainString(String[] split, int startIndex) { return domain; } - - /** - * Creates a comma separated list of PreparedStatement place holders - * - * @param length The number of wildcards to create - * @return A string with {@code length} wildcards for usage in a PreparedStatement - */ - public static String preparePlaceHolders(int length) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < length;) { - builder.append("?"); - if (++i < length) { - builder.append(","); - } - } - return builder.toString(); - } - /** - * Adds all of the parsed values to the PreparedStatement - * - * @param preparedStatement The preparedStanement to add to - * @param values The values to set - * @throws SQLException see {@link PreparedStatement#setString(int, String)} - */ - public static void setValues(PreparedStatement preparedStatement, String... values) throws SQLException { - for (int i = 0; i < values.length; i++) { - preparedStatement.setString(i + 1, values[i]); - } - } } diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java index db944ffd..52bc7ffb 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java @@ -52,6 +52,9 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * A store that persists regions in a YAML-encoded file. + */ public class YAMLDatabase extends AbstractAsynchronousDatabase { /** @@ -62,7 +65,15 @@ public class YAMLDatabase extends AbstractAsynchronousDatabase { private Map regions; private final File file; private final Logger logger; - + + /** + * Create a new instance. + * + * @param file the file + * @param logger a logger + * @throws ProtectionDatabaseException + * @throws FileNotFoundException + */ public YAMLDatabase(File file, Logger logger) throws ProtectionDatabaseException, FileNotFoundException { this.logger = logger; this.file = file; @@ -89,9 +100,9 @@ public void performLoad() throws ProtectionDatabaseException { } catch (IOException e) { throw new ProtectionDatabaseException(e); } - + Map regionData = config.getNodes("regions"); - + // No regions are even configured if (regionData == null) { this.regions = new HashMap(); @@ -108,14 +119,14 @@ public void performLoad() throws ProtectionDatabaseException { Map regions = new HashMap(); Map parentSets = new LinkedHashMap(); - + for (Map.Entry entry : regionData.entrySet()) { String id = entry.getKey().toLowerCase().replace(".", ""); YAMLNode node = entry.getValue(); String type = node.getString("type"); ProtectedRegion region; - + try { if (type == null) { logger.warning("Undefined region type for region '" + id + "'!\n" + @@ -139,14 +150,14 @@ public void performLoad() throws ProtectionDatabaseException { "Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + "\n"); continue; } - + Integer priority = checkNonNull(node.getInt("priority")); region.setPriority(priority); setFlags(region, node.getNode("flags")); region.setOwners(parseDomain(node.getNode("owners"))); region.setMembers(parseDomain(node.getNode("members"))); regions.put(id, region); - + String parentId = node.getString("parent"); if (parentId != null) { parentSets.put(region, parentId); @@ -154,11 +165,11 @@ public void performLoad() throws ProtectionDatabaseException { } catch (NullPointerException e) { logger.log(Level.WARNING, "Unexpected NullPointerException encountered during parsing for the region '" + id + "'!\n" + - "Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + - "\n\nNote: This region will disappear as a result!", e); + "Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + + "\n\nNote: This region will disappear as a result!", e); } } - + // Relink parents for (Map.Entry entry : parentSets.entrySet()) { ProtectedRegion parent = regions.get(entry.getValue()); @@ -172,23 +183,23 @@ public void performLoad() throws ProtectionDatabaseException { logger.warning("Unknown region parent: " + entry.getValue()); } } - + this.regions = regions; } - + private V checkNonNull(V val) throws NullPointerException { if (val == null) { throw new NullPointerException(); } - + return val; } - + private void setFlags(ProtectedRegion region, YAMLNode flagsData) { if (flagsData == null) { return; } - + // @TODO: Make this better for (Flag flag : DefaultFlag.getFlags()) { if (flag == null) { @@ -201,16 +212,16 @@ private void setFlags(ProtectedRegion region, YAMLNode flagsData) { if (o != null) { setFlag(region, flag, o); } - + if (flag.getRegionGroupFlag() != null) { - Object o2 = flagsData.getProperty(flag.getRegionGroupFlag().getName()); + Object o2 = flagsData.getProperty(flag.getRegionGroupFlag().getName()); if (o2 != null) { setFlag(region, flag.getRegionGroupFlag(), o2); } } } } - + private void setFlag(ProtectedRegion region, Flag flag, Object rawValue) { T val = flag.unmarshal(rawValue); if (val == null) { @@ -220,13 +231,13 @@ private void setFlag(ProtectedRegion region, Flag flag, Object rawValue) } region.setFlag(flag, val); } - + @SuppressWarnings("deprecation") private DefaultDomain parseDomain(YAMLNode node) { if (node == null) { return new DefaultDomain(); } - + DefaultDomain domain = new DefaultDomain(); for (String name : node.getStringList("players", null)) { @@ -240,11 +251,11 @@ private DefaultDomain parseDomain(YAMLNode node) { logger.log(Level.WARNING, "Failed to parse UUID '" + stringId +"'", e); } } - + for (String name : node.getStringList("groups", null)) { domain.addGroup(name); } - + return domain; } @@ -307,18 +318,18 @@ protected void performSave() { "#"); config.save(); } - + private Map getFlagData(ProtectedRegion region) { Map flagData = new HashMap(); - + for (Map.Entry, Object> entry : region.getFlags().entrySet()) { Flag flag = entry.getKey(); addMarshalledFlag(flagData, flag, entry.getValue()); } - + return flagData; } - + @SuppressWarnings("unchecked") private void addMarshalledFlag(Map flagData, Flag flag, Object val) { if (val == null) { @@ -327,7 +338,7 @@ private void addMarshalledFlag(Map flagData, Flag flag, O flagData.put(flag.getName(), flag.marshal((V) val)); } - + @SuppressWarnings("deprecation") private Map getDomainData(DefaultDomain domain) { Map domainData = new HashMap(); @@ -335,21 +346,21 @@ private Map getDomainData(DefaultDomain domain) { setDomainData(domainData, "players", domain.getPlayers()); setDomainData(domainData, "unique-ids", domain.getUniqueIds()); setDomainData(domainData, "groups", domain.getGroups()); - + return domainData; } - + private void setDomainData(Map domainData, String key, Set domain) { if (domain.isEmpty()) { return; } - + List list = new ArrayList(); - + for (Object str : domain) { list.add(String.valueOf(str)); } - + domainData.put(key, list); } @@ -384,5 +395,5 @@ private static String dumpAsYaml(Object object) { return ""; } } - + } diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/AbstractDatabaseMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/AbstractDatabaseMigrator.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java index 549fdaea..abfd57a6 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/AbstractDatabaseMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java @@ -1,62 +1,62 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -public abstract class AbstractDatabaseMigrator implements DatabaseMigrator { - - private static HashMap> migrators = - new HashMap>(); - - public static Map> getMigrators() { - if (!migrators.isEmpty()) return migrators; - - AbstractDatabaseMigrator.migrators.put(new MigratorKey("mysql", "yaml"), MySQLToYAMLMigrator.class); - AbstractDatabaseMigrator.migrators.put(new MigratorKey("yaml", "mysql"), YAMLToMySQLMigrator.class); - - return migrators; - } - - protected abstract Set getWorldsFromOld() throws MigrationException; - - protected abstract Map getRegionsForWorldFromOld(String world) throws MigrationException; - - protected abstract ProtectionDatabase getNewWorldStorage(String world) throws MigrationException; - - public void migrate() throws MigrationException { - for (String world : this.getWorldsFromOld()) { - ProtectionDatabase database = this.getNewWorldStorage(world); - database.setRegions(this.getRegionsForWorldFromOld(world)); - - try { - database.save(); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - } - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.sk89q.worldguard.protection.databases.ProtectionDatabase; +import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +public abstract class AbstractDatabaseMigrator implements DatabaseMigrator { + + private static HashMap> migrators = + new HashMap>(); + + public static Map> getMigrators() { + if (!migrators.isEmpty()) return migrators; + + AbstractDatabaseMigrator.migrators.put(new MigratorKey("mysql", "yaml"), MySQLToYAMLMigrator.class); + AbstractDatabaseMigrator.migrators.put(new MigratorKey("yaml", "mysql"), YAMLToMySQLMigrator.class); + + return migrators; + } + + protected abstract Set getWorldsFromOld() throws MigrationException; + + protected abstract Map getRegionsForWorldFromOld(String world) throws MigrationException; + + protected abstract ProtectionDatabase getNewWorldStorage(String world) throws MigrationException; + + public void migrate() throws MigrationException { + for (String world : this.getWorldsFromOld()) { + ProtectionDatabase database = this.getNewWorldStorage(world); + database.setRegions(this.getRegionsForWorldFromOld(world)); + + try { + database.save(); + } catch (ProtectionDatabaseException e) { + throw new MigrationException(e); + } + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/DatabaseMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/DatabaseMigrator.java similarity index 91% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/DatabaseMigrator.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/DatabaseMigrator.java index 838170d0..56623766 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/DatabaseMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/DatabaseMigrator.java @@ -1,31 +1,31 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -/** - * Represents a method of converting from one database to another. - * - * @author Nicholas Steicke - */ -public interface DatabaseMigrator { - - public void migrate() throws MigrationException; -} - +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +/** + * Represents a method of converting from one database to another. + * + * @author Nicholas Steicke + */ +public interface DatabaseMigrator { + + public void migrate() throws MigrationException; +} + diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigrationException.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigrationException.java similarity index 92% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigrationException.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigrationException.java index 9ce01b43..84aef164 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigrationException.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigrationException.java @@ -1,40 +1,40 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -public class MigrationException extends Exception { - private static final long serialVersionUID = 1L; - - public MigrationException() { - super(); - } - - public MigrationException(String message) { - super(message); - } - - public MigrationException(String message, Throwable cause) { - super(message, cause); - } - - public MigrationException(Throwable cause) { - super(cause); - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +public class MigrationException extends Exception { + private static final long serialVersionUID = 1L; + + public MigrationException() { + super(); + } + + public MigrationException(String message) { + super(message); + } + + public MigrationException(String message, Throwable cause) { + super(message, cause); + } + + public MigrationException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigratorKey.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigratorKey.java similarity index 92% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigratorKey.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigratorKey.java index 5fe0e8bf..da85cdc4 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MigratorKey.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigratorKey.java @@ -1,43 +1,43 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -public class MigratorKey { - public final String from; - public final String to; - - public MigratorKey(String from, String to) { - this.from = from; - this.to = to; - } - - public boolean equals(Object o) { - MigratorKey other = (MigratorKey) o; - - return other.from.equals(this.from) && other.to.equals(this.to); - } - - public int hashCode() { - int hash = 17; - hash = hash * 31 + this.from.hashCode(); - hash = hash * 31 + this.to.hashCode(); - return hash; - } +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +public class MigratorKey { + public final String from; + public final String to; + + public MigratorKey(String from, String to) { + this.from = from; + this.to = to; + } + + public boolean equals(Object o) { + MigratorKey other = (MigratorKey) o; + + return other.from.equals(this.from) && other.to.equals(this.to); + } + + public int hashCode() { + int hash = 17; + hash = hash * 31 + this.from.hashCode(); + hash = hash * 31 + this.to.hashCode(); + return hash; + } } \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MySQLToYAMLMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MySQLToYAMLMigrator.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/MySQLToYAMLMigrator.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/MySQLToYAMLMigrator.java index 370e9e32..bdfafa5a 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/MySQLToYAMLMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MySQLToYAMLMigrator.java @@ -1,97 +1,97 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.databases.MySQLDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.YAMLDatabase; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.io.File; -import java.io.FileNotFoundException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class MySQLToYAMLMigrator extends AbstractDatabaseMigrator { - - private WorldGuardPlugin plugin; - private Set worlds; - - public MySQLToYAMLMigrator(WorldGuardPlugin plugin) throws MigrationException { - this.plugin = plugin; - this.worlds = new HashSet(); - - ConfigurationManager config = plugin.getGlobalStateManager(); - - try { - Connection conn = DriverManager.getConnection(config.sqlDsn, config.sqlUsername, config.sqlPassword); - - ResultSet worlds = conn.prepareStatement("SELECT `name` FROM `world`;").executeQuery(); - - while(worlds.next()) { - this.worlds.add(worlds.getString(1)); - } - - conn.close(); - } catch (SQLException e) { - throw new MigrationException(e); - } - } - - @Override - protected Set getWorldsFromOld() { - return this.worlds; - } - - @Override - protected Map getRegionsForWorldFromOld(String world) throws MigrationException { - ProtectionDatabase oldDatabase; - try { - oldDatabase = new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger()); - oldDatabase.load(); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - - return oldDatabase.getRegions(); - } - - @Override - protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException { - try { - File file = new File(plugin.getDataFolder(), - "worlds" + File.separator + world + File.separator + "regions.yml"); - - return new YAMLDatabase(file, plugin.getLogger()); - } catch (FileNotFoundException e) { - throw new MigrationException(e); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.databases.MySQLDatabase; +import com.sk89q.worldguard.protection.databases.YAMLDatabase; +import com.sk89q.worldguard.protection.databases.ProtectionDatabase; +import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.io.File; +import java.io.FileNotFoundException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class MySQLToYAMLMigrator extends AbstractDatabaseMigrator { + + private WorldGuardPlugin plugin; + private Set worlds; + + public MySQLToYAMLMigrator(WorldGuardPlugin plugin) throws MigrationException { + this.plugin = plugin; + this.worlds = new HashSet(); + + ConfigurationManager config = plugin.getGlobalStateManager(); + + try { + Connection conn = DriverManager.getConnection(config.sqlDsn, config.sqlUsername, config.sqlPassword); + + ResultSet worlds = conn.prepareStatement("SELECT `name` FROM `world`;").executeQuery(); + + while(worlds.next()) { + this.worlds.add(worlds.getString(1)); + } + + conn.close(); + } catch (SQLException e) { + throw new MigrationException(e); + } + } + + @Override + protected Set getWorldsFromOld() { + return this.worlds; + } + + @Override + protected Map getRegionsForWorldFromOld(String world) throws MigrationException { + ProtectionDatabase oldDatabase; + try { + oldDatabase = new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger()); + oldDatabase.load(); + } catch (ProtectionDatabaseException e) { + throw new MigrationException(e); + } + + return oldDatabase.getRegions(); + } + + @Override + protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException { + try { + File file = new File(plugin.getDataFolder(), + "worlds" + File.separator + world + File.separator + "regions.yml"); + + return new YAMLDatabase(file, plugin.getLogger()); + } catch (FileNotFoundException e) { + throw new MigrationException(e); + } catch (ProtectionDatabaseException e) { + throw new MigrationException(e); + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/YAMLToMySQLMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/YAMLToMySQLMigrator.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrators/YAMLToMySQLMigrator.java rename to src/main/java/com/sk89q/worldguard/protection/databases/migrator/YAMLToMySQLMigrator.java index 8b6dc950..2a1d879f 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrators/YAMLToMySQLMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/YAMLToMySQLMigrator.java @@ -1,86 +1,86 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrators; - -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.databases.MySQLDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.YAMLDatabase; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.io.File; -import java.io.FileNotFoundException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class YAMLToMySQLMigrator extends AbstractDatabaseMigrator { - - private WorldGuardPlugin plugin; - private HashMap regionYamlFiles; - - public YAMLToMySQLMigrator(WorldGuardPlugin plugin) { - this.plugin = plugin; - - this.regionYamlFiles = new HashMap(); - - File files[] = new File(plugin.getDataFolder(), "worlds" + File.separator).listFiles(); - for (File item : files) { - if (item.isDirectory()) { - for (File subItem : item.listFiles()) { - if (subItem.getName().equals("regions.yml")) { - this.regionYamlFiles.put(item.getName(), subItem); - } - } - } - } - } - - @Override - protected Set getWorldsFromOld() { - return this.regionYamlFiles.keySet(); - } - - @Override - protected Map getRegionsForWorldFromOld(String world) throws MigrationException { - ProtectionDatabase oldDatabase; - try { - oldDatabase = new YAMLDatabase(this.regionYamlFiles.get(world), plugin.getLogger()); - oldDatabase.load(); - } catch (FileNotFoundException e) { - throw new MigrationException(e); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - - return oldDatabase.getRegions(); - } - - @Override - protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException { - try { - return new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger()); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - } - -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.databases.MySQLDatabase; +import com.sk89q.worldguard.protection.databases.YAMLDatabase; +import com.sk89q.worldguard.protection.databases.ProtectionDatabase; +import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class YAMLToMySQLMigrator extends AbstractDatabaseMigrator { + + private WorldGuardPlugin plugin; + private HashMap regionYamlFiles; + + public YAMLToMySQLMigrator(WorldGuardPlugin plugin) { + this.plugin = plugin; + + this.regionYamlFiles = new HashMap(); + + File files[] = new File(plugin.getDataFolder(), "worlds" + File.separator).listFiles(); + for (File item : files) { + if (item.isDirectory()) { + for (File subItem : item.listFiles()) { + if (subItem.getName().equals("regions.yml")) { + this.regionYamlFiles.put(item.getName(), subItem); + } + } + } + } + } + + @Override + protected Set getWorldsFromOld() { + return this.regionYamlFiles.keySet(); + } + + @Override + protected Map getRegionsForWorldFromOld(String world) throws MigrationException { + ProtectionDatabase oldDatabase; + try { + oldDatabase = new YAMLDatabase(this.regionYamlFiles.get(world), plugin.getLogger()); + oldDatabase.load(); + } catch (FileNotFoundException e) { + throw new MigrationException(e); + } catch (ProtectionDatabaseException e) { + throw new MigrationException(e); + } + + return oldDatabase.getRegions(); + } + + @Override + protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException { + try { + return new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger()); + } catch (ProtectionDatabaseException e) { + throw new MigrationException(e); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/AbstractJob.java similarity index 97% rename from src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java rename to src/main/java/com/sk89q/worldguard/protection/databases/mysql/AbstractJob.java index 44e6e3ec..944a3b7b 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/AbstractJob.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/AbstractJob.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.protection.database.mysql; +package com.sk89q.worldguard.protection.databases.mysql; import com.sk89q.worldguard.bukkit.ConfigurationManager; import org.yaml.snakeyaml.error.YAMLException; diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java similarity index 97% rename from src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java rename to src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java index a095db61..7c592a17 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/MySQLDatabaseImpl.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java @@ -17,8 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.protection.database.mysql; +package com.sk89q.worldguard.protection.databases.mysql; +import com.google.common.util.concurrent.ListeningExecutorService; import com.jolbox.bonecp.BoneCP; import com.jolbox.bonecp.BoneCPConfig; import com.sk89q.worldguard.bukkit.ConfigurationManager; @@ -270,7 +271,7 @@ protected void performLoad() throws ProtectionDatabaseException { Connection connection = null; try { connection = getConnection(); - setRegions(new RegionLoader(this, connection).call()); + setRegions(new RegionLoader(this, connection).load()); } catch (SQLException e) { throw new ProtectionDatabaseException("Failed to load regions database", e); } finally { @@ -288,7 +289,7 @@ protected void performSave() throws ProtectionDatabaseException { Connection connection = null; try { connection = getConnection(); - new RegionCommitter(this, connection, getRegions()).call(); + new RegionWriter(this, connection, getRegions()).save(); } catch (SQLException e) { throw new ProtectionDatabaseException("Failed to save regions database", e); } finally { diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionLoader.java similarity index 98% rename from src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java rename to src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionLoader.java index 648290e5..e4793837 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionLoader.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionLoader.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.protection.database.mysql; +package com.sk89q.worldguard.protection.databases.mysql; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector2D; @@ -40,9 +40,8 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.concurrent.Callable; -class RegionLoader extends AbstractJob implements Callable> { +class RegionLoader extends AbstractJob { /* ========= Everything below is a nightmare. ========= @@ -60,8 +59,7 @@ class RegionLoader extends AbstractJob implements Callable call() { + public Map load() { parentSets = new HashMap(); // We load the cuboid regions first, as this is likely to be the diff --git a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionWriter.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java rename to src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionWriter.java index ff3bbbd5..c3294638 100644 --- a/src/main/java/com/sk89q/worldguard/internal/protection/database/mysql/RegionCommitter.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionWriter.java @@ -17,15 +17,13 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.protection.database.mysql; +package com.sk89q.worldguard.protection.databases.mysql; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector2D; import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.internal.protection.database.mysql.UserToIdCache.NameToIdCache; -import com.sk89q.worldguard.internal.protection.database.mysql.UserToIdCache.UUIDToIdCache; +import com.sk89q.worldguard.internal.util.sql.StatementUtils; import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.RegionDBUtil; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; @@ -43,33 +41,33 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.concurrent.Callable; import java.util.logging.Level; import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldguard.protection.databases.mysql.UserRowCache.NameRowCache; +import static com.sk89q.worldguard.protection.databases.mysql.UserRowCache.UUIDRowCache; -class RegionCommitter extends AbstractJob implements Callable { +class RegionWriter extends AbstractJob { /* ========= Everything below is a nightmare. ========= */ private final Map regions; - private final NameToIdCache usernameCache; - private final UUIDToIdCache uuidCache; + private final NameRowCache usernameCache; + private final UUIDRowCache uuidCache; private final int worldId; - RegionCommitter(MySQLDatabaseImpl database, Connection conn, Map regions) { + RegionWriter(MySQLDatabaseImpl database, Connection conn, Map regions) { super(database, conn); checkNotNull(regions); this.regions = regions; this.worldId = database.getWorldId(); - usernameCache = new NameToIdCache(database, conn); - uuidCache = new UUIDToIdCache(database, conn); + usernameCache = new NameRowCache(database, conn); + uuidCache = new UUIDRowCache(database, conn); } - @Override - public Object call() throws SQLException, ProtectionDatabaseException { + public void save() throws SQLException, ProtectionDatabaseException { /* * As we don't get notified on the creation/removal of regions: @@ -183,8 +181,6 @@ public Object call() throws SQLException, ProtectionDatabaseException { closeQuietly(removeRegion); } } - - return null; } /* @@ -207,11 +203,11 @@ private Map getGroupIds(String... groupnames) { "`group`.`name` " + "FROM `" + config.sqlTablePrefix + "group` AS `group` " + "WHERE `name` IN (%s)", - RegionDBUtil.preparePlaceHolders(groupnames.length) + StatementUtils.preparePlaceHolders(groupnames.length) ) ); - RegionDBUtil.setValues(findGroupsStatement, groupnames); + StatementUtils.setValues(findGroupsStatement, groupnames); findGroupsResults = findGroupsStatement.executeQuery(); diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java new file mode 100644 index 00000000..0ae2ebd7 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java @@ -0,0 +1,184 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.mysql; + +import com.google.common.collect.Lists; +import com.sk89q.worldguard.internal.util.sql.StatementUtils; +import com.sk89q.worldguard.util.io.Closer; + +import javax.annotation.Nullable; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.google.common.base.Preconditions.checkNotNull; + +abstract class UserRowCache extends AbstractJob { + + private static final int MAX_NUMBER_PER_QUERY = 100; + private static final Object LOCK = new Object(); + + private final Map cache = new HashMap(); + private final String fieldName; + + protected UserRowCache(MySQLDatabaseImpl database, Connection conn, String fieldName) { + super(database, conn); + this.fieldName = fieldName; + } + + protected abstract String fromType(V o); + + protected abstract V toType(String o); + + protected abstract V toKey(V object); + + @Nullable + public Integer find(V object) { + return cache.get(object); + } + + public void fetch(Collection entries) throws SQLException { + synchronized (LOCK) { // Lock across all cache instances + checkNotNull(entries); + + // Get a list of missing entries + List fetchList = new ArrayList(); + for (V entry : entries) { + if (!cache.containsKey(toKey(entry))) { + fetchList.add(entry); + } + } + + if (fetchList.isEmpty()) { + return; // Nothing to do + } + + // Search for entries + for (List partition : Lists.partition(fetchList, MAX_NUMBER_PER_QUERY)) { + Closer closer = Closer.create(); + try { + PreparedStatement statement = closer.register(conn.prepareStatement( + String.format( + "SELECT `user`.`id`, `user`.`" + fieldName + "` " + + "FROM `" + config.sqlTablePrefix + "user` AS `user` " + + "WHERE `" + fieldName + "` IN (%s)", + StatementUtils.preparePlaceHolders(partition.size())))); + + String[] values = new String[partition.size()]; + int i = 0; + for (V entry : partition) { + values[i] = fromType(entry); + i++; + } + + StatementUtils.setValues(statement, values); + ResultSet results = closer.register(statement.executeQuery()); + while (results.next()) { + cache.put(toKey(toType(results.getString(fieldName))), results.getInt("id")); + } + } finally { + closer.closeQuietly(); + } + } + + List missing = new ArrayList(); + for (V entry : fetchList) { + if (!cache.containsKey(toKey(entry))) { + missing.add(entry); + } + } + + // Insert entries that are missing + if (!missing.isEmpty()) { + Closer closer = Closer.create(); + try { + PreparedStatement statement = closer.register(conn.prepareStatement( + "INSERT INTO `" + config.sqlTablePrefix + "user` (`id`, `" + fieldName + "`) VALUES (null, ?)", + Statement.RETURN_GENERATED_KEYS)); + + for (V entry : missing) { + statement.setString(1, fromType(entry)); + statement.execute(); + + ResultSet generatedKeys = statement.getGeneratedKeys(); + if (generatedKeys.first()) { + cache.put(toKey(entry), generatedKeys.getInt(1)); + } else { + logger.warning("Could not get the database ID for user " + entry); + } + } + } finally { + closer.closeQuietly(); + } + } + } + } + + static class NameRowCache extends UserRowCache { + protected NameRowCache(MySQLDatabaseImpl database, Connection conn) { + super(database, conn, "name"); + } + + @Override + protected String fromType(String o) { + return o; + } + + @Override + protected String toType(String o) { + return o; + } + + @Override + protected String toKey(String object) { + return object.toLowerCase(); + } + } + + static class UUIDRowCache extends UserRowCache { + protected UUIDRowCache(MySQLDatabaseImpl database, Connection conn) { + super(database, conn, "uuid"); + } + + @Override + protected String fromType(UUID o) { + return o.toString(); + } + + @Override + protected UUID toType(String o) { + return UUID.fromString(o); + } + + @Override + protected UUID toKey(UUID object) { + return object; + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java index 7cdc8926..aa929763 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java @@ -26,21 +26,24 @@ import com.sk89q.worldguard.protection.databases.ProtectionDatabase; import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** * A very simple implementation of the region manager that uses a flat list * and iterates through the list to identify applicable regions. This method * is not very efficient. - * - * @author sk89q */ public class FlatRegionManager extends RegionManager { /** * List of protected regions. */ - private Map regions; + private ConcurrentMap regions = new ConcurrentHashMap(); /** * Construct the manager. @@ -49,7 +52,6 @@ public class FlatRegionManager extends RegionManager { */ public FlatRegionManager(ProtectionDatabase regionLoader) { super(regionLoader); - regions = new TreeMap(); } @Override @@ -59,7 +61,7 @@ public Map getRegions() { @Override public void setRegions(Map regions) { - this.regions = new TreeMap(regions); + this.regions = new ConcurrentHashMap(regions); } @Override @@ -185,7 +187,7 @@ public boolean overlapsUnownedRegion(ProtectedRegion checkRegion, LocalPlayer pl intersectRegions = new ArrayList(); } - return intersectRegions.size() > 0; + return !intersectRegions.isEmpty(); } @Override diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java index a9bc74ab..487e5e29 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java @@ -30,7 +30,12 @@ import org.khelekore.prtree.PRTree; import org.khelekore.prtree.SimpleMBR; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; public class PRTreeRegionManager extends RegionManager { @@ -55,7 +60,7 @@ public Map getRegions() { @Override public void setRegions(Map regions) { - Map newRegions = new TreeMap(regions); + ConcurrentMap newRegions = new ConcurrentHashMap(regions); PRTree tree = new PRTree(converter, BRANCH_FACTOR); tree.load(newRegions.values()); this.data = new RegionsContainer(newRegions, tree); @@ -65,8 +70,9 @@ public void setRegions(Map regions) { public void addRegion(ProtectedRegion region) { RegionsContainer data = this.data; data.regions.put(region.getId().toLowerCase(), region); - data.tree = new PRTree(converter, BRANCH_FACTOR); - data.tree.load(data.regions.values()); + PRTree tree = new PRTree(converter, BRANCH_FACTOR); + tree.load(data.regions.values()); + this.data = new RegionsContainer(data.regions, tree); } @Override @@ -95,8 +101,9 @@ public void removeRegion(String id) { } } - data.tree = new PRTree(converter, BRANCH_FACTOR); - data.tree.load(data.regions.values()); + PRTree tree = new PRTree(converter, BRANCH_FACTOR); + tree.load(data.regions.values()); + this.data = new RegionsContainer(data.regions, tree); } @Override @@ -197,7 +204,7 @@ public boolean overlapsUnownedRegion(ProtectedRegion checkRegion, LocalPlayer pl intersectRegions = new ArrayList(); } - return intersectRegions.size() > 0; + return !intersectRegions.isEmpty(); } @Override @@ -219,15 +226,15 @@ public int getRegionCountOfPlayer(LocalPlayer player) { } private class RegionsContainer { - private final Map regions; - private PRTree tree; + private final ConcurrentMap regions; + private final PRTree tree; private RegionsContainer() { - regions = new TreeMap(); + regions = new ConcurrentHashMap(); tree = new PRTree(converter, BRANCH_FACTOR); } - private RegionsContainer(Map regions, PRTree tree) { + private RegionsContainer(ConcurrentMap regions, PRTree tree) { this.regions = regions; this.tree = tree; } diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java index 62a3ec23..51ad8698 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java @@ -60,6 +60,7 @@ public RegionManager(ProtectionDatabase loader) { public void load() throws ProtectionDatabaseException { loader.load(this); } + /** * Load the list of regions. If the regions do not load properly, then * the existing list should be used (as stored previously). diff --git a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java index ff69b01b..e3355391 100644 --- a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java +++ b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java @@ -35,8 +35,6 @@ /** * Represents a region of any shape and size that can be protected. - * - * @author sk89q */ public abstract class ProtectedRegion implements Comparable { @@ -45,34 +43,11 @@ public abstract class ProtectedRegion implements Comparable { private static final Pattern idPattern = Pattern.compile("^[A-Za-z0-9_,'\\-\\+/]{1,}$"); - /** - * Holds the region's ID. - */ private String id; - - /** - * Priority. - */ private int priority = 0; - - /** - * Holds the curParent. - */ private ProtectedRegion parent; - - /** - * List of owners. - */ private DefaultDomain owners = new DefaultDomain(); - - /** - * List of members. - */ private DefaultDomain members = new DefaultDomain(); - - /** - * List of flags. - */ private Map, Object> flags = new ConcurrentHashMap, Object>(); /** @@ -457,8 +432,8 @@ public boolean contains(int x, int y, int z) { /** * Check to see if any of the 2D points are inside this region. * - * @param pts - * @return + * @param pts a list positions + * @return true if contained */ public boolean containsAny(List pts) { for (BlockVector2D pt : pts) { @@ -477,6 +452,7 @@ public boolean containsAny(List pts) { * * @param other The region to compare to */ + @Override public int compareTo(ProtectedRegion other) { if (priority > other.priority) { return -1; @@ -598,6 +574,6 @@ public boolean equals(Object obj) { * situation. */ public static class CircularInheritanceException extends Exception { - private static final long serialVersionUID = 7479613488496776022L; } + } diff --git a/src/main/java/com/sk89q/worldguard/util/RegionUtil.java b/src/main/java/com/sk89q/worldguard/util/RegionUtil.java index 3dc0426e..332963bc 100644 --- a/src/main/java/com/sk89q/worldguard/util/RegionUtil.java +++ b/src/main/java/com/sk89q/worldguard/util/RegionUtil.java @@ -24,11 +24,9 @@ /** * Various utility functions for regions. - * - * @author sk89q */ @Deprecated -public class RegionUtil { +public final class RegionUtil { private RegionUtil() { } From d87a335e8594724f15cea9b235040acbb150fe41 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 21:00:02 -0700 Subject: [PATCH 014/133] Disable Flyway validation on migration. --- .../protection/databases/mysql/MySQLDatabaseImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java index 7c592a17..3000f2af 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java @@ -19,7 +19,6 @@ package com.sk89q.worldguard.protection.databases.mysql; -import com.google.common.util.concurrent.ListeningExecutorService; import com.jolbox.bonecp.BoneCP; import com.jolbox.bonecp.BoneCPConfig; import com.sk89q.worldguard.bukkit.ConfigurationManager; @@ -178,6 +177,7 @@ private void migrate() throws SQLException, ProtectionDatabaseException { flyway.setDataSource(boneConfig.getJdbcUrl(), boneConfig.getUser(), boneConfig.getPassword()); flyway.setTable(config.sqlTablePrefix + "migrations"); flyway.setPlaceholders(placeHolders); + flyway.setValidateOnMigrate(false); flyway.migrate(); } From fafd3aba6afa2b262c5cd760c8d491e0d7405dac Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 21:03:12 -0700 Subject: [PATCH 015/133] Implement UUID migration. --- .../bukkit/ConfigurationManager.java | 15 +- .../bukkit/RegionPermissionModel.java | 6 +- .../worldguard/bukkit/WorldGuardPlugin.java | 78 ++++++-- .../bukkit/commands/RegionCommands.java | 60 ++++++ .../worldguard/domains/DefaultDomain.java | 42 ++++- .../protection/GlobalRegionManager.java | 20 +- .../migrator/AbstractDatabaseMigrator.java | 8 +- .../databases/migrator/UUIDMigrator.java | 176 ++++++++++++++++++ 8 files changed, 377 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/migrator/UUIDMigrator.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java b/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java index eadde464..29e4dfcc 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java @@ -102,6 +102,8 @@ public class ConfigurationManager { public boolean usePlayerTeleports; public boolean deopOnJoin; public boolean blockInGameOp; + public boolean migrateRegionsToUuid; + public boolean keepUnresolvedNames; public Map hostKeys = new HashMap(); /** @@ -142,6 +144,8 @@ public void load() { config.removeProperty("suppress-tick-sync-warnings"); useRegionsScheduler = config.getBoolean("regions.use-scheduler", true); + migrateRegionsToUuid = config.getBoolean("regions.uuid-migration.perform-on-next-start", true); + keepUnresolvedNames = config.getBoolean("regions.uuid-migration.keep-names-that-lack-uuids", true); useRegionsCreatureSpawnEvent = config.getBoolean("regions.use-creature-spawn-event", true); autoGodMode = config.getBoolean("auto-invincible", config.getBoolean("auto-invincible-permission", false)); config.removeProperty("auto-invincible-permission"); @@ -177,10 +181,6 @@ public void load() { } config.setHeader(CONFIG_HEADER); - - if (!config.save()) { - plugin.getLogger().severe("Error saving configuration!"); - } } /** @@ -190,6 +190,13 @@ public void unload() { worlds.clear(); } + public void disableUuidMigration() { + config.setProperty("regions.uuid-migration.perform-on-next-start", false); + if (!config.save()) { + plugin.getLogger().severe("Error saving configuration!"); + } + } + /** * Get the configuration for a world. * diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionPermissionModel.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionPermissionModel.java index 44b07c0d..3e0ba0fe 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionPermissionModel.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionPermissionModel.java @@ -42,10 +42,14 @@ public boolean mayForceLoadRegions() { public boolean mayForceSaveRegions() { return hasPluginPermission("region.save"); } - + public boolean mayMigrateRegionStore() { return hasPluginPermission("region.migratedb"); } + + public boolean mayMigrateRegionNames() { + return hasPluginPermission("region.migrateuuid"); + } public boolean mayDefine() { return hasPluginPermission("region.define"); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index a7213061..6889fd5d 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -52,6 +52,9 @@ import com.sk89q.worldguard.internal.listener.ChestProtectionListener; import com.sk89q.worldguard.internal.listener.RegionProtectionListener; import com.sk89q.worldguard.protection.GlobalRegionManager; +import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.databases.migrator.MigrationException; +import com.sk89q.worldguard.protection.databases.migrator.UUIDMigrator; import com.sk89q.worldguard.protection.databases.util.UnresolvedNamesException; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; @@ -154,6 +157,21 @@ public void run() { // Need to create the plugins/WorldGuard folder getDataFolder().mkdirs(); + File cacheDir = new File(getDataFolder(), "cache"); + cacheDir.mkdirs(); + try { + profileCache = new SQLiteCache(new File(cacheDir, "profiles.sqlite")); + } catch (IOException e) { + getLogger().log(Level.WARNING, "Failed to initialize SQLite profile cache"); + profileCache = new HashMapCache(); + } + + profileService = new CacheForwardingService( + new CombinedProfileService( + BukkitPlayerService.getInstance(), + HttpRepositoryService.forMinecraft()), + profileCache); + PermissionsResolverManager.initialize(this); // This must be done before configuration is loaded @@ -162,10 +180,24 @@ public void run() { try { // Load the configuration configuration.load(); + + getLogger().info("Loading region data..."); globalRegionManager.preload(); + + migrateRegionUniqueIds(); // Migrate to UUIDs } catch (FatalConfigurationLoadingException e) { - e.printStackTrace(); + getLogger().log(Level.WARNING, "Encountered fatal error while loading configuration", e); getServer().shutdown(); + getLogger().log(Level.WARNING, "\n" + + "******************************************************\n" + + "* Failed to load WorldGuard configuration!\n" + + "* \n" + + "* Shutting down the server just in case...\n" + + "* \n" + + "* The error should be printed above this message. If you can't\n" + + "* figure out the problem, ask us on our forums:\n" + + "* http://forum.enginehub.org\n" + + "******************************************************\n"); } // Migrate regions after the regions were loaded because @@ -207,21 +239,6 @@ public void run() { } worldListener.registerEvents(); - File cacheDir = new File(getDataFolder(), "cache"); - cacheDir.mkdirs(); - try { - profileCache = new SQLiteCache(new File(cacheDir, "profiles.sqlite")); - } catch (IOException e) { - getLogger().log(Level.WARNING, "Failed to initialize SQLite profile cache"); - profileCache = new HashMapCache(); - } - - profileService = new CacheForwardingService( - new CombinedProfileService( - BukkitPlayerService.getInstance(), - HttpRepositoryService.forMinecraft()), - profileCache); - if (!configuration.hasCommandBookGodMode()) { // Check god mode for existing players, if any for (Player player : getServer().getOnlinePlayers()) { @@ -260,6 +277,35 @@ public void onDisable() { this.getServer().getScheduler().cancelTasks(this); } + private void migrateRegionUniqueIds() { + try { + if (configuration.migrateRegionsToUuid) { + UUIDMigrator migrator = new UUIDMigrator(profileService, getLogger()); + migrator.readConfiguration(configuration); + List managers = globalRegionManager.getLoaded(); + + // Try migration + if (migrator.migrate(managers)) { + getLogger().info("Now saving regions... this may take a while."); + + for (RegionManager manager : managers) { + manager.save(); + } + + getLogger().info( + "Regions saved after UUID migration! This won't happen again unless " + + "you change the relevant configuration option in WorldGuard's config."); + + configuration.disableUuidMigration(); + } + } + } catch (MigrationException e) { + getLogger().log(Level.WARNING, "Failed to migrate regions to use UUIDs instead of player names", e); + } catch (ProtectionDatabaseException e) { + getLogger().log(Level.WARNING, "Failed to save regions after UUID conversion", e); + } + } + @Override public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { try { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java index 4bf89246..84f8f5bf 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java @@ -34,14 +34,17 @@ import com.sk89q.worldedit.bukkit.selections.Polygonal2DSelection; import com.sk89q.worldedit.bukkit.selections.Selection; import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.bukkit.LoggerToChatHandler; import com.sk89q.worldguard.bukkit.RegionPermissionModel; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; import com.sk89q.worldguard.protection.databases.RegionDBUtil; import com.sk89q.worldguard.protection.databases.migrator.AbstractDatabaseMigrator; import com.sk89q.worldguard.protection.databases.migrator.MigrationException; import com.sk89q.worldguard.protection.databases.migrator.MigratorKey; +import com.sk89q.worldguard.protection.databases.migrator.UUIDMigrator; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; @@ -65,6 +68,8 @@ import java.util.Date; import java.util.List; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Implements the /region commands for WorldGuard. @@ -1199,6 +1204,61 @@ public void migrateDB(CommandContext args, CommandSender sender) throws CommandE "If you wish to use the destination format as your new backend, please update your config and reload WorldGuard."); } + /** + * Migrate the region databases to use UUIDs rather than name. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"migrateuuid"}, + desc = "Migrate loaded databases to use UUIDs", max = 0) + public void migrateUuid(CommandContext args, CommandSender sender) throws CommandException { + // Check permissions + if (!getPermissionModel(sender).mayMigrateRegionNames()) { + throw new CommandPermissionsException(); + } + + LoggerToChatHandler handler = null; + Logger minecraftLogger = null; + + if (sender instanceof Player) { + handler = new LoggerToChatHandler(sender); + handler.setLevel(Level.ALL); + minecraftLogger = Logger.getLogger("Minecraft"); + minecraftLogger.addHandler(handler); + } + + try { + UUIDMigrator migrator = new UUIDMigrator(plugin.getProfileService(), plugin.getLogger()); + migrator.readConfiguration(plugin.getGlobalStateManager()); + List managers = plugin.getGlobalRegionManager().getLoaded(); + + // Try migration + if (migrator.migrate(managers)) { + sender.sendMessage(ChatColor.YELLOW + "Now saving regions... this may take a while."); + + for (RegionManager manager : managers) { + manager.save(); + } + + sender.sendMessage(ChatColor.YELLOW + "Migration complete!"); + } else { + sender.sendMessage(ChatColor.YELLOW + "There were no names to migrate."); + } + } catch (ProtectionDatabaseException e) { + plugin.getLogger().log(Level.WARNING, "Failed to save", e); + throw new CommandException("Error encountered while saving: " + e.getMessage()); + } catch (MigrationException e) { + plugin.getLogger().log(Level.WARNING, "Failed to migrate", e); + throw new CommandException("Error encountered while migrating: " + e.getMessage()); + } finally { + if (minecraftLogger != null) { + minecraftLogger.removeHandler(handler); + } + } + } + /** * Teleport to a region * diff --git a/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java b/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java index 05c77830..abe347b9 100644 --- a/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java +++ b/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java @@ -39,8 +39,46 @@ */ public class DefaultDomain implements Domain { - private final PlayerDomain playerDomain = new PlayerDomain(); - private final GroupDomain groupDomain = new GroupDomain(); + private PlayerDomain playerDomain = new PlayerDomain(); + private GroupDomain groupDomain = new GroupDomain(); + + /** + * Get the domain that holds the players. + * + * @return a domain + */ + public PlayerDomain getPlayerDomain() { + return playerDomain; + } + + /** + * Set a new player domain. + * + * @param playerDomain a domain + */ + public void setPlayerDomain(PlayerDomain playerDomain) { + checkNotNull(playerDomain); + this.playerDomain = playerDomain; + } + + /** + * Set the domain that holds the groups. + * + * @return a domain + */ + public GroupDomain getGroupDomain() { + return groupDomain; + } + + /** + * Set a new group domain. + * + * @param groupDomain a domain + */ + public void setGroupDomain(GroupDomain groupDomain) { + checkNotNull(groupDomain); + this.groupDomain = groupDomain; + } /** * Add the given player to the domain, identified by the player's name. diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index cf3c4859..5624e8ee 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -25,9 +25,9 @@ import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.databases.MySQLDatabase; -import com.sk89q.worldguard.protection.databases.YAMLDatabase; import com.sk89q.worldguard.protection.databases.ProtectionDatabase; import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.databases.YAMLDatabase; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.PRTreeRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; @@ -38,7 +38,10 @@ import java.io.File; import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; @@ -247,6 +250,21 @@ public RegionManager get(World world) { return manager; } + /** + * Get a list of loaded region managers. + * + * @return an unmodifiable list + */ + public List getLoaded() { + List list = new ArrayList(); + for (RegionManager manager : managers.values()) { + if (manager != null) { + list.add(manager); + } + } + return Collections.unmodifiableList(list); + } + /** * Returns whether the player can bypass. * diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java index abfd57a6..1d12bf29 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java @@ -19,14 +19,14 @@ package com.sk89q.worldguard.protection.databases.migrator; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - import com.sk89q.worldguard.protection.databases.ProtectionDatabase; import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + public abstract class AbstractDatabaseMigrator implements DatabaseMigrator { private static HashMap> migrators = diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/UUIDMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/UUIDMigrator.java new file mode 100644 index 00000000..1788c4cd --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/UUIDMigrator.java @@ -0,0 +1,176 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.databases.migrator; + +import com.google.common.base.Predicate; +import com.sk89q.squirrelid.Profile; +import com.sk89q.squirrelid.resolver.ProfileService; +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.domains.PlayerDomain; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class UUIDMigrator { + + private static final int LOG_DELAY = 5000; + + private final Timer timer = new Timer(); + private final ProfileService profileService; + private final Logger logger; + private final ConcurrentMap resolvedNames = new ConcurrentHashMap(); + private final Set unresolvedNames = new HashSet(); + private boolean keepUnresolvedNames = true; + + public UUIDMigrator(ProfileService profileService, Logger logger) { + checkNotNull(profileService); + checkNotNull(logger); + + this.profileService = profileService; + this.logger = logger; + } + + public boolean migrate(Collection managers) throws MigrationException { + Set names = new HashSet(); + + // Name scan pass + logger.log(Level.INFO, "UUID migrator: Gathering names to convert..."); + for (RegionManager manager : managers) { + scanForNames(manager, names); + } + + if (names.isEmpty()) { + logger.log(Level.INFO, "UUID migrator: No names to convert!"); + return false; + } + + TimerTask resolvedTask = new ResolvedNamesTimerTask(); + try { + timer.schedule(resolvedTask, LOG_DELAY, LOG_DELAY); + logger.log(Level.INFO, "UUID migrator: Resolving " + names.size() + " name(s) into UUIDs... this may take a while."); + profileService.findAllByName(names, new Predicate() { + @Override + public boolean apply(Profile profile) { + resolvedNames.put(profile.getName().toLowerCase(), profile.getUniqueId()); + return true; + } + }); + } catch (IOException e) { + throw new MigrationException("The name -> UUID service failed", e); + } catch (InterruptedException e) { + throw new MigrationException("The migration was interrupted"); + } finally { + resolvedTask.cancel(); + } + + logger.log(Level.INFO, "UUID migrator: UUIDs resolved... now migrating all regions to UUIDs where possible..."); + for (RegionManager manager : managers) { + convertToUniqueIds(manager); + } + + if (!unresolvedNames.isEmpty()) { + if (keepUnresolvedNames) { + logger.log(Level.WARNING, + "UUID migrator: Some member and owner names do not seem to exist or own Minecraft so they " + + "could not be converted into UUIDs. They have been left as names, but the conversion can " + + "be re-run with 'keep-names-that-lack-uuids' set to false in the configuration in " + + "order to remove these names. Leaving the names means that someone can register with one of " + + "these names in the future and become that player."); + } else { + logger.log(Level.WARNING, + "UUID migrator: Some member and owner names do not seem to exist or own Minecraft so they " + + "could not be converted into UUIDs. These names have been removed."); + } + } + + logger.log(Level.INFO, "UUID migrator: Migration finished!"); + + return true; + } + + private void scanForNames(RegionManager manager, Set target) { + for (ProtectedRegion region : manager.getRegions().values()) { + target.addAll(region.getOwners().getPlayers()); + target.addAll(region.getMembers().getPlayers()); + } + } + + private void convertToUniqueIds(RegionManager manager) { + for (ProtectedRegion region : manager.getRegions().values()) { + convertToUniqueIds(region.getOwners()); + convertToUniqueIds(region.getMembers()); + } + } + + private void convertToUniqueIds(DefaultDomain domain) { + PlayerDomain playerDomain = new PlayerDomain(); + for (UUID uuid : domain.getUniqueIds()) { + playerDomain.addPlayer(uuid); + } + + for (String name : domain.getPlayers()) { + UUID uuid = resolvedNames.get(name.toLowerCase()); + if (uuid != null) { + playerDomain.addPlayer(uuid); + } else { + if (keepUnresolvedNames) { + playerDomain.addPlayer(name); + } + unresolvedNames.add(name); + } + } + + domain.setPlayerDomain(playerDomain); + } + + public boolean isKeepUnresolvedNames() { + return keepUnresolvedNames; + } + + public void setKeepUnresolvedNames(boolean keepUnresolvedNames) { + this.keepUnresolvedNames = keepUnresolvedNames; + } + + public void readConfiguration(ConfigurationManager config) { + setKeepUnresolvedNames(config.keepUnresolvedNames); + } + + private class ResolvedNamesTimerTask extends TimerTask { + @Override + public void run() { + logger.info("UUID migrator: UUIDs have been found for " + resolvedNames.size() + " name(s)..."); + } + } + +} From 9a852f4cbe92b17015e577800b9f8d84167fbe7b Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 21:08:11 -0700 Subject: [PATCH 016/133] Fix shutdown guard not working. --- .../java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 6889fd5d..a563ae75 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -19,6 +19,7 @@ package com.sk89q.worldguard.bukkit; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.sk89q.bukkit.util.CommandsManagerRegistration; @@ -83,6 +84,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.jar.JarFile; @@ -252,7 +254,7 @@ public void run() { @Override public void onDisable() { - executorService.shutdownNow(); + executorService.shutdown(); try { getLogger().log(Level.INFO, "Shutting down executor and waiting for any pending tasks..."); @@ -267,9 +269,12 @@ public void onDisable() { getLogger().log(Level.INFO, builder.toString()); } + Futures.successfulAsList(tasks).get(); executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.DAYS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + getLogger().log(Level.WARNING, "Some tasks failed while waiting for remaining tasks to finish", e); } globalRegionManager.unload(); From 504fceceeb51fd4b3420197747e30720b7d6c938 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 21:19:05 -0700 Subject: [PATCH 017/133] Remove /wg cancelall because it doesn't entirely work yet. --- .../bukkit/commands/WorldGuardCommands.java | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java index 41690de1..22991901 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java @@ -62,7 +62,7 @@ public void reload(CommandContext args, CommandSender sender) throws CommandExce // TODO: This is subject to a race condition, but at least other commands are not being processed concurrently List> tasks = plugin.getSupervisor().getTasks(); if (!tasks.isEmpty()) { - throw new CommandException("There are currently pending tasks. Use /wg running and /wg cancelall to monitor and cancel these tasks first."); + throw new CommandException("There are currently pending tasks. Use /wg running to monitor these tasks first."); } LoggerToChatHandler handler = null; @@ -143,26 +143,6 @@ public void flushStates(CommandContext args, CommandSender sender) throws Comman } } - @Command(aliases = {"cancelall"}, flags = "f", desc = "Cancel all running tasks", max = 0) - @CommandPermissions("worldguard.cancel.all") - public void cancelAllTasks(CommandContext args, CommandSender sender) throws CommandException { - List> tasks = plugin.getSupervisor().getTasks(); - - boolean force = args.hasFlag('f'); - if (force && !plugin.hasPermission(sender, "worldguard.cancel.force")) { - throw new CommandException("You are not permitted to force cancel."); - } - - if (!tasks.isEmpty()) { - for (Task task : tasks) { - task.cancel(force); - sender.sendMessage(ChatColor.YELLOW + "Cancelled: " + ChatColor.WHITE + task.getName()); - } - } else { - sender.sendMessage(ChatColor.YELLOW + "There are currently no running tasks."); - } - } - @Command(aliases = {"running", "queue"}, desc = "List running tasks", max = 0) @CommandPermissions("worldguard.running") public void listRunningTasks(CommandContext args, CommandSender sender) throws CommandException { From 29f804b9f0617ac1b626754cfcb6c8a9c3c29977 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 21:22:32 -0700 Subject: [PATCH 018/133] Save YAML regions database to a temporary file before renaming. --- .../protection/databases/YAMLDatabase.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java index 52bc7ffb..a1e26b4a 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java @@ -87,13 +87,13 @@ public YAMLDatabase(File file, Logger logger) throws ProtectionDatabaseException } } - private YAMLProcessor createYamlProcessor() { + private YAMLProcessor createYamlProcessor(File file) { return new YAMLProcessor(file, false, YAMLFormat.COMPACT); } @Override public void performLoad() throws ProtectionDatabaseException { - YAMLProcessor config = createYamlProcessor(); + YAMLProcessor config = createYamlProcessor(file); try { config.load(); @@ -260,8 +260,9 @@ private DefaultDomain parseDomain(YAMLNode node) { } @Override - protected void performSave() { - YAMLProcessor config = createYamlProcessor(); + protected void performSave() throws ProtectionDatabaseException { + File tempFile = new File(file.getParentFile(), file.getName() + ".tmp"); + YAMLProcessor config = createYamlProcessor(tempFile); config.clear(); @@ -317,6 +318,11 @@ protected void performSave() { "# REMEMBER TO KEEP PERIODICAL BACKUPS.\r\n" + "#"); config.save(); + + file.delete(); + if (!tempFile.renameTo(file)) { + throw new ProtectionDatabaseException("Failed to rename temporary regions file to " + file.getAbsolutePath()); + } } private Map getFlagData(ProtectedRegion region) { From b5dfed9e013aa8172f667080cb6bb80d94028c85 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 2 Aug 2014 23:28:04 -0700 Subject: [PATCH 019/133] Update for dependency package rename. --- src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java | 2 +- .../protection/databases/AbstractAsynchronousDatabase.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index a563ae75..fca4e7dc 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -30,7 +30,7 @@ import com.sk89q.minecraft.util.commands.MissingNestedCommandException; import com.sk89q.minecraft.util.commands.SimpleInjector; import com.sk89q.minecraft.util.commands.WrappedCommandException; -import com.sk89q.odeum.concurrency.EvenMoreExecutors; +import com.sk89q.odeum.concurrent.EvenMoreExecutors; import com.sk89q.odeum.task.SimpleSupervisor; import com.sk89q.odeum.task.Supervisor; import com.sk89q.odeum.task.Task; diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java index 65fce0b7..72a1aa53 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java @@ -22,7 +22,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; -import com.sk89q.odeum.concurrency.EvenMoreExecutors; +import com.sk89q.odeum.concurrent.EvenMoreExecutors; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; From 7a011687817fc77c4a4b05eef3391c9b8110031d Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 8 Aug 2014 20:22:05 -0700 Subject: [PATCH 020/133] Rewrite region API. Migrations need to be re-implemented. This commit, however, adds: * Better support for background saves and loads. * Periodical writes of region changes rather than explicit save() calls. * Rewrite of the MySQL region code. * Support for partial saves when using MySQL. * Decoupling of region index implementations and RegionManager. What still needs to be done includes: * Background region modification operations (addmember, etc.). * Unit tests to verify correctness of the partial saving. * Migration support (to be re-added). * Better handling when regions are failing to save. * Testing of the /rg load and /rg save commands. * Verification of how server shutdown / world unload is handled. * Verification that thread-unsafe concurrent saves of data is not happening. --- .../bukkit/ConfigurationManager.java | 41 +- .../bukkit/LegacyWorldGuardMigration.java | 155 ---- .../sk89q/worldguard/bukkit/ReportWriter.java | 2 +- .../worldguard/bukkit/WorldGuardPlugin.java | 37 +- .../bukkit/commands/AsyncCommandHelper.java | 12 - .../bukkit/commands/RegionCommands.java | 75 +- .../bukkit/commands/RegionManagerLoad.java | 53 ++ .../bukkit/commands/RegionManagerSave.java | 53 ++ .../bukkit/commands/RegionMemberCommands.java | 24 +- .../commands/RegionPrintoutBuilder.java | 2 +- .../worldguard/domains/DefaultDomain.java | 20 +- .../sk89q/worldguard/domains/GroupDomain.java | 33 +- .../worldguard/domains/PlayerDomain.java | 44 +- .../protection/ApplicableRegionSet.java | 152 ++-- .../protection/GlobalRegionManager.java | 297 +------ .../protection/ManagerContainer.java | 174 ++++ .../UnsupportedIntersectionException.java | 8 +- .../AbstractAsynchronousDatabase.java | 226 ------ .../databases/AbstractProtectionDatabase.java | 60 -- .../protection/databases/CSVDatabase.java | 357 --------- .../databases/ProtectionDatabase.java | 110 --- .../protection/databases/RegionDBUtil.java | 2 +- .../migrator/AbstractDatabaseMigrator.java | 62 -- .../migrator/MySQLToYAMLMigrator.java | 97 --- .../migrator/YAMLToMySQLMigrator.java | 86 -- .../databases/mysql/AbstractJob.java | 78 -- .../databases/mysql/MySQLDatabaseImpl.java | 305 ------- .../databases/mysql/RegionLoader.java | 456 ----------- .../databases/mysql/RegionWriter.java | 608 -------------- .../worldguard/protection/flags/EnumFlag.java | 9 + .../worldguard/protection/flags/Flag.java | 11 +- .../protection/flags/RegionGroupFlag.java | 5 +- .../worldguard/protection/flags/SetFlag.java | 16 +- .../managers/FlatRegionManager.java | 210 ----- .../managers/PRTreeRegionManager.java | 243 ------ .../managers/RegionCollectionConsumer.java | 70 ++ .../protection/managers/RegionDifference.java | 83 ++ .../protection/managers/RegionManager.java | 405 +++++++--- .../protection/managers/RemovalStrategy.java | 40 + .../index/AbstractRegionIndex.java} | 10 +- .../index/ConcurrentRegionIndex.java} | 17 +- .../managers/index/HashMapIndex.java | 263 ++++++ .../managers/index/PriorityRTreeIndex.java | 102 +++ .../managers/index/RegionIndex.java | 138 ++++ .../storage/DifferenceSaveException.java | 41 + .../managers/storage/MemoryRegionStore.java | 52 ++ .../managers/storage/RegionStore.java | 69 ++ .../storage/RegionStoreException.java} | 20 +- .../managers/storage/RegionStoreUtils.java | 125 +++ .../storage/driver/DirectoryYamlDriver.java | 103 +++ .../managers/storage/driver/DriverType.java | 52 ++ .../storage/driver/RegionStoreDriver.java} | 35 +- .../managers/storage/driver/SQLDriver.java | 101 +++ .../storage/file/YamlFileStore.java} | 754 ++++++++---------- .../managers/storage/sql/DataLoader.java | 335 ++++++++ .../managers/storage/sql/DataUpdater.java | 168 ++++ .../storage/sql/DomainTableCache.java | 53 ++ .../managers/storage/sql/RegionInserter.java | 189 +++++ .../managers/storage/sql/RegionRemover.java | 88 ++ .../managers/storage/sql/RegionUpdater.java | 341 ++++++++ .../managers/storage/sql/SQLRegionStore.java | 375 +++++++++ .../storage/sql/StatementBatch.java} | 45 +- .../storage/sql/TableCache.java} | 110 ++- .../regions/GlobalProtectedRegion.java | 34 +- .../regions/ProtectedCuboidRegion.java | 90 +-- .../regions/ProtectedPolygonalRegion.java | 102 +-- .../protection/regions/ProtectedRegion.java | 300 ++++--- .../protection/regions/RegionType.java | 52 ++ .../util/DomainInputResolver.java | 2 +- .../util/UnresolvedNamesException.java | 2 +- .../migrator/MigrationException.java | 7 +- .../migrator/UUIDMigrator.java | 2 +- .../sk89q/worldguard/util/ChangeTracked.java | 42 + .../com/sk89q/worldguard/util/Normal.java | 119 +++ .../worldguard/util/sql/DataSourceConfig.java | 145 ++++ .../region/mysql/V2__Bug_fix_and_UUID.sql | 12 +- .../migrations/region/sqlite/V1__Initial.sql | 160 ++++ .../protection/FlatRegionOverlapTest.java | 29 - ...est.java => HashMapIndexPriorityTest.java} | 10 +- ...ava => HashMapIndexRegionOverlapTest.java} | 10 +- ...tryExitTest.java => HashMapIndexTest.java} | 9 +- .../protection/MockApplicableRegionSet.java | 2 +- ...yTest.java => PriorityRTreeIndexTest.java} | 10 +- .../PriorityRTreeRegionEntryExitTest.java | 33 + ...va => PriorityRTreeRegionOverlapTest.java} | 10 +- .../PriorityRTreeRegionPriorityTest.java | 33 + .../protection/RegionEntryExitTest.java | 1 + 87 files changed, 5098 insertions(+), 4425 deletions(-) delete mode 100644 src/main/java/com/sk89q/worldguard/bukkit/LegacyWorldGuardMigration.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerLoad.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerSave.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/AbstractProtectionDatabase.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/migrator/MySQLToYAMLMigrator.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/migrator/YAMLToMySQLMigrator.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/mysql/AbstractJob.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionLoader.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionWriter.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java delete mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/RegionCollectionConsumer.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/RegionDifference.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/RemovalStrategy.java rename src/main/java/com/sk89q/worldguard/protection/{databases/migrator/DatabaseMigrator.java => managers/index/AbstractRegionIndex.java} (76%) rename src/{test/java/com/sk89q/worldguard/protection/FlatRegionManagerTest.java => main/java/com/sk89q/worldguard/protection/managers/index/ConcurrentRegionIndex.java} (65%) create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/index/HashMapIndex.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/index/PriorityRTreeIndex.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/index/RegionIndex.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/DifferenceSaveException.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/MemoryRegionStore.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStore.java rename src/main/java/com/sk89q/worldguard/protection/{databases/ProtectionDatabaseException.java => managers/storage/RegionStoreException.java} (67%) create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStoreUtils.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DirectoryYamlDriver.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DriverType.java rename src/main/java/com/sk89q/worldguard/protection/{databases/MySQLDatabase.java => managers/storage/driver/RegionStoreDriver.java} (50%) mode change 100755 => 100644 create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/SQLDriver.java rename src/main/java/com/sk89q/worldguard/protection/{databases/YAMLDatabase.java => managers/storage/file/YamlFileStore.java} (56%) create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DataLoader.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DataUpdater.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DomainTableCache.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionInserter.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionRemover.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionUpdater.java create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/SQLRegionStore.java rename src/main/java/com/sk89q/worldguard/protection/{databases/migrator/MigratorKey.java => managers/storage/sql/StatementBatch.java} (52%) rename src/main/java/com/sk89q/worldguard/protection/{databases/mysql/UserRowCache.java => managers/storage/sql/TableCache.java} (61%) create mode 100644 src/main/java/com/sk89q/worldguard/protection/regions/RegionType.java rename src/main/java/com/sk89q/worldguard/protection/{databases => }/util/DomainInputResolver.java (98%) rename src/main/java/com/sk89q/worldguard/protection/{databases => }/util/UnresolvedNamesException.java (95%) rename src/main/java/com/sk89q/worldguard/protection/{databases => util}/migrator/MigrationException.java (91%) rename src/main/java/com/sk89q/worldguard/protection/{databases => util}/migrator/UUIDMigrator.java (99%) create mode 100644 src/main/java/com/sk89q/worldguard/util/ChangeTracked.java create mode 100644 src/main/java/com/sk89q/worldguard/util/Normal.java create mode 100644 src/main/java/com/sk89q/worldguard/util/sql/DataSourceConfig.java create mode 100644 src/main/resources/migrations/region/sqlite/V1__Initial.sql delete mode 100644 src/test/java/com/sk89q/worldguard/protection/FlatRegionOverlapTest.java rename src/test/java/com/sk89q/worldguard/protection/{PRTreeRegionPriorityTest.java => HashMapIndexPriorityTest.java} (76%) rename src/test/java/com/sk89q/worldguard/protection/{PRTreeRegionManagerTest.java => HashMapIndexRegionOverlapTest.java} (76%) rename src/test/java/com/sk89q/worldguard/protection/{PRTreeRegionEntryExitTest.java => HashMapIndexTest.java} (77%) rename src/test/java/com/sk89q/worldguard/protection/{FlatRegionPriorityTest.java => PriorityRTreeIndexTest.java} (75%) create mode 100644 src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionEntryExitTest.java rename src/test/java/com/sk89q/worldguard/protection/{PRTreeRegionOverlapTest.java => PriorityRTreeRegionOverlapTest.java} (75%) create mode 100644 src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionPriorityTest.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java b/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java index 29e4dfcc..43b34fda 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/ConfigurationManager.java @@ -66,30 +66,11 @@ public class ConfigurationManager { "# - Lines starting with # are comments and so they are ignored.\r\n" + "#\r\n"; - /** - * Reference to the plugin. - */ private WorldGuardPlugin plugin; - - /** - * Holds configurations for different worlds. - */ private ConcurrentMap worlds; - - /** - * The global configuration for use when loading worlds - */ private YAMLProcessor config; - - /** - * List of people with god mode. - */ @Deprecated private Set hasGodMode = new HashSet(); - - /** - * List of people who can breathe underwater. - */ private Set hasAmphibious = new HashSet(); private boolean hasCommandBookGodMode = false; @@ -125,6 +106,25 @@ public ConfigurationManager(WorldGuardPlugin plugin) { this.worlds = new ConcurrentHashMap(); } + /** + * Get the folder for storing data files and configuration. + * + * @return the data folder + */ + public File getDataFolder() { + return plugin.getDataFolder(); + } + + /** + * Get the folder for storing data files and configuration for each + * world. + * + * @return the data folder + */ + public File getWorldsDataFolder() { + return new File(getDataFolder(), "worlds"); + } + /** * Load the configuration. */ @@ -167,8 +167,7 @@ public void load() { } } - useSqlDatabase = config.getBoolean( - "regions.sql.use", false); + useSqlDatabase = config.getBoolean("regions.sql.use", false); sqlDsn = config.getString("regions.sql.dsn", "jdbc:mysql://localhost/worldguard"); sqlUsername = config.getString("regions.sql.username", "worldguard"); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/LegacyWorldGuardMigration.java b/src/main/java/com/sk89q/worldguard/bukkit/LegacyWorldGuardMigration.java deleted file mode 100644 index f540e0d1..00000000 --- a/src/main/java/com/sk89q/worldguard/bukkit/LegacyWorldGuardMigration.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import com.sk89q.worldguard.protection.databases.CSVDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.managers.RegionManager; -import org.bukkit.World; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Utility methods for porting from legacy versions. - */ -public final class LegacyWorldGuardMigration { - - private LegacyWorldGuardMigration() { - } - - /** - * Port over the blacklist. - * - * @param plugin The plugin instance - */ - public static void migrateBlacklist(WorldGuardPlugin plugin) { - World mainWorld = plugin.getServer().getWorlds().get(0); - String mainWorldName = mainWorld.getName(); - String newPath = "worlds/" + mainWorldName + "/blacklist.txt"; - - File oldFile = new File(plugin.getDataFolder(), "blacklist.txt"); - File newFile = new File(plugin.getDataFolder(), newPath); - - if (!newFile.exists() && oldFile.exists()) { - plugin.getLogger().warning("WorldGuard will now update your blacklist " - + "from an older version of WorldGuard."); - - // Need to make root directories - newFile.getParentFile().mkdirs(); - - if (copyFile(oldFile, newFile)) { - oldFile.renameTo(new File(plugin.getDataFolder(), - "blacklist.txt.old")); - } else { - plugin.getLogger().warning("blacklist.txt has been converted " + - "for the main world at " + newPath + ""); - plugin.getLogger().warning("Your other worlds currently have no " + - "blacklist defined!"); - } - - } - } - - /** - * Migrate region settings. - * - * @param plugin The plugin instance - */ - public static void migrateRegions(WorldGuardPlugin plugin) { - try { - File oldDatabase = new File(plugin.getDataFolder(), "regions.txt"); - if (!oldDatabase.exists()) return; - - plugin.getLogger().info("The regions database has changed in 5.x. " - + "Your old regions database will be converted to the new format " - + "and set as your primary world's database."); - - World w = plugin.getServer().getWorlds().get(0); - RegionManager mgr = plugin.getGlobalRegionManager().get(w); - - // First load up the old database using the CSV loader - CSVDatabase db = new CSVDatabase(oldDatabase, plugin.getLogger()); - db.load(); - - // Then save the new database - mgr.setRegions(db.getRegions()); - mgr.save(); - - oldDatabase.renameTo(new File(plugin.getDataFolder(), "regions.txt.old")); - - plugin.getLogger().info("Regions database converted!"); - } catch (ProtectionDatabaseException e) { - plugin.getLogger().warning("Failed to load regions: " - + e.getMessage()); - } - } - - /** - * Copies a file. - * - * @param from The source file - * @param to The destination file - * @return true if successful - */ - private static boolean copyFile(File from, File to) { - InputStream in = null; - OutputStream out = null; - - try { - in = new FileInputStream(from); - out = new FileOutputStream(to); - - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - - in.close(); - out.close(); - - return true; - } catch (FileNotFoundException ignore) { - } catch (IOException ignore) { - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ignore) { - } - } - - if (out != null) { - try { - out.close(); - } catch (IOException ignore) { - } - } - } - - return false; - } -} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/ReportWriter.java b/src/main/java/com/sk89q/worldguard/bukkit/ReportWriter.java index c25d234f..82fa737b 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/ReportWriter.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/ReportWriter.java @@ -300,7 +300,7 @@ private void appendWorldConfigurations(WorldGuardPlugin plugin, List worl regionsLog.put("Number of regions", worldRegions.getRegions().size()); LogListBlock globalRegionLog = regionsLog.putChild("Global region"); - ProtectedRegion globalRegion = worldRegions.getRegion("__global__"); + ProtectedRegion globalRegion = worldRegions.matchRegion("__global__"); if (globalRegion == null) { globalRegionLog.put("Status", "UNDEFINED"); } else { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index fca4e7dc..a6ad15d9 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -53,10 +53,9 @@ import com.sk89q.worldguard.internal.listener.ChestProtectionListener; import com.sk89q.worldguard.internal.listener.RegionProtectionListener; import com.sk89q.worldguard.protection.GlobalRegionManager; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.migrator.MigrationException; -import com.sk89q.worldguard.protection.databases.migrator.UUIDMigrator; -import com.sk89q.worldguard.protection.databases.util.UnresolvedNamesException; +import com.sk89q.worldguard.protection.util.migrator.MigrationException; +import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator; +import com.sk89q.worldguard.protection.util.UnresolvedNamesException; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; import org.bukkit.ChatColor; @@ -98,8 +97,8 @@ public class WorldGuardPlugin extends JavaPlugin { private static WorldGuardPlugin inst; private final CommandsManager commands; - private final GlobalRegionManager globalRegionManager; - private final ConfigurationManager configuration; + private GlobalRegionManager globalRegionManager; + private ConfigurationManager configuration; private FlagStateManager flagStateManager; private final Supervisor supervisor = new SimpleSupervisor(); private ListeningExecutorService executorService; @@ -111,9 +110,6 @@ public class WorldGuardPlugin extends JavaPlugin { * this merely instantiates the objects. */ public WorldGuardPlugin() { - configuration = new ConfigurationManager(this); - globalRegionManager = new GlobalRegionManager(this); - final WorldGuardPlugin plugin = inst = this; commands = new CommandsManager() { @Override @@ -137,6 +133,7 @@ public static WorldGuardPlugin inst() { @Override @SuppressWarnings("deprecation") public void onEnable() { + configuration = new ConfigurationManager(this); executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20)); // Set the proper command injector @@ -176,14 +173,12 @@ public void run() { PermissionsResolverManager.initialize(this); - // This must be done before configuration is loaded - LegacyWorldGuardMigration.migrateBlacklist(this); - try { // Load the configuration configuration.load(); getLogger().info("Loading region data..."); + globalRegionManager = new GlobalRegionManager(this); globalRegionManager.preload(); migrateRegionUniqueIds(); // Migrate to UUIDs @@ -202,10 +197,6 @@ public void run() { "******************************************************\n"); } - // Migrate regions after the regions were loaded because - // the migration code reuses the loaded region managers - LegacyWorldGuardMigration.migrateRegions(this); - flagStateManager = new FlagStateManager(this); if (configuration.useRegionsScheduler) { @@ -306,8 +297,8 @@ private void migrateRegionUniqueIds() { } } catch (MigrationException e) { getLogger().log(Level.WARNING, "Failed to migrate regions to use UUIDs instead of player names", e); - } catch (ProtectionDatabaseException e) { - getLogger().log(Level.WARNING, "Failed to save regions after UUID conversion", e); + } catch (IOException e) { + getLogger().log(Level.WARNING, "Failed to save region data", e); } } @@ -343,16 +334,14 @@ public String convertThrowable(@Nullable Throwable throwable) { } else if (throwable instanceof RejectedExecutionException) { return "There are currently too many tasks queued to add yours. Use /wg running to list queued and running tasks."; } else if (throwable instanceof CancellationException) { - return "Task was cancelled"; + return "WorldGuard: Task was cancelled"; } else if (throwable instanceof InterruptedException) { - return "Task was interrupted"; + return "WorldGuard: Task was interrupted"; } else if (throwable instanceof UnresolvedNamesException) { return throwable.getMessage(); - } else if (throwable instanceof Exception) { - getLogger().log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable); - return "Unexpected error occurred: " + ((Exception) throwable).getMessage(); } else { - return "Unknown error"; + getLogger().log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable); + return "WorldGuard: An unexpected error occurred! Please see the server console."; } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java index 184e9306..a3ebaabc 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java @@ -23,7 +23,6 @@ import com.google.common.util.concurrent.ListenableFuture; import com.sk89q.odeum.task.FutureForwardingTask; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.World; import org.bukkit.command.CommandSender; @@ -129,17 +128,6 @@ public AsyncCommandHelper forRegionDataSave(World world, boolean silent) { return this; } - public AsyncCommandHelper thenSaveRegionData(RegionManager manager, World world) { - checkNotNull(manager); - checkNotNull(world); - - ListenableFuture future = manager.save(true); - - AsyncCommandHelper.wrap(future, plugin, sender).forRegionDataSave(world, true); - - return this; - } - public static AsyncCommandHelper wrap(ListenableFuture future, WorldGuardPlugin plugin, CommandSender sender) { return new AsyncCommandHelper(future, plugin, sender); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java index 84f8f5bf..fff71fe0 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java @@ -39,18 +39,15 @@ import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; import com.sk89q.worldguard.protection.databases.RegionDBUtil; -import com.sk89q.worldguard.protection.databases.migrator.AbstractDatabaseMigrator; -import com.sk89q.worldguard.protection.databases.migrator.MigrationException; -import com.sk89q.worldguard.protection.databases.migrator.MigratorKey; -import com.sk89q.worldguard.protection.databases.migrator.UUIDMigrator; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; import com.sk89q.worldguard.protection.flags.RegionGroup; import com.sk89q.worldguard.protection.flags.RegionGroupFlag; import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.util.migrator.MigrationException; +import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator; import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; @@ -62,10 +59,9 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import java.lang.reflect.InvocationTargetException; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -78,9 +74,6 @@ public final class RegionCommands { private final WorldGuardPlugin plugin; - private MigratorKey migrateDBRequest; - private Date migrateDBRequestDate; - public RegionCommands(WorldGuardPlugin plugin) { this.plugin = plugin; } @@ -95,17 +88,6 @@ private static RegionPermissionModel getPermissionModel(CommandSender sender) { return new RegionPermissionModel(WorldGuardPlugin.inst(), sender); } - /** - * Save the regions asynchronously and alert the sender if any errors - * occur during save. - * - * @param manager the manager to save - * @param sender the sender - */ - private void saveRegionsSilently(RegionManager manager, World world, CommandSender sender) { - AsyncCommandHelper.wrap(Futures.immediateFuture(null), plugin, sender).thenSaveRegionData(manager, world); - } - /** * Gets the world from the given flag, or falling back to the the current player * if the sender is a player, otherwise reporting an error. @@ -167,7 +149,7 @@ private static ProtectedRegion findExistingRegion(RegionManager regionManager, S // Validate the id validateRegionId(id, allowGlobal); - ProtectedRegion region = regionManager.getRegionExact(id); + ProtectedRegion region = regionManager.getRegion(id); // No region found! if (region == null) { @@ -414,9 +396,6 @@ public void define(CommandContext args, CommandSender sender) throws CommandExce // Add region regionManager.addRegion(region); - - // Save regions - saveRegionsSilently(regionManager, player.getWorld(), sender); } /** @@ -470,9 +449,6 @@ public void redefine(CommandContext args, CommandSender sender) throws CommandEx sender.sendMessage(ChatColor.YELLOW + "Region '" + id + "' updated with new area."); regionManager.addRegion(region); // Replace region - - // Save regions - saveRegionsSilently(regionManager, world, sender); } /** @@ -529,7 +505,7 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep } } - ProtectedRegion existing = regionManager.getRegionExact(id); + ProtectedRegion existing = regionManager.getRegion(id); // Check for an existing region if (existing != null) { @@ -570,9 +546,6 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep // Replace region regionManager.addRegion(region); - - // Save regions - saveRegionsSilently(regionManager, player.getWorld(), sender); } /** @@ -890,9 +863,6 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept printout.appendFlagsList(false); printout.append(")"); printout.send(sender); - - // Save regions - saveRegionsSilently(regionManager, world, sender); } /** @@ -927,9 +897,6 @@ public void setPriority(CommandContext args, CommandSender sender) sender.sendMessage(ChatColor.YELLOW + "Priority of '" + existing.getId() + "' set to " + priority + " (higher numbers override)."); - - // Save regions - saveRegionsSilently(regionManager, world, sender); } /** @@ -994,9 +961,6 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE printout.append(")"); } printout.send(sender); - - // Save regions - saveRegionsSilently(regionManager, world, sender); } /** @@ -1027,9 +991,6 @@ public void remove(CommandContext args, CommandSender sender) throws CommandExce regionManager.removeRegion(existing.getId()); sender.sendMessage(ChatColor.YELLOW + "Region '" + existing.getId() + "' removed."); - - // Save regions - saveRegionsSilently(regionManager, world, sender); } /** @@ -1063,20 +1024,22 @@ public void load(CommandContext args, final CommandSender sender) throws Command throw new CommandException("No region manager exists for world '" + world.getName() + "'."); } - ListenableFuture future = manager.load(true); + ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerLoad(manager)); AsyncCommandHelper.wrap(future, plugin, sender) .forRegionDataLoad(world, false); } else { // Load regions for all worlds - List> futures = new ArrayList>(); + List managers = new ArrayList(); + for (World w : Bukkit.getServer().getWorlds()) { RegionManager manager = plugin.getGlobalRegionManager().get(w); if (manager != null) { - futures.add(manager.load(true)); + managers.add(manager); } } - ListenableFuture future = Futures.successfulAsList(futures); + + ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerLoad(managers)); AsyncCommandHelper.wrap(future, plugin, sender) .registerWithSupervisor("Loading regions for all worlds") @@ -1118,20 +1081,22 @@ public void save(CommandContext args, final CommandSender sender) throws Command throw new CommandException("No region manager exists for world '" + world.getName() + "'."); } - ListenableFuture future = manager.save(true); + ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerSave(manager)); AsyncCommandHelper.wrap(future, plugin, sender) .forRegionDataSave(world, false); } else { // Save for all worlds - List> futures = new ArrayList>(); + List managers = new ArrayList(); + for (World w : Bukkit.getServer().getWorlds()) { RegionManager manager = plugin.getGlobalRegionManager().get(w); if (manager != null) { - futures.add(manager.save(true)); + managers.add(manager); } } - ListenableFuture future = Futures.successfulAsList(futures); + + ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerSave(managers)); AsyncCommandHelper.wrap(future, plugin, sender) .registerWithSupervisor("Saving regions for all worlds") @@ -1156,7 +1121,8 @@ public void migrateDB(CommandContext args, CommandSender sender) throws CommandE if (!getPermissionModel(sender).mayMigrateRegionStore()) { throw new CommandPermissionsException(); } - + + /* String from = args.getString(0).toLowerCase().trim(); String to = args.getString(1).toLowerCase().trim(); @@ -1202,6 +1168,7 @@ public void migrateDB(CommandContext args, CommandSender sender) throws CommandE sender.sendMessage(ChatColor.YELLOW + "Regions have been migrated successfully.\n" + "If you wish to use the destination format as your new backend, please update your config and reload WorldGuard."); + */ } /** @@ -1246,7 +1213,7 @@ public void migrateUuid(CommandContext args, CommandSender sender) throws Comman } else { sender.sendMessage(ChatColor.YELLOW + "There were no names to migrate."); } - } catch (ProtectionDatabaseException e) { + } catch (IOException e) { plugin.getLogger().log(Level.WARNING, "Failed to save", e); throw new CommandException("Error encountered while saving: " + e.getMessage()); } catch (MigrationException e) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerLoad.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerLoad.java new file mode 100644 index 00000000..c36de61c --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerLoad.java @@ -0,0 +1,53 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands; + +import com.sk89q.worldguard.protection.managers.RegionManager; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.Callable; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class RegionManagerLoad implements Callable> { + + private final Collection managers; + + RegionManagerLoad(Collection managers) { + checkNotNull(managers); + this.managers = managers; + } + + RegionManagerLoad(RegionManager... manager) { + this(Arrays.asList(manager)); + } + + @Override + public Collection call() throws IOException { + for (RegionManager manager : managers) { + manager.load(); + } + + return managers; + } + +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerSave.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerSave.java new file mode 100644 index 00000000..19fbf4d8 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerSave.java @@ -0,0 +1,53 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands; + +import com.sk89q.worldguard.protection.managers.RegionManager; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.Callable; + +import static com.google.common.base.Preconditions.checkNotNull; + +class RegionManagerSave implements Callable> { + + private final Collection managers; + + RegionManagerSave(Collection managers) { + checkNotNull(managers); + this.managers = managers; + } + + RegionManagerSave(RegionManager... manager) { + this(Arrays.asList(manager)); + } + + @Override + public Collection call() throws IOException { + for (RegionManager manager : managers) { + manager.save(); + } + + return managers; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java index a817a05b..f9732f02 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java @@ -27,8 +27,8 @@ import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.databases.util.DomainInputResolver; -import com.sk89q.worldguard.protection.databases.util.DomainInputResolver.UserLocatorPolicy; +import com.sk89q.worldguard.protection.util.DomainInputResolver; +import com.sk89q.worldguard.protection.util.DomainInputResolver.UserLocatorPolicy; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -72,7 +72,7 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE String id = args.getString(0); RegionManager manager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = manager.getRegion(id); + ProtectedRegion region = manager.matchRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -104,8 +104,7 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE .formatUsing(region.getId(), world.getName()) .registerWithSupervisor("Adding members to the region '%s' on '%s'") .sendMessageAfterDelay("(Please wait... querying player names...)") - .thenRespondWith("Region '%s' updated with new members.", "Failed to add new members") - .thenSaveRegionData(manager, world); + .thenRespondWith("Region '%s' updated with new members.", "Failed to add new members"); } @Command(aliases = {"addowner", "addowner", "ao"}, @@ -134,7 +133,7 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx String id = args.getString(0); RegionManager manager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = manager.getRegion(id); + ProtectedRegion region = manager.matchRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -179,8 +178,7 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx .formatUsing(region.getId(), world.getName()) .registerWithSupervisor("Adding owners to the region '%s' on '%s'") .sendMessageAfterDelay("(Please wait... querying player names...)") - .thenRespondWith("Region '%s' updated with new owners.", "Failed to add new owners") - .thenSaveRegionData(manager, world); + .thenRespondWith("Region '%s' updated with new owners.", "Failed to add new owners"); } @Command(aliases = {"removemember", "remmember", "removemem", "remmem", "rm"}, @@ -209,7 +207,7 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma String id = args.getString(0); RegionManager manager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = manager.getRegion(id); + ProtectedRegion region = manager.matchRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -253,8 +251,7 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma .formatUsing(region.getId(), world.getName()) .registerWithSupervisor("Removing members from the region '%s' on '%s'") .sendMessageAfterDelay("(Please wait... querying player names...)") - .thenRespondWith("Region '%s' updated with members removed.", "Failed to remove members") - .thenSaveRegionData(manager, world); + .thenRespondWith("Region '%s' updated with members removed.", "Failed to remove members"); } @Command(aliases = {"removeowner", "remowner", "ro"}, @@ -284,7 +281,7 @@ public void removeOwner(CommandContext args, String id = args.getString(0); RegionManager manager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = manager.getRegion(id); + ProtectedRegion region = manager.matchRegion(id); if (region == null) { throw new CommandException("Could not find a region by that ID."); @@ -328,7 +325,6 @@ public void removeOwner(CommandContext args, .formatUsing(region.getId(), world.getName()) .registerWithSupervisor("Removing owners from the region '%s' on '%s'") .sendMessageAfterDelay("(Please wait... querying player names...)") - .thenRespondWith("Region '%s' updated with owners removed.", "Failed to remove owners") - .thenSaveRegionData(manager, world); + .thenRespondWith("Region '%s' updated with owners removed.", "Failed to remove owners"); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java index 5ada6da0..97eda335 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java @@ -75,7 +75,7 @@ public void appendBasics() { builder.append(ChatColor.GRAY); builder.append(" (type="); - builder.append(region.getTypeName()); + builder.append(region.getType().getName()); builder.append(ChatColor.GRAY); builder.append(", priority="); diff --git a/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java b/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java index abe347b9..80670542 100644 --- a/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java +++ b/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java @@ -23,6 +23,7 @@ import com.sk89q.squirrelid.Profile; import com.sk89q.squirrelid.cache.ProfileCache; import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.util.ChangeTracked; import javax.annotation.Nullable; import java.util.ArrayList; @@ -37,7 +38,7 @@ /** * A combination of a {@link PlayerDomain} and a {@link GroupDomain}. */ -public class DefaultDomain implements Domain { +public class DefaultDomain implements Domain, ChangeTracked { private PlayerDomain playerDomain = new PlayerDomain(); private GroupDomain groupDomain = new GroupDomain(); @@ -84,9 +85,7 @@ public void setGroupDomain(GroupDomain groupDomain) { * Add the given player to the domain, identified by the player's name. * * @param name the name of the player - * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ */ - @Deprecated public void addPlayer(String name) { playerDomain.addPlayer(name); } @@ -95,9 +94,7 @@ public void addPlayer(String name) { * Remove the given player from the domain, identified by the player's name. * * @param name the name of the player - * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ */ - @Deprecated public void removePlayer(String name) { playerDomain.removePlayer(name); } @@ -179,9 +176,7 @@ public void removeAll(DefaultDomain other) { * Get the set of player names. * * @return the set of player names - * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ */ - @Deprecated public Set getPlayers() { return playerDomain.getPlayers(); } @@ -252,7 +247,6 @@ public void removeAll() { clear(); } - @SuppressWarnings("deprecation") public String toPlayersString() { return toPlayersString(null); } @@ -340,4 +334,14 @@ public String toUserFriendlyString(ProfileCache cache) { return str.toString(); } + @Override + public boolean isDirty() { + return playerDomain.isDirty() || groupDomain.isDirty(); + } + + @Override + public void setDirty(boolean dirty) { + playerDomain.setDirty(dirty); + groupDomain.setDirty(dirty); + } } diff --git a/src/main/java/com/sk89q/worldguard/domains/GroupDomain.java b/src/main/java/com/sk89q/worldguard/domains/GroupDomain.java index 5118d762..0623dbb6 100644 --- a/src/main/java/com/sk89q/worldguard/domains/GroupDomain.java +++ b/src/main/java/com/sk89q/worldguard/domains/GroupDomain.java @@ -20,19 +20,23 @@ package com.sk89q.worldguard.domains; import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.util.ChangeTracked; +import java.util.Collections; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; /** * Contains groups in a domain. */ -public class GroupDomain implements Domain { +public class GroupDomain implements Domain, ChangeTracked { private final Set groups = new CopyOnWriteArraySet(); + private boolean dirty = true; /** * Create a new instance. @@ -43,11 +47,11 @@ public GroupDomain() { /** * Create a new instance. * - * @param groupsy an array of groups + * @param groups an array of groups */ - public GroupDomain(String[] groupsy) { - checkNotNull(groupsy); - for (String group : groupsy) { + public GroupDomain(String[] groups) { + checkNotNull(groups); + for (String group : groups) { addGroup(group); } } @@ -59,7 +63,9 @@ public GroupDomain(String[] groupsy) { */ public void addGroup(String name) { checkNotNull(name); - groups.add(name.toLowerCase()); + checkArgument(!name.trim().isEmpty(), "Can't add empty group name"); + setDirty(true); + groups.add(name.trim().toLowerCase()); } /** @@ -69,7 +75,8 @@ public void addGroup(String name) { */ public void removeGroup(String name) { checkNotNull(name); - groups.remove(name.toLowerCase()); + setDirty(true); + groups.remove(name.trim().toLowerCase()); } @Override @@ -90,7 +97,7 @@ public boolean contains(LocalPlayer player) { * @return the set of group names */ public Set getGroups() { - return groups; + return Collections.unmodifiableSet(groups); } @Override @@ -113,4 +120,14 @@ public void clear() { groups.clear(); } + @Override + public boolean isDirty() { + return dirty; + } + + @Override + public void setDirty(boolean dirty) { + this.dirty = dirty; + } + } diff --git a/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java b/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java index 3da2e3c6..f390a8db 100644 --- a/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java +++ b/src/main/java/com/sk89q/worldguard/domains/PlayerDomain.java @@ -20,20 +20,24 @@ package com.sk89q.worldguard.domains; import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.util.ChangeTracked; +import java.util.Collections; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; /** * Stores players (only) in a domain. */ -public class PlayerDomain implements Domain { +public class PlayerDomain implements Domain, ChangeTracked { private final Set uniqueIds = new CopyOnWriteArraySet(); private final Set names = new CopyOnWriteArraySet(); + private boolean dirty = true; /** * Create a new instance. @@ -58,12 +62,15 @@ public PlayerDomain(String[] names) { * Add the given player to the domain, identified by the player's name. * * @param name the name of the player - * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ */ - @Deprecated public void addPlayer(String name) { checkNotNull(name); - names.add(name.toLowerCase()); + checkArgument(!name.trim().isEmpty(), "Can't add empty player name"); + setDirty(true); + names.add(name.trim().toLowerCase()); + // Trim because some names contain spaces (previously valid Minecraft + // names) and we cannot store these correctly in the SQL storage + // implementations } /** @@ -73,6 +80,7 @@ public void addPlayer(String name) { */ public void addPlayer(UUID uniqueId) { checkNotNull(uniqueId); + setDirty(true); uniqueIds.add(uniqueId); } @@ -83,6 +91,7 @@ public void addPlayer(UUID uniqueId) { */ public void addPlayer(LocalPlayer player) { checkNotNull(player); + setDirty(true); addPlayer(player.getUniqueId()); } @@ -90,12 +99,11 @@ public void addPlayer(LocalPlayer player) { * Remove the given player from the domain, identified by the player's name. * * @param name the name of the player - * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ */ - @Deprecated public void removePlayer(String name) { checkNotNull(name); - names.remove(name.toLowerCase()); + setDirty(true); + names.remove(name.trim().toLowerCase()); } /** @@ -116,8 +124,8 @@ public void removePlayer(UUID uuid) { */ public void removePlayer(LocalPlayer player) { checkNotNull(player); - names.remove(player.getName().toLowerCase()); - uniqueIds.remove(player.getUniqueId()); + removePlayer(player.getName()); + removePlayer(player.getUniqueId()); } @Override @@ -130,11 +138,9 @@ public boolean contains(LocalPlayer player) { * Get the set of player names. * * @return the set of player names - * @deprecated names are deprecated in favor of UUIDs in MC 1.7+ */ - @Deprecated public Set getPlayers() { - return names; + return Collections.unmodifiableSet(names); } /** @@ -143,7 +149,7 @@ public Set getPlayers() { * @return the set of player UUIDs */ public Set getUniqueIds() { - return uniqueIds; + return Collections.unmodifiableSet(uniqueIds); } @Override @@ -155,7 +161,7 @@ public boolean contains(UUID uniqueId) { @Override public boolean contains(String playerName) { checkNotNull(playerName); - return names.contains(playerName.toLowerCase()); + return names.contains(playerName.trim().toLowerCase()); } @Override @@ -169,4 +175,14 @@ public void clear() { names.clear(); } + @Override + public boolean isDirty() { + return dirty; + } + + @Override + public void setDirty(boolean dirty) { + this.dirty = dirty; + } + } diff --git a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java index fd9c966d..aac4e751 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java +++ b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java @@ -20,67 +20,87 @@ package com.sk89q.worldguard.protection; import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.protection.flags.*; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.RegionGroup; +import com.sk89q.worldguard.protection.flags.RegionGroupFlag; +import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.flags.StateFlag.State; +import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import java.util.*; +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import static com.google.common.base.Preconditions.checkNotNull; /** - * Represents a set of regions for a particular point or area and the rules - * that are represented by that set. An instance of this can be used to - * query the value of a flag or check if a player can build in the respective - * region or point. This object contains the list of applicable regions and so - * the expensive search of regions that are in the desired area has already - * been completed. - * - * @author sk89q + * Represents the effective set of flags, owners, and members for a given + * spatial query. + * + *

An instance of this can be created using the spatial query methods + * available on {@link RegionManager}.

*/ public class ApplicableRegionSet implements Iterable { - private Collection applicable; - private ProtectedRegion globalRegion; + private final SortedSet applicable; + @Nullable + private final ProtectedRegion globalRegion; + + /** + * Construct the object. + * + *

A sorted set will be created to include the collection of regions.

+ * + * @param applicable the regions contained in this set + * @param globalRegion the global region, set aside for special handling. + */ + public ApplicableRegionSet(Collection applicable, @Nullable ProtectedRegion globalRegion) { + this(new TreeSet(checkNotNull(applicable)), globalRegion); + } /** * Construct the object. * - * @param applicable The regions contained in this set - * @param globalRegion The global region, set aside for special handling. + * @param applicable the regions contained in this set + * @param globalRegion the global region, set aside for special handling. */ - public ApplicableRegionSet(Collection applicable, - ProtectedRegion globalRegion) { + public ApplicableRegionSet(SortedSet applicable, @Nullable ProtectedRegion globalRegion) { + checkNotNull(applicable); this.applicable = applicable; this.globalRegion = globalRegion; } /** - * Checks if a player can build in an area. + * Test whether a player can build in an area. * * @param player The player to check * @return build ability */ public boolean canBuild(LocalPlayer player) { + checkNotNull(player); return internalGetState(DefaultFlag.BUILD, player, null); } + /** + * Test whether the construct flag evaluates true for the given player. + * + * @param player the player + * @return true if true + */ public boolean canConstruct(LocalPlayer player) { + checkNotNull(player); final RegionGroup flag = getFlag(DefaultFlag.CONSTRUCT, player); return RegionGroupFlag.isMember(this, flag, player); } - /** - * Checks if a player can use buttons and such in an area. - * - * @param player The player to check - * @return able to use items - * @deprecated This method seems to be the opposite of its name - */ - @Deprecated - public boolean canUse(LocalPlayer player) { - return !allows(DefaultFlag.USE, player) - && !canBuild(player); - } - /** * Gets the state of a state flag. This cannot be used for the build flag. * @@ -89,9 +109,12 @@ public boolean canUse(LocalPlayer player) { * @throws IllegalArgumentException if the build flag is given */ public boolean allows(StateFlag flag) { + checkNotNull(flag); + if (flag == DefaultFlag.BUILD) { throw new IllegalArgumentException("Can't use build flag with allows()"); } + return internalGetState(flag, null, null); } @@ -103,7 +126,9 @@ public boolean allows(StateFlag flag) { * @return whether the state is allows for it * @throws IllegalArgumentException if the build flag is given */ - public boolean allows(StateFlag flag, LocalPlayer player) { + public boolean allows(StateFlag flag, @Nullable LocalPlayer player) { + checkNotNull(flag); + if (flag == DefaultFlag.BUILD) { throw new IllegalArgumentException("Can't use build flag with allows()"); } @@ -111,12 +136,14 @@ public boolean allows(StateFlag flag, LocalPlayer player) { } /** - * Indicates whether a player is an owner of all regions in this set. + * Test whether a player is an owner of all regions in this set. * - * @param player player + * @param player the player * @return whether the player is an owner of all regions */ public boolean isOwnerOfAll(LocalPlayer player) { + checkNotNull(player); + for (ProtectedRegion region : applicable) { if (!region.isOwner(player)) { return false; @@ -127,13 +154,14 @@ public boolean isOwnerOfAll(LocalPlayer player) { } /** - * Indicates whether a player is an owner or member of all regions in - * this set. + * Test whether a player is an owner or member of all regions in this set. * - * @param player player + * @param player the player * @return whether the player is a member of all regions */ public boolean isMemberOfAll(LocalPlayer player) { + checkNotNull(player); + for (ProtectedRegion region : applicable) { if (!region.isMember(player)) { return false; @@ -144,15 +172,16 @@ public boolean isMemberOfAll(LocalPlayer player) { } /** - * Checks to see if a flag is permitted. + * Test whether a flag tests true. * - * @param flag flag to check - * @param player null to not check owners and members - * @param groupPlayer player to use for the group flag check + * @param flag the flag to check + * @param player the player, or null to not check owners and members + * @param groupPlayer a player to use for the group flag check * @return the allow/deny state for the flag */ - private boolean internalGetState(StateFlag flag, LocalPlayer player, - LocalPlayer groupPlayer) { + private boolean internalGetState(StateFlag flag, @Nullable LocalPlayer player, @Nullable LocalPlayer groupPlayer) { + checkNotNull(flag); + boolean found = false; boolean hasFlagDefined = false; boolean allowed = false; // Used for ALLOW override @@ -254,6 +283,7 @@ private boolean internalGetState(StateFlag flag, LocalPlayer player, if (player != null) { hasFlagDefined = true; + //noinspection StatementWithEmptyBody if (hasCleared.contains(region)) { // Already cleared, so do nothing } else { @@ -269,19 +299,17 @@ private boolean internalGetState(StateFlag flag, LocalPlayer player, found = true; } - return !found ? def : - (allowed || (player != null && needsClear.size() == 0)); + return !found ? def : (allowed || (player != null && needsClear.isEmpty())); } /** * Clear a region's parents for isFlagAllowed(). * - * @param needsClear The regions that should be cleared - * @param hasCleared The regions already cleared - * @param region The region to start from + * @param needsClear the regions that should be cleared + * @param hasCleared the regions already cleared + * @param region the region to start from */ - private void clearParents(Set needsClear, - Set hasCleared, ProtectedRegion region) { + private void clearParents(Set needsClear, Set hasCleared, ProtectedRegion region) { ProtectedRegion parent = region.getParent(); while (parent != null) { @@ -294,10 +322,13 @@ private void clearParents(Set needsClear, } /** - * @see #getFlag(com.sk89q.worldguard.protection.flags.Flag, com.sk89q.worldguard.LocalPlayer) - * @param flag flag to check - * @return value of the flag + * Gets the value of a flag. Do not use this for state flags + * (use {@link #allows(StateFlag, LocalPlayer)} for that). + * + * @param flag the flag to check + * @return value of the flag, which may be null */ + @Nullable public , V> V getFlag(T flag) { return getFlag(flag, null); } @@ -308,10 +339,13 @@ public , V> V getFlag(T flag) { * * @param flag flag to check * @param groupPlayer player to check {@link RegionGroup}s against - * @return value of the flag + * @return value of the flag, which may be null * @throws IllegalArgumentException if a StateFlag is given */ - public , V> V getFlag(T flag, LocalPlayer groupPlayer) { + @Nullable + public , V> V getFlag(T flag, @Nullable LocalPlayer groupPlayer) { + checkNotNull(flag); + /* if (flag instanceof StateFlag) { throw new IllegalArgumentException("Cannot use StateFlag with getFlag()"); @@ -341,6 +375,7 @@ public , V> V getFlag(T flag, LocalPlayer groupPlayer) { } } + //noinspection StatementWithEmptyBody if (hasCleared.contains(region)) { // Already cleared, so do nothing } else if (region.getFlag(flag) != null) { @@ -388,16 +423,15 @@ private void clearParents(Map needsClear, /** * Get the number of regions that are included. * - * @return the size of this ApplicbleRegionSet + * @return the number of contained regions */ public int size() { return applicable.size(); } - - /** - * Get an iterator of affected regions. - */ + + @Override public Iterator iterator() { return applicable.iterator(); } + } diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index 5624e8ee..b5260092 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -24,289 +24,74 @@ import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.databases.MySQLDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.databases.YAMLDatabase; import com.sk89q.worldguard.protection.flags.StateFlag; -import com.sk89q.worldguard.protection.managers.PRTreeRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; -import java.io.File; -import java.io.FileNotFoundException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; +import javax.annotation.Nullable; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; -/** - * This class keeps track of region information for every world. It loads - * world region information as needed. - * - * @author sk89q - * @author Redecouverte - */ public class GlobalRegionManager { - /** - * Reference to the plugin. - */ - private WorldGuardPlugin plugin; + private final WorldGuardPlugin plugin; + private final ConfigurationManager config; + private final ManagerContainer container; - /** - * Reference to the global configuration. - */ - private ConfigurationManager config; - - /** - * Map of managers per-world. - */ - private ConcurrentHashMap managers; - - /** - * Stores the list of modification dates for the world files. This allows - * WorldGuard to reload files as needed. - */ - private HashMap lastModified; - - /** - * Construct the object. - * - * @param plugin The plugin instance - */ public GlobalRegionManager(WorldGuardPlugin plugin) { this.plugin = plugin; config = plugin.getGlobalStateManager(); - managers = new ConcurrentHashMap(); - lastModified = new HashMap(); - } - - /** - * Unload region information. - */ - public void unload() { - managers.clear(); - lastModified.clear(); - } - - /** - * Get the path for a world's regions file. - * - * @param name The name of the world - * @return The region file path for a world's region file - */ - protected File getPath(String name) { - return new File(plugin.getDataFolder(), - "worlds" + File.separator + name + File.separator + "regions.yml"); - } - - /** - * Unload region information for a world. - * - * @param name The name of the world to unload - */ - public void unload(String name) { - RegionManager manager = managers.remove(name); - - if (manager != null) { - lastModified.remove(name); - } - } - - /** - * Unload all region information. - */ - public void unloadAll() { - managers.clear(); - lastModified.clear(); + container = new ManagerContainer(config); } + @Nullable public RegionManager load(World world) { - RegionManager manager = create(world); - if (manager != null) { - managers.put(world.getName(), manager); - } - return manager; + return container.load(world.getName()); } - /** - * Load region information for a world. - * - * @param world The world to load a RegionManager for - * @return the loaded RegionManager - */ - public RegionManager create(World world) { - String name = world.getName(); - boolean sql = config.useSqlDatabase; - ProtectionDatabase database; - File file = null; - - try { - if (!sql) { - file = getPath(name); - database = new YAMLDatabase(file, plugin.getLogger()); - - // Store the last modification date so we can track changes - lastModified.put(name, file.lastModified()); - } else { - database = new MySQLDatabase(config, name, plugin.getLogger()); - } - - // Create a manager - RegionManager manager = new PRTreeRegionManager(database); - manager.load(); - - if (plugin.getGlobalStateManager().get(world).summaryOnStart) { - plugin.getLogger().info(manager.getRegions().size() - + " regions loaded for '" + name + "'"); - } - - return manager; - } catch (ProtectionDatabaseException e) { - String logStr = "Failed to load regions from "; - if (sql) { - logStr += "SQL Database <" + config.sqlDsn + "> "; - } else { - logStr += "file \"" + file + "\" "; - } - - plugin.getLogger().log(Level.SEVERE, logStr + " : " + e.getMessage()); - e.printStackTrace(); - } catch (FileNotFoundException e) { - plugin.getLogger().log(Level.SEVERE, "Error loading regions for world \"" - + name + "\": " + e.toString() + "\n\t" + e.getMessage()); - e.printStackTrace(); - } - - // @TODO: THIS CREATES PROBLEMS!!one!!1!!eleven!!1!!! - return null; - } - - /** - * Preloads region managers for all worlds. - */ public void preload() { - // Load regions for (World world : plugin.getServer().getWorlds()) { load(world); } } - /** - * Reloads the region information from file when region databases - * have changed. - */ - public void reloadChanged() { - if (config.useSqlDatabase) return; - - for (String name : managers.keySet()) { - File file = getPath(name); - - Long oldDate = lastModified.get(name); - - if (oldDate == null) { - oldDate = 0L; - } - - try { - if (file.lastModified() > oldDate) { - World world = plugin.getServer().getWorld(name); - - if (world != null) { - load(world); - } - } - } catch (Exception ignore) { - } - } + public void unload(String name) { + container.unload(name); } - /** - * Get the region manager for a particular world. - * - * @param world The world to get a RegionManager for - * @return The region manager. - */ + public void unload() { + container.unloadAll(); + } + + public void unloadAll() { + container.unloadAll(); + } + + @Nullable public RegionManager get(World world) { - RegionManager manager = managers.get(world.getName()); - RegionManager newManager = null; - - while (manager == null) { - if (newManager == null) { - newManager = create(world); - } - managers.putIfAbsent(world.getName(), newManager); - manager = managers.get(world.getName()); - } - - return manager; + return container.get(world.getName()); } - /** - * Get a list of loaded region managers. - * - * @return an unmodifiable list - */ public List getLoaded() { - List list = new ArrayList(); - for (RegionManager manager : managers.values()) { - if (manager != null) { - list.add(manager); - } - } - return Collections.unmodifiableList(list); + return container.getLoaded(); } - /** - * Returns whether the player can bypass. - * - * @param player The player to check - * @param world The world to check for - * @return Whether {@code player} has bypass permission for {@code world} - */ public boolean hasBypass(LocalPlayer player, World world) { - return player.hasPermission("worldguard.region.bypass." - + world.getName()); + return player.hasPermission("worldguard.region.bypass." + world.getName()); } - /** - * Returns whether the player can bypass. - * - * @param player The player to check - * @param world The world to check - * @return Whether {@code player} has bypass permission for {@code world} - */ public boolean hasBypass(Player player, World world) { - return plugin.hasPermission(player, "worldguard.region.bypass." - + world.getName()); + return plugin.hasPermission(player, "worldguard.region.bypass." + world.getName()); } - /** - * Check if a player has permission to build at a block. - * - * @param player The player to check - * @param block The block to check at - * @return Whether {@code player} can build at {@code block}'s location - */ public boolean canBuild(Player player, Block block) { return canBuild(player, block.getLocation()); } - /** - * Check if a player has permission to build at a location. - * - * @param player The player to check - * @param loc The location to check - * @return Whether {@code player} can build at {@code loc} - */ public boolean canBuild(Player player, Location loc) { World world = loc.getWorld(); WorldConfiguration worldConfig = config.get(world); @@ -320,8 +105,7 @@ public boolean canBuild(Player player, Location loc) { if (!hasBypass(player, world)) { RegionManager mgr = get(world); - if (!mgr.getApplicableRegions(BukkitUtil.toVector(loc)) - .canBuild(localPlayer)) { + if (mgr != null && !mgr.getApplicableRegions(BukkitUtil.toVector(loc)).canBuild(localPlayer)) { return false; } } @@ -346,39 +130,25 @@ public boolean canConstruct(Player player, Location loc) { if (!hasBypass(player, world)) { RegionManager mgr = get(world); - final ApplicableRegionSet applicableRegions = mgr.getApplicableRegions(BukkitUtil.toVector(loc)); - if (!applicableRegions.canBuild(localPlayer)) { - return false; - } - if (!applicableRegions.canConstruct(localPlayer)) { - return false; + if (mgr != null) { + final ApplicableRegionSet applicableRegions = mgr.getApplicableRegions(BukkitUtil.toVector(loc)); + if (!applicableRegions.canBuild(localPlayer)) { + return false; + } + if (!applicableRegions.canConstruct(localPlayer)) { + return false; + } } } return true; } - /** - * Checks to see whether a flag is allowed. - * - * @see #allows(com.sk89q.worldguard.protection.flags.StateFlag, org.bukkit.Location, com.sk89q.worldguard.LocalPlayer) - * @param flag The flag to check - * @param loc The location to check the flag at - * @return Whether the flag is allowed - */ public boolean allows(StateFlag flag, Location loc) { return allows(flag, loc, null); } - /** - * Checks to see whether a flag is allowed. - * - * @param flag The flag to check - * @param loc The location to check the flag at - * @param player The player to check for the flag's {@link com.sk89q.worldguard.protection.flags.RegionGroup} - * @return Whether the flag is allowed - */ - public boolean allows(StateFlag flag, Location loc, LocalPlayer player) { + public boolean allows(StateFlag flag, Location loc, @Nullable LocalPlayer player) { World world = loc.getWorld(); WorldConfiguration worldConfig = config.get(world); @@ -387,6 +157,7 @@ public boolean allows(StateFlag flag, Location loc, LocalPlayer player) { } RegionManager mgr = get(world); - return mgr.getApplicableRegions(toVector(loc)).allows(flag, player); + return mgr == null || mgr.getApplicableRegions(toVector(loc)).allows(flag, player); } + } diff --git a/src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java b/src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java new file mode 100644 index 00000000..521f0fc1 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java @@ -0,0 +1,174 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection; + +import com.google.common.base.Supplier; +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex; +import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex; +import com.sk89q.worldguard.protection.managers.storage.RegionStore; +import com.sk89q.worldguard.protection.managers.storage.driver.DriverType; +import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver; +import com.sk89q.worldguard.util.Normal; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Manages different {@link RegionManager}s for different worlds or dimensions. + */ +class ManagerContainer { + + private static final Logger log = Logger.getLogger(ManagerContainer.class.getCanonicalName()); + private static final int SAVE_INTERVAL = 1000 * 30; + + private final ConcurrentMap mapping = new ConcurrentHashMap(); + private final Object lock = new Object(); + private final EnumMap drivers = new EnumMap(DriverType.class); + private final RegionStoreDriver defaultDriver; + private final Supplier indexFactory = new PriorityRTreeIndex.Factory(); + private final Timer timer = new Timer(); + + ManagerContainer(ConfigurationManager config) { + checkNotNull(config); + + for (DriverType type : DriverType.values()) { + drivers.put(type, type.create(config)); + } + + if (config.useSqlDatabase) { + defaultDriver = drivers.get(DriverType.SQL); + } else { + defaultDriver = drivers.get(DriverType.YAML); + } + + timer.schedule(new BackgroundSaver(), SAVE_INTERVAL, SAVE_INTERVAL); + } + + @Nullable + public RegionManager load(String name) { + checkNotNull(name); + + Normal normal = Normal.normal(name); + + synchronized (lock) { + RegionManager manager = mapping.get(normal); + if (manager != null) { + return manager; + } else { + try { + manager = createAndLoad(name); + mapping.put(normal, manager); + return manager; + } catch (IOException e) { + log.log(Level.WARNING, "Failed to load the region data for '" + name + "'", e); + return null; + } + } + } + } + + private RegionManager createAndLoad(String name) throws IOException { + RegionStore store = defaultDriver.get(name); + RegionManager manager = new RegionManager(store, indexFactory); + manager.load(); // Try loading, although it may fail + return manager; + } + + public void unload(String name) { + checkNotNull(name); + + Normal normal = Normal.normal(name); + + synchronized (lock) { + RegionManager manager = mapping.get(normal); + if (manager != null) { + try { + manager.save(); + } catch (IOException e) { + log.log(Level.WARNING, "Failed to save the region data for '" + name + "'", e); + } + } + mapping.remove(normal); + } + } + + public void unloadAll() { + synchronized (lock) { + for (Map.Entry entry : mapping.entrySet()) { + String name = entry.getKey().toString(); + RegionManager manager = entry.getValue(); + try { + manager.save(); + } catch (IOException e) { + log.log(Level.WARNING, "Failed to save the region data for '" + name + "' while unloading the data for all worlds", e); + } + } + + mapping.clear(); + } + } + + @Nullable + public RegionManager get(String name) { + checkNotNull(name); + return mapping.get(Normal.normal(name)); + } + + public List getLoaded() { + return Collections.unmodifiableList(new ArrayList(mapping.values())); + } + + private class BackgroundSaver extends TimerTask { + @Override + public void run() { + synchronized (lock) { + // Block loading of new region managers + + for (Map.Entry entry : mapping.entrySet()) { + String name = entry.getKey().toString(); + RegionManager manager = entry.getValue(); + try { + manager.saveChanges(); + } catch (IOException e) { + log.log(Level.WARNING, "Failed to save the region data for '" + name + "' during a periodical save", e); + } catch (Exception e) { + log.log(Level.WARNING, "An expected error occurred during a periodical save", e); + } + } + } + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/UnsupportedIntersectionException.java b/src/main/java/com/sk89q/worldguard/protection/UnsupportedIntersectionException.java index 2f631c1b..21a2ea28 100644 --- a/src/main/java/com/sk89q/worldguard/protection/UnsupportedIntersectionException.java +++ b/src/main/java/com/sk89q/worldguard/protection/UnsupportedIntersectionException.java @@ -19,6 +19,12 @@ package com.sk89q.worldguard.protection; +/** + * Thrown when an intersection between two different types of regions is not + * supported. + * + * @deprecated no longer utilized + */ +@Deprecated public class UnsupportedIntersectionException extends Exception { - private static final long serialVersionUID = 6423189392345575148L; } diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java deleted file mode 100644 index 72a1aa53..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/AbstractAsynchronousDatabase.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases; - -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import com.sk89q.odeum.concurrent.EvenMoreExecutors; -import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import javax.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.RejectedExecutionException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * An abstract implementation of a {@code RegionManager} that supports - * asynchronously loading and saving region data while only allowing one - * single operation (either load or save) occurring at a given time. - */ -public abstract class AbstractAsynchronousDatabase extends AbstractProtectionDatabase { - - private static final Logger log = Logger.getLogger(AbstractAsynchronousDatabase.class.getName()); - - private final ListeningExecutorService executor = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 4)); - private final Object lock = new Object(); - private QueuedTask lastSave; - private QueuedTask lastLoad; - - @Override - public final void load() throws ProtectionDatabaseException, RejectedExecutionException { - blockOnSave(submitLoadTask(null)); - } - - @Override - public final ListenableFuture load(RegionManager manager, boolean async) throws RejectedExecutionException { - ListenableFuture future = submitLoadTask(manager); - if (!async) { - blockOnLoad(future); - } - return future; - } - - @Override - public final void save() throws ProtectionDatabaseException, RejectedExecutionException { - blockOnSave(submitSaveTask(new HashMap(getRegions()))); - } - - @Override - public final ListenableFuture save(RegionManager manager, boolean async) throws RejectedExecutionException { - ListenableFuture future = submitSaveTask(new HashMap(manager.getRegions())); - if (!async) { - blockOnSave(future); - } - return future; - } - - /** - * Submit a load task and return a future for it. - * - *

If a load task is already queued then that load task's future will - * be returned.

- * - * @param manager the manager - * @return a future - * @throws RejectedExecutionException thrown if there are too many load/save tasks queued - */ - private ListenableFuture submitLoadTask(@Nullable final RegionManager manager) throws RejectedExecutionException { - synchronized (lock) { - lastSave = null; // Void the pending queued save so that any future - // save() calls will submit a brand new save task - - QueuedTask last = lastLoad; - - // Check if there is already a queued task that has not yet started - // that we can return, rather than queue yet another task - if (last != null && !last.started) { - return last.future; - } else { - // Submit the task - final QueuedTask task = new QueuedTask(); - task.future = executor.submit(new Callable() { - @Override - public Object call() throws Exception { - task.started = true; - - performLoad(); - if (manager != null) { - manager.setRegions(getRegions()); - } - return AbstractAsynchronousDatabase.this; - } - }); - - this.lastLoad = task; - - return task.future; - } - } - } - - /** - * Submit a save task and return a future for it. - * - *

If a save task is already queued then that save task's future will - * be returned.

- * - * @param entries a map of regions - * @return a future - * @throws RejectedExecutionException thrown if there are too many load/save tasks queued - */ - private ListenableFuture submitSaveTask(final Map entries) throws RejectedExecutionException { - checkNotNull(entries); - - synchronized (lock) { - lastLoad = null; // Void the pending queued load so that any future - // load() calls will submit a brand new load task - - QueuedTask last = lastSave; - - // Check if there is already a queued task that has not yet started - // that we can return, rather than queue yet another task - if (last != null && !last.started) { - return last.future; - } else { - // Submit the task - final QueuedTask task = new QueuedTask(); - task.future = executor.submit(new Callable() { - @Override - public Object call() throws Exception { - task.started = true; - - setRegions(entries); - performSave(); - return AbstractAsynchronousDatabase.this; - } - }); - - this.lastSave = task; - - return task.future; - } - } - } - - /** - * Block on the given future and print error messages about failing to - * load the database on error. - * - * @param future the future - */ - private void blockOnLoad(Future future) { - try { - future.get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - log.log(Level.WARNING, "Failed to load the region database", e); - } - } - - /** - * Block on the given future and print error messages about failing to - * save the database on error. - * - * @param future the future - */ - private void blockOnSave(Future future) { - try { - future.get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - log.log(Level.WARNING, "Failed to save the region database", e); - } - } - - /** - * Call to execute a load that may occur in any thread. However, no - * other save or load operation will be simultaneously ongoing. - */ - protected abstract void performLoad() throws ProtectionDatabaseException; - - /** - * Call to execute a save that may occur in any thread. However, no - * other save or load operation will be simultaneously ongoing. - * - *

{@link #setRegions(Map)} must not be called until loading - * has completed and the provided map is in its completed state.

- */ - protected abstract void performSave() throws ProtectionDatabaseException; - - /** - * Stores information about the a queued task. - */ - private static class QueuedTask { - private boolean started = false; - private ListenableFuture future; - } - -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/AbstractProtectionDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/AbstractProtectionDatabase.java deleted file mode 100644 index e62effaf..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/AbstractProtectionDatabase.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.sk89q.worldguard.protection.managers.RegionManager; - -public abstract class AbstractProtectionDatabase implements ProtectionDatabase { - - @Override - public void load(RegionManager manager) throws ProtectionDatabaseException { - load(); - manager.setRegions(getRegions()); - } - - @Override - public final void save(RegionManager manager) throws ProtectionDatabaseException { - save(manager, false); - } - - @Override - public ListenableFuture load(RegionManager manager, boolean async) { - try { - load(manager); - } catch (ProtectionDatabaseException e) { - return Futures.immediateFailedFuture(e); - } - return Futures.immediateCheckedFuture(this); - } - - @Override - public ListenableFuture save(RegionManager manager, boolean async) { - setRegions(manager.getRegions()); - try { - save(); - } catch (ProtectionDatabaseException e) { - return Futures.immediateFailedFuture(e); - } - return Futures.immediateCheckedFuture(this); - } - -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java deleted file mode 100644 index 75a72be7..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/CSVDatabase.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases; - -import au.com.bytecode.opencsv.CSVReader; -import com.sk89q.worldedit.BlockVector; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.flags.StateFlag; -import com.sk89q.worldguard.protection.flags.StateFlag.State; -import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; -import com.sk89q.worldguard.util.ArrayReader; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Represents a protected area database that uses CSV files. - * - * @deprecated no longer maintained - use {@link YAMLDatabase} - */ -@Deprecated -public class CSVDatabase extends AbstractProtectionDatabase { - - private static final Map legacyFlagCodes = new HashMap(); - static { - legacyFlagCodes.put("z", DefaultFlag.PASSTHROUGH); - legacyFlagCodes.put("b", DefaultFlag.BUILD); - legacyFlagCodes.put("p", DefaultFlag.PVP); - legacyFlagCodes.put("m", DefaultFlag.MOB_DAMAGE); - legacyFlagCodes.put("c", DefaultFlag.CREEPER_EXPLOSION); - legacyFlagCodes.put("t", DefaultFlag.TNT); - legacyFlagCodes.put("l", DefaultFlag.LIGHTER); - legacyFlagCodes.put("f", DefaultFlag.FIRE_SPREAD); - legacyFlagCodes.put("F", DefaultFlag.LAVA_FIRE); - legacyFlagCodes.put("C", DefaultFlag.CHEST_ACCESS); - } - - private final Logger logger; - - /** - * References the CSV file. - */ - private final File file; - /** - * Holds the list of regions. - */ - private Map regions; - - /** - * Construct the database with a path to a file. No file is read or - * written at this time. - * - * @param file The file in CSV format containing the region database - * @param logger The logger to log errors to - */ - public CSVDatabase(File file, Logger logger) { - this.file = file; - this.logger = logger; - } - - /** - * Saves the database. - */ - public void save() throws ProtectionDatabaseException { - throw new UnsupportedOperationException("CSV format is no longer implemented"); - } - - public void load() throws ProtectionDatabaseException { - Map regions = - new HashMap(); - Map parentSets = - new LinkedHashMap(); - - CSVReader reader = null; - try { - reader = new CSVReader(new FileReader(file)); - - String[] line; - - while ((line = reader.readNext()) != null) { - if (line.length < 2) { - logger.warning("Invalid region definition: " + line); - continue; - } - - String id = line[0].toLowerCase().replace(".", ""); - String type = line[1]; - ArrayReader entries = new ArrayReader(line); - - if (type.equalsIgnoreCase("cuboid")) { - if (line.length < 8) { - logger.warning("Invalid region definition: " + line); - continue; - } - - Vector pt1 = new Vector( - Integer.parseInt(line[2]), - Integer.parseInt(line[3]), - Integer.parseInt(line[4])); - Vector pt2 = new Vector( - Integer.parseInt(line[5]), - Integer.parseInt(line[6]), - Integer.parseInt(line[7])); - - BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector(); - BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector(); - - int priority = entries.get(8) == null ? 0 : Integer.parseInt(entries.get(8)); - String ownersData = entries.get(9); - String flagsData = entries.get(10); - //String enterMessage = nullEmptyString(entries.get(11)); - - ProtectedRegion region = new ProtectedCuboidRegion(id, min, max); - region.setPriority(priority); - parseFlags(region, flagsData); - region.setOwners(this.parseDomains(ownersData)); - regions.put(id, region); - } else if (type.equalsIgnoreCase("cuboid.2")) { - Vector pt1 = new Vector( - Integer.parseInt(line[2]), - Integer.parseInt(line[3]), - Integer.parseInt(line[4])); - Vector pt2 = new Vector( - Integer.parseInt(line[5]), - Integer.parseInt(line[6]), - Integer.parseInt(line[7])); - - BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector(); - BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector(); - - int priority = entries.get(8) == null ? 0 : Integer.parseInt(entries.get(8)); - String parentId = entries.get(9); - String ownersData = entries.get(10); - String membersData = entries.get(11); - String flagsData = entries.get(12); - //String enterMessage = nullEmptyString(entries.get(13)); - //String leaveMessage = nullEmptyString(entries.get(14)); - - ProtectedRegion region = new ProtectedCuboidRegion(id, min, max); - region.setPriority(priority); - parseFlags(region, flagsData); - region.setOwners(this.parseDomains(ownersData)); - region.setMembers(this.parseDomains(membersData)); - regions.put(id, region); - - // Link children to parents later - if (parentId.length() > 0) { - parentSets.put(region, parentId); - } - } - } - } catch (IOException e) { - throw new ProtectionDatabaseException(e); - } finally { - try { - reader.close(); - } catch (IOException ignored) { - } - } - - for (Map.Entry entry : parentSets.entrySet()) { - ProtectedRegion parent = regions.get(entry.getValue()); - if (parent != null) { - try { - entry.getKey().setParent(parent); - } catch (CircularInheritanceException e) { - logger.warning("Circular inheritance detect with '" - + entry.getValue() + "' detected as a parent"); - } - } else { - logger.warning("Unknown region parent: " + entry.getValue()); - } - } - - this.regions = regions; - } - - /** - * Used to parse the specified domain in the CSV file. - * - * @param data The domain data as a string - * @return The domain data as a DefaultDomain - */ - private DefaultDomain parseDomains(String data) { - if (data == null) { - return new DefaultDomain(); - } - - DefaultDomain domain = new DefaultDomain(); - Pattern pattern = Pattern.compile("^([A-Za-z]):(.*)$"); - - String[] parts = data.split(","); - - for (String part : parts) { - if (part.trim().length() == 0) { - continue; - } - - Matcher matcher = pattern.matcher(part); - - if (!matcher.matches()) { - logger.warning("Invalid owner specification: " + part); - continue; - } - - String type = matcher.group(1); - String id = matcher.group(2); - - if (type.equals("u")) { - domain.addPlayer(id); - } else if (type.equals("g")) { - domain.addGroup(id); - } else { - logger.warning("Unknown owner specification: " + type); - } - } - - return domain; - } - - /** - * Used to parse the list of flags. - * - * @param data The flag data in string format - */ - private void parseFlags(ProtectedRegion region, String data) { - if (data == null) { - return; - } - - State curState = State.ALLOW; - - for (int i = 0; i < data.length(); i++) { - char k = data.charAt(i); - if (k == '+') { - curState = State.ALLOW; - } else if (k == '-') { - curState = State.DENY; - } else { - String flagStr; - if (k == '_') { - if (i == data.length() - 1) { - logger.warning("_ read ahead fail"); - break; - } - flagStr = "_" + data.charAt(i + 1); - i++; - - logger.warning("_? custom flags are no longer supported"); - continue; - } else { - flagStr = String.valueOf(k); - } - - StateFlag flag = legacyFlagCodes.get(flagStr); - if (flag != null) { - region.setFlag(flag, curState); - } else { - logger.warning("Legacy flag '" + flagStr + "' is unsupported"); - } - } - } - } - - /** - * Used to write the list of domains. - * - * @param domain - * @return - */ -/* private String writeDomains(DefaultDomain domain) { - StringBuilder str = new StringBuilder(); - - for (String player : domain.getPlayers()) { - str.append("u:" + player + ","); - } - - for (String group : domain.getGroups()) { - str.append("g:" + group + ","); - } - - return str.length() > 0 ? - str.toString().substring(0, str.length() - 1) : ""; - }*/ - - /** - * Helper method to prepend '+' or '-' in front of a flag according - * to the flag's state. - * - * @param state - * @param flag - * @return - */ -/* - private String writeFlag(State state, String flag) { - if (state == State.ALLOW) { - return "+" + flag; - } else if (state == State.DENY) { - return "-" + flag; - } - - return ""; - } -*/ - - /** - * Returns a null if a string is null or empty. - * - * @param str The string to format - * @return null if the string is empty or null, otherwise the provided string - */ - protected String nullEmptyString(String str) { - if (str == null) { - return null; - } else if (str.length() == 0) { - return null; - } else { - return str; - } - } - - public Map getRegions() { - return regions; - } - - public void setRegions(Map regions) { - this.regions = regions; - } -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java b/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java deleted file mode 100644 index 88ade282..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabase.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases; - -import com.google.common.util.concurrent.ListenableFuture; -import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.util.Map; -import java.util.concurrent.RejectedExecutionException; - -/** - * Represents a database to read and write lists of regions from and to. - */ -public interface ProtectionDatabase { - - /** - * Load the list of regions. The method should not modify the list returned - * by getRegions() unless the load finishes successfully. - * - * @throws ProtectionDatabaseException when an error occurs - */ - public void load() throws ProtectionDatabaseException, RejectedExecutionException; - - /** - * Load the list of regions into a region manager. - * - *

This call will block.

- * - * @param manager The manager to load regions into - * @throws ProtectionDatabaseException when an error occurs - */ - public void load(RegionManager manager) throws ProtectionDatabaseException, RejectedExecutionException; - - /** - * Load the list of regions into a region manager, optionally to suggest - * that the data be load in another thread without blocking the thread - * from which the call is made. - * - *

{@code async} is merely a suggestion and it may be ignored by - * implementations if it is not supported.

- * - * @param manager The manager to load regions into - * @param async true to attempt to save the data asynchronously if it is supported - */ - public ListenableFuture load(RegionManager manager, boolean async) throws RejectedExecutionException; - - /** - * Save the list of regions. - * - * @throws ProtectionDatabaseException when an error occurs - */ - public void save() throws ProtectionDatabaseException, RejectedExecutionException; - - /** - * Save the list of regions from a region manager. - * - *

This call will block.

- * - * @param manager The manager to load regions into - * @throws ProtectionDatabaseException when an error occurs - */ - public void save(RegionManager manager) throws ProtectionDatabaseException, RejectedExecutionException; - - /** - * Save the list of regions from a region manager, optionally to suggest - * that the data be saved in another thread without blocking the thread - * from which the call is made. - * - *

{@code async} is merely a suggestion and it may be ignored by - * implementations if it is not supported.

- * - * @param manager The manager to load regions into - * @param async true to attempt to save the data asynchronously if it is supported - * @throws RejectedExecutionException on rejection - */ - public ListenableFuture save(RegionManager manager, boolean async) throws RejectedExecutionException; - - /** - * Get a list of regions. - * - * @return the regions loaded by this ProtectionDatabase - */ - public Map getRegions(); - - /** - * Set the list of regions. - * - * @param regions The regions to be applied to this ProtectionDatabase - */ - public void setRegions(Map regions); - -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java b/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java index 158d6567..2fcf9d8b 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java +++ b/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java @@ -20,7 +20,7 @@ package com.sk89q.worldguard.protection.databases; import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.databases.util.DomainInputResolver; +import com.sk89q.worldguard.protection.util.DomainInputResolver; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java deleted file mode 100644 index 1d12bf29..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/AbstractDatabaseMigrator.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrator; - -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public abstract class AbstractDatabaseMigrator implements DatabaseMigrator { - - private static HashMap> migrators = - new HashMap>(); - - public static Map> getMigrators() { - if (!migrators.isEmpty()) return migrators; - - AbstractDatabaseMigrator.migrators.put(new MigratorKey("mysql", "yaml"), MySQLToYAMLMigrator.class); - AbstractDatabaseMigrator.migrators.put(new MigratorKey("yaml", "mysql"), YAMLToMySQLMigrator.class); - - return migrators; - } - - protected abstract Set getWorldsFromOld() throws MigrationException; - - protected abstract Map getRegionsForWorldFromOld(String world) throws MigrationException; - - protected abstract ProtectionDatabase getNewWorldStorage(String world) throws MigrationException; - - public void migrate() throws MigrationException { - for (String world : this.getWorldsFromOld()) { - ProtectionDatabase database = this.getNewWorldStorage(world); - database.setRegions(this.getRegionsForWorldFromOld(world)); - - try { - database.save(); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - } - } -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MySQLToYAMLMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MySQLToYAMLMigrator.java deleted file mode 100644 index bdfafa5a..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MySQLToYAMLMigrator.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrator; - -import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.databases.MySQLDatabase; -import com.sk89q.worldguard.protection.databases.YAMLDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.io.File; -import java.io.FileNotFoundException; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public class MySQLToYAMLMigrator extends AbstractDatabaseMigrator { - - private WorldGuardPlugin plugin; - private Set worlds; - - public MySQLToYAMLMigrator(WorldGuardPlugin plugin) throws MigrationException { - this.plugin = plugin; - this.worlds = new HashSet(); - - ConfigurationManager config = plugin.getGlobalStateManager(); - - try { - Connection conn = DriverManager.getConnection(config.sqlDsn, config.sqlUsername, config.sqlPassword); - - ResultSet worlds = conn.prepareStatement("SELECT `name` FROM `world`;").executeQuery(); - - while(worlds.next()) { - this.worlds.add(worlds.getString(1)); - } - - conn.close(); - } catch (SQLException e) { - throw new MigrationException(e); - } - } - - @Override - protected Set getWorldsFromOld() { - return this.worlds; - } - - @Override - protected Map getRegionsForWorldFromOld(String world) throws MigrationException { - ProtectionDatabase oldDatabase; - try { - oldDatabase = new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger()); - oldDatabase.load(); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - - return oldDatabase.getRegions(); - } - - @Override - protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException { - try { - File file = new File(plugin.getDataFolder(), - "worlds" + File.separator + world + File.separator + "regions.yml"); - - return new YAMLDatabase(file, plugin.getLogger()); - } catch (FileNotFoundException e) { - throw new MigrationException(e); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - } -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/YAMLToMySQLMigrator.java b/src/main/java/com/sk89q/worldguard/protection/databases/migrator/YAMLToMySQLMigrator.java deleted file mode 100644 index 2a1d879f..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/YAMLToMySQLMigrator.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.migrator; - -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.databases.MySQLDatabase; -import com.sk89q.worldguard.protection.databases.YAMLDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.io.File; -import java.io.FileNotFoundException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class YAMLToMySQLMigrator extends AbstractDatabaseMigrator { - - private WorldGuardPlugin plugin; - private HashMap regionYamlFiles; - - public YAMLToMySQLMigrator(WorldGuardPlugin plugin) { - this.plugin = plugin; - - this.regionYamlFiles = new HashMap(); - - File files[] = new File(plugin.getDataFolder(), "worlds" + File.separator).listFiles(); - for (File item : files) { - if (item.isDirectory()) { - for (File subItem : item.listFiles()) { - if (subItem.getName().equals("regions.yml")) { - this.regionYamlFiles.put(item.getName(), subItem); - } - } - } - } - } - - @Override - protected Set getWorldsFromOld() { - return this.regionYamlFiles.keySet(); - } - - @Override - protected Map getRegionsForWorldFromOld(String world) throws MigrationException { - ProtectionDatabase oldDatabase; - try { - oldDatabase = new YAMLDatabase(this.regionYamlFiles.get(world), plugin.getLogger()); - oldDatabase.load(); - } catch (FileNotFoundException e) { - throw new MigrationException(e); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - - return oldDatabase.getRegions(); - } - - @Override - protected ProtectionDatabase getNewWorldStorage(String world) throws MigrationException { - try { - return new MySQLDatabase(plugin.getGlobalStateManager(), world, plugin.getLogger()); - } catch (ProtectionDatabaseException e) { - throw new MigrationException(e); - } - } - -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/AbstractJob.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/AbstractJob.java deleted file mode 100644 index 944a3b7b..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/AbstractJob.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.mysql; - -import com.sk89q.worldguard.bukkit.ConfigurationManager; -import org.yaml.snakeyaml.error.YAMLException; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.logging.Logger; - -import static com.google.common.base.Preconditions.checkNotNull; - -@SuppressWarnings("ProtectedField") -abstract class AbstractJob { - - protected final MySQLDatabaseImpl database; - protected final ConfigurationManager config; - protected final Connection conn; - protected final Logger logger; - - protected AbstractJob(MySQLDatabaseImpl database, Connection conn) { - checkNotNull(database); - checkNotNull(conn); - this.database = database; - this.config = database.getConfiguration(); - this.conn = conn; - this.logger = database.getLogger(); - } - - static void closeQuietly(ResultSet rs) { - if (rs != null) { - try { - rs.close(); - } catch (SQLException ignored) {} - } - } - - static void closeQuietly(Statement st) { - if (st != null) { - try { - st.close(); - } catch (SQLException ignored) {} - } - } - - protected Object sqlUnmarshal(String rawValue) { - try { - return database.getYaml().load(rawValue); - } catch (YAMLException e) { - return String.valueOf(rawValue); - } - } - - protected String sqlMarshal(Object rawObject) { - return database.getYaml().dump(rawObject); - } - -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java deleted file mode 100644 index 3000f2af..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/MySQLDatabaseImpl.java +++ /dev/null @@ -1,305 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.mysql; - -import com.jolbox.bonecp.BoneCP; -import com.jolbox.bonecp.BoneCPConfig; -import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.protection.databases.AbstractAsynchronousDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.util.io.Closer; -import org.flywaydb.core.Flyway; -import org.flywaydb.core.api.FlywayException; -import org.flywaydb.core.api.MigrationVersion; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.DumperOptions.FlowStyle; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.SafeConstructor; -import org.yaml.snakeyaml.representer.Representer; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * For internal use. Do not subclass. - */ -public class MySQLDatabaseImpl extends AbstractAsynchronousDatabase { - - private final ConfigurationManager config; - private final Logger logger; - - private final BoneCP connectionPool; - private final Yaml yaml = createYaml(); - private final int worldId; - - private Map regions = new HashMap(); - - public MySQLDatabaseImpl(ConfigurationManager config, String worldName, Logger logger) throws ProtectionDatabaseException { - checkNotNull(config); - checkNotNull(worldName); - checkNotNull(logger); - - this.config = config; - this.logger = logger; - - BoneCPConfig poolConfig = new BoneCPConfig(); - poolConfig.setJdbcUrl(config.sqlDsn); - poolConfig.setUsername(config.sqlUsername); - poolConfig.setPassword(config.sqlPassword); - - try { - connectionPool = new BoneCP(poolConfig); - } catch (SQLException e) { - throw new ProtectionDatabaseException("Failed to connect to the database", e); - } - - try { - migrate(); - } catch (FlywayException e) { - throw new ProtectionDatabaseException("Failed to migrate tables", e); - } catch (SQLException e) { - throw new ProtectionDatabaseException("Failed to migrate tables", e); - } - - try { - worldId = chooseWorldId(worldName); - } catch (SQLException e) { - throw new ProtectionDatabaseException("Failed to choose the ID for this world", e); - } - } - - private boolean tryQuery(Connection conn, String sql) throws SQLException { - Closer closer = Closer.create(); - try { - Statement statement = closer.register(conn.createStatement()); - statement.executeQuery(sql); - return true; - } catch (SQLException ex) { - return false; - } finally { - closer.closeQuietly(); - } - } - - /** - * Migrate the tables to the latest version. - * - * @throws SQLException thrown if a connection can't be opened - * @throws ProtectionDatabaseException thrown on other error - */ - private void migrate() throws SQLException, ProtectionDatabaseException { - Closer closer = Closer.create(); - Connection conn = closer.register(getConnection()); - - // Check some tables - boolean tablesExist; - boolean isRecent; - boolean isBeforeMigrations; - boolean hasMigrations; - - try { - tablesExist = tryQuery(conn, "SELECT * FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 1"); - isRecent = tryQuery(conn, "SELECT `world_id` FROM `" + config.sqlTablePrefix + "region_cuboid` LIMIT 1"); - isBeforeMigrations = !tryQuery(conn, "SELECT `uuid` FROM `" + config.sqlTablePrefix + "user` LIMIT 1"); - hasMigrations = tryQuery(conn, "SELECT * FROM `" + config.sqlTablePrefix + "migrations` LIMIT 1"); - } finally { - closer.closeQuietly(); - } - - // We don't bother with migrating really old tables - if (tablesExist && !isRecent) { - throw new ProtectionDatabaseException( - "Sorry, your tables are too old for the region SQL auto-migration system. " + - "Please run region_manual_update_20110325.sql on your database, which comes " + - "with WorldGuard or can be found in http://github.com/sk89q/worldguard"); - } - - // Our placeholders - Map placeHolders = new HashMap(); - placeHolders.put("tablePrefix", config.sqlTablePrefix); - - BoneCPConfig boneConfig = connectionPool.getConfig(); - - Flyway flyway = new Flyway(); - - // The MySQL support predates the usage of Flyway, so let's do some - // checks and issue messages appropriately - if (!hasMigrations) { - flyway.setInitOnMigrate(true); - - if (tablesExist) { - // Detect if this is before migrations - if (isBeforeMigrations) { - flyway.setInitVersion(MigrationVersion.fromVersion("1")); - } - - logger.log(Level.INFO, "The MySQL region tables exist but the migrations table seems to not exist yet. Creating the migrations table..."); - } else { - // By default, if Flyway sees any tables at all in the schema, it - // will assume that we are up to date, so we have to manually - // check ourselves and then ask Flyway to start from the beginning - // if our test table doesn't exist - flyway.setInitVersion(MigrationVersion.fromVersion("0")); - - logger.log(Level.INFO, "MySQL region tables do not exist: creating..."); - } - } - - flyway.setClassLoader(getClass().getClassLoader()); - flyway.setLocations("migrations/region/mysql"); - flyway.setDataSource(boneConfig.getJdbcUrl(), boneConfig.getUser(), boneConfig.getPassword()); - flyway.setTable(config.sqlTablePrefix + "migrations"); - flyway.setPlaceholders(placeHolders); - flyway.setValidateOnMigrate(false); - flyway.migrate(); - } - - /** - * Get the ID for this world from the database or pick a new one if - * an entry does not exist yet. - * - * @param worldName the world name - * @return a world ID - * @throws SQLException on a database access error - */ - private int chooseWorldId(String worldName) throws SQLException { - Closer closer = Closer.create(); - try { - Connection conn = closer.register(getConnection()); - PreparedStatement worldStmt = closer.register(conn.prepareStatement( - "SELECT `id` FROM `" + config.sqlTablePrefix + "world` WHERE `name` = ? LIMIT 0, 1" - )); - - worldStmt.setString(1, worldName); - ResultSet worldResult = closer.register(worldStmt.executeQuery()); - - if (worldResult.first()) { - return worldResult.getInt("id"); - } else { - PreparedStatement insertWorldStatement = closer.register(conn.prepareStatement( - "INSERT INTO " + - "`" + config.sqlTablePrefix + "world` " + - "(`id`, `name`) VALUES (null, ?)", - Statement.RETURN_GENERATED_KEYS - )); - - insertWorldStatement.setString(1, worldName); - insertWorldStatement.execute(); - ResultSet generatedKeys = insertWorldStatement.getGeneratedKeys(); - - if (generatedKeys.first()) { - return generatedKeys.getInt(1); - } else { - throw new SQLException("Expected result, got none"); - } - } - } finally { - closer.closeQuietly(); - } - } - - private static Yaml createYaml() { - DumperOptions options = new DumperOptions(); - options.setIndent(2); - options.setDefaultFlowStyle(FlowStyle.FLOW); - Representer representer = new Representer(); - representer.setDefaultFlowStyle(FlowStyle.FLOW); - - // We have to use this in order to properly save non-string values - return new Yaml(new SafeConstructor(), new Representer(), options); - } - - ConfigurationManager getConfiguration() { - return this.config; - } - - Logger getLogger() { - return logger; - } - - Yaml getYaml() { - return yaml; - } - - int getWorldId() { - return worldId; - } - - private Connection getConnection() throws SQLException { - return connectionPool.getConnection(); - } - - @Override - public Map getRegions() { - return regions; - } - - @Override - public void setRegions(Map regions) { - this.regions = regions; - } - - @Override - protected void performLoad() throws ProtectionDatabaseException { - Connection connection = null; - try { - connection = getConnection(); - setRegions(new RegionLoader(this, connection).load()); - } catch (SQLException e) { - throw new ProtectionDatabaseException("Failed to load regions database", e); - } finally { - if (connection != null) { - try { - connection.close(); - } catch (SQLException ignored) { - } - } - } - } - - @Override - protected void performSave() throws ProtectionDatabaseException { - Connection connection = null; - try { - connection = getConnection(); - new RegionWriter(this, connection, getRegions()).save(); - } catch (SQLException e) { - throw new ProtectionDatabaseException("Failed to save regions database", e); - } finally { - if (connection != null) { - try { - connection.close(); - } catch (SQLException ignored) { - } - } - } - } - -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionLoader.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionLoader.java deleted file mode 100644 index e4793837..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionLoader.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.mysql; - -import com.sk89q.worldedit.BlockVector; -import com.sk89q.worldedit.BlockVector2D; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.flags.Flag; -import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; -import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -class RegionLoader extends AbstractJob { - - /* - ========= Everything below is a nightmare. ========= - */ - - private final int worldId; - - private Map cuboidRegions; - private Map poly2dRegions; - private Map globalRegions; - private Map parentSets; - - RegionLoader(MySQLDatabaseImpl database, Connection conn) { - super(database, conn); - this.worldId = database.getWorldId(); - } - - public Map load() { - parentSets = new HashMap(); - - // We load the cuboid regions first, as this is likely to be the - // largest dataset. This should save time in regards to the putAll()s - this.loadCuboid(); - Map regions = this.cuboidRegions; - this.cuboidRegions = null; - - this.loadPoly2d(); - regions.putAll(this.poly2dRegions); - this.poly2dRegions = null; - - this.loadGlobal(); - regions.putAll(this.globalRegions); - this.globalRegions = null; - - // Relink parents // Taken verbatim from YAMLDatabase - for (Map.Entry entry : parentSets.entrySet()) { - ProtectedRegion parent = regions.get(entry.getValue()); - if (parent != null) { - try { - entry.getKey().setParent(parent); - } catch (CircularInheritanceException e) { - logger.warning("Circular inheritance detect with '" - + entry.getValue() + "' detected as a parent"); - } - } else { - logger.warning("Unknown region parent: " + entry.getValue()); - } - } - - return regions; - } - - private void loadFlags(ProtectedRegion region) { - // @TODO: Iterate _ONCE_ - PreparedStatement flagsStatement = null; - ResultSet flagsResultSet = null; - try { - flagsStatement = this.conn.prepareStatement( - "SELECT " + - "`region_flag`.`flag`, " + - "`region_flag`.`value` " + - "FROM `" + config.sqlTablePrefix + "region_flag` AS `region_flag` " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldId - ); - - flagsStatement.setString(1, region.getId().toLowerCase()); - flagsResultSet = flagsStatement.executeQuery(); - - Map regionFlags = new HashMap(); - while (flagsResultSet.next()) { - regionFlags.put( - flagsResultSet.getString("flag"), - sqlUnmarshal(flagsResultSet.getString("value")) - ); - } - - // @TODO: Make this better - for (Flag flag : DefaultFlag.getFlags()) { - Object o = regionFlags.get(flag.getName()); - if (o != null) { - setFlag(region, flag, o); - } - } - } catch (SQLException ex) { - logger.warning( - "Unable to load flags for region " - + region.getId().toLowerCase() + ": " + ex.getMessage() - ); - } finally { - closeQuietly(flagsResultSet); - closeQuietly(flagsStatement); - } - } - - private void setFlag(ProtectedRegion region, Flag flag, Object rawValue) { - T val = flag.unmarshal(rawValue); - if (val == null) { - logger.warning("Failed to parse flag '" + flag.getName() + "' with value '" + rawValue + "'"); - return; - } - region.setFlag(flag, val); - } - - private void loadOwnersAndMembers(ProtectedRegion region) { - DefaultDomain owners = new DefaultDomain(); - DefaultDomain members = new DefaultDomain(); - - ResultSet userSet = null; - PreparedStatement usersStatement = null; - try { - usersStatement = this.conn.prepareStatement( - "SELECT " + - "`user`.`name`, `user`.`uuid`, " + - "`region_players`.`owner` " + - "FROM `" + config.sqlTablePrefix + "region_players` AS `region_players` " + - "LEFT JOIN `" + config.sqlTablePrefix + "user` AS `user` ON ( " + - "`region_players`.`user_id` = " + - "`user`.`id`) " + - "WHERE `region_players`.`region_id` = ? " + - "AND `region_players`.`world_id` = " + this.worldId - ); - - usersStatement.setString(1, region.getId().toLowerCase()); - userSet = usersStatement.executeQuery(); - while (userSet.next()) { - DefaultDomain domain; - if (userSet.getBoolean("owner")) { - domain = owners; - } else { - domain = members; - } - - String name = userSet.getString("name"); - String uuid = userSet.getString("uuid"); - if (name != null) { - domain.addPlayer(name); - } else if (uuid != null) { - try { - domain.addPlayer(UUID.fromString(uuid)); - } catch (IllegalArgumentException e) { - logger.warning("Invalid UUID in database: " + uuid); - } - } - } - } catch (SQLException ex) { - logger.warning("Unable to load users for region " + region.getId().toLowerCase() + ": " + ex.getMessage()); - } finally { - closeQuietly(userSet); - closeQuietly(usersStatement); - } - - PreparedStatement groupsStatement = null; - ResultSet groupSet = null; - try { - groupsStatement = this.conn.prepareStatement( - "SELECT " + - "`group`.`name`, " + - "`region_groups`.`owner` " + - "FROM `" + config.sqlTablePrefix + "region_groups` AS `region_groups` " + - "LEFT JOIN `" + config.sqlTablePrefix + "group` AS `group` ON ( " + - "`region_groups`.`group_id` = " + - "`group`.`id`) " + - "WHERE `region_groups`.`region_id` = ? " + - "AND `region_groups`.`world_id` = " + this.worldId - ); - - groupsStatement.setString(1, region.getId().toLowerCase()); - groupSet = groupsStatement.executeQuery(); - while (groupSet.next()) { - if (groupSet.getBoolean("owner")) { - owners.addGroup(groupSet.getString("name")); - } else { - members.addGroup(groupSet.getString("name")); - } - } - } catch (SQLException ex) { - logger.warning("Unable to load groups for region " + region.getId().toLowerCase() + ": " + ex.getMessage()); - } finally { - closeQuietly(groupSet); - closeQuietly(groupsStatement); - } - - region.setOwners(owners); - region.setMembers(members); - } - - private void loadGlobal() { - Map regions = - new HashMap(); - - PreparedStatement globalRegionStatement = null; - ResultSet globalResultSet = null; - try { - globalRegionStatement = this.conn.prepareStatement( - "SELECT " + - "`region`.`id`, " + - "`region`.`priority`, " + - "`parent`.`id` AS `parent` " + - "FROM `" + config.sqlTablePrefix + "region` AS `region` " + - "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " + - "ON (`region`.`parent` = `parent`.`id` " + - "AND `region`.`world_id` = `parent`.`world_id`) " + - "WHERE `region`.`type` = 'global' " + - "AND `region`.`world_id` = ? " - ); - - globalRegionStatement.setInt(1, this.worldId); - globalResultSet = globalRegionStatement.executeQuery(); - - while (globalResultSet.next()) { - ProtectedRegion region = new GlobalProtectedRegion(globalResultSet.getString("id")); - - region.setPriority(globalResultSet.getInt("priority")); - - this.loadFlags(region); - this.loadOwnersAndMembers(region); - - regions.put(globalResultSet.getString("id"), region); - - String parentId = globalResultSet.getString("parent"); - if (parentId != null) { - parentSets.put(region, parentId); - } - } - } catch (SQLException ex) { - ex.printStackTrace(); - logger.warning("Unable to load regions from sql database: " + ex.getMessage()); - Throwable t = ex.getCause(); - while (t != null) { - logger.warning("\t\tCause: " + t.getMessage()); - t = t.getCause(); - } - } finally { - closeQuietly(globalResultSet); - closeQuietly(globalRegionStatement); - } - - globalRegions = regions; - } - - private void loadCuboid() { - Map regions = new HashMap(); - - PreparedStatement cuboidRegionStatement = null; - ResultSet cuboidResultSet = null; - try { - cuboidRegionStatement = this.conn.prepareStatement( - "SELECT " + - "`region_cuboid`.`min_z`, " + - "`region_cuboid`.`min_y`, " + - "`region_cuboid`.`min_x`, " + - "`region_cuboid`.`max_z`, " + - "`region_cuboid`.`max_y`, " + - "`region_cuboid`.`max_x`, " + - "`region`.`id`, " + - "`region`.`priority`, " + - "`parent`.`id` AS `parent` " + - "FROM `" + config.sqlTablePrefix + "region_cuboid` AS `region_cuboid` " + - "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `region` " + - "ON (`region_cuboid`.`region_id` = `region`.`id` " + - "AND `region_cuboid`.`world_id` = `region`.`world_id`) " + - "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " + - "ON (`region`.`parent` = `parent`.`id` " + - "AND `region`.`world_id` = `parent`.`world_id`) " + - "WHERE `region`.`world_id` = ? " - ); - - cuboidRegionStatement.setInt(1, this.worldId); - cuboidResultSet = cuboidRegionStatement.executeQuery(); - - while (cuboidResultSet.next()) { - Vector pt1 = new Vector( - cuboidResultSet.getInt("min_x"), - cuboidResultSet.getInt("min_y"), - cuboidResultSet.getInt("min_z") - ); - Vector pt2 = new Vector( - cuboidResultSet.getInt("max_x"), - cuboidResultSet.getInt("max_y"), - cuboidResultSet.getInt("max_z") - ); - - BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector(); - BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector(); - ProtectedRegion region = new ProtectedCuboidRegion( - cuboidResultSet.getString("id"), - min, - max - ); - - region.setPriority(cuboidResultSet.getInt("priority")); - - this.loadFlags(region); - this.loadOwnersAndMembers(region); - - regions.put(cuboidResultSet.getString("id"), region); - - String parentId = cuboidResultSet.getString("parent"); - if (parentId != null) { - parentSets.put(region, parentId); - } - } - - } catch (SQLException ex) { - ex.printStackTrace(); - logger.warning("Unable to load regions from sql database: " + ex.getMessage()); - Throwable t = ex.getCause(); - while (t != null) { - logger.warning("\t\tCause: " + t.getMessage()); - t = t.getCause(); - } - } finally { - closeQuietly(cuboidResultSet); - closeQuietly(cuboidRegionStatement); - } - - cuboidRegions = regions; - } - - private void loadPoly2d() { - Map regions = new HashMap(); - - PreparedStatement poly2dRegionStatement = null; - ResultSet poly2dResultSet = null; - PreparedStatement poly2dVectorStatement = null; - try { - poly2dRegionStatement = this.conn.prepareStatement( - "SELECT " + - "`region_poly2d`.`min_y`, " + - "`region_poly2d`.`max_y`, " + - "`region`.`id`, " + - "`region`.`priority`, " + - "`parent`.`id` AS `parent` " + - "FROM `" + config.sqlTablePrefix + "region_poly2d` AS `region_poly2d` " + - "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `region` " + - "ON (`region_poly2d`.`region_id` = `region`.`id` " + - "AND `region_poly2d`.`world_id` = `region`.`world_id`) " + - "LEFT JOIN `" + config.sqlTablePrefix + "region` AS `parent` " + - "ON (`region`.`parent` = `parent`.`id` " + - "AND `region`.`world_id` = `parent`.`world_id`) " + - "WHERE `region`.`world_id` = ? " - ); - - poly2dRegionStatement.setInt(1, this.worldId); - poly2dResultSet = poly2dRegionStatement.executeQuery(); - - poly2dVectorStatement = this.conn.prepareStatement( - "SELECT " + - "`region_poly2d_point`.`x`, " + - "`region_poly2d_point`.`z` " + - "FROM `" + config.sqlTablePrefix + "region_poly2d_point` AS `region_poly2d_point` " + - "WHERE `region_poly2d_point`.`region_id` = ? " + - "AND `region_poly2d_point`.`world_id` = " + this.worldId - ); - - while (poly2dResultSet.next()) { - String id = poly2dResultSet.getString("id"); - - Integer minY = poly2dResultSet.getInt("min_y"); - Integer maxY = poly2dResultSet.getInt("max_y"); - List points = new ArrayList(); - - poly2dVectorStatement.setString(1, id); - ResultSet poly2dVectorResultSet = poly2dVectorStatement.executeQuery(); - - while (poly2dVectorResultSet.next()) { - points.add(new BlockVector2D( - poly2dVectorResultSet.getInt("x"), - poly2dVectorResultSet.getInt("z") - )); - } - - if (points.size() < 3) { - logger.warning(String.format("Invalid polygonal region '%s': region only has %d point(s). Ignoring.", id, points.size())); - continue; - } - - closeQuietly(poly2dVectorResultSet); - - ProtectedRegion region = new ProtectedPolygonalRegion(id, points, minY, maxY); - - region.setPriority(poly2dResultSet.getInt("priority")); - - this.loadFlags(region); - this.loadOwnersAndMembers(region); - - regions.put(poly2dResultSet.getString("id"), region); - - String parentId = poly2dResultSet.getString("parent"); - if (parentId != null) { - parentSets.put(region, parentId); - } - } - } catch (SQLException ex) { - ex.printStackTrace(); - logger.warning("Unable to load regions from sql database: " + ex.getMessage()); - Throwable t = ex.getCause(); - while (t != null) { - logger.warning("\t\tCause: " + t.getMessage()); - t = t.getCause(); - } - } finally { - closeQuietly(poly2dResultSet); - closeQuietly(poly2dRegionStatement); - closeQuietly(poly2dVectorStatement); - } - - poly2dRegions = regions; - } - -} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionWriter.java b/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionWriter.java deleted file mode 100644 index c3294638..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/RegionWriter.java +++ /dev/null @@ -1,608 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases.mysql; - -import com.sk89q.worldedit.BlockVector; -import com.sk89q.worldedit.BlockVector2D; -import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.internal.util.sql.StatementUtils; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; -import com.sk89q.worldguard.protection.flags.Flag; -import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; -import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.logging.Level; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.worldguard.protection.databases.mysql.UserRowCache.NameRowCache; -import static com.sk89q.worldguard.protection.databases.mysql.UserRowCache.UUIDRowCache; - -class RegionWriter extends AbstractJob { - - /* - ========= Everything below is a nightmare. ========= - */ - - private final Map regions; - private final NameRowCache usernameCache; - private final UUIDRowCache uuidCache; - private final int worldId; - - RegionWriter(MySQLDatabaseImpl database, Connection conn, Map regions) { - super(database, conn); - checkNotNull(regions); - this.regions = regions; - this.worldId = database.getWorldId(); - usernameCache = new NameRowCache(database, conn); - uuidCache = new UUIDRowCache(database, conn); - } - - public void save() throws SQLException, ProtectionDatabaseException { - - /* - * As we don't get notified on the creation/removal of regions: - * 1) We get a list of all of the in-database regions - * 2) We iterate over all of the in-memory regions - * 2a) If the region is in the database, we update the database and - * remove the region from the in-database list - * b) If the region is not in the database, we insert it - * 3) We iterate over what remains of the in-database list and remove - * them from the database - * - * TODO: Look at adding/removing/updating the database when the in - * memory region is created/remove/updated - * - * @see com.sk89q.worldguard.protection.databases.ProtectionDatabase#save() - */ - - List regionsInDatabase = new ArrayList(); - - PreparedStatement getAllRegionsStatement = null; - ResultSet getAllRegionsResult = null; - try { - getAllRegionsStatement = this.conn.prepareStatement( - "SELECT `region`.`id` FROM " + - "`" + config.sqlTablePrefix + "region` AS `region` " + - "WHERE `world_id` = ? " - ); - - getAllRegionsStatement.setInt(1, this.worldId); - getAllRegionsResult = getAllRegionsStatement.executeQuery(); - - while(getAllRegionsResult.next()) { - regionsInDatabase.add(getAllRegionsResult.getString("id")); - } - } catch (SQLException ex) { - logger.warning("Could not get region list for save comparison: " + ex.getMessage()); - } finally { - closeQuietly(getAllRegionsResult); - closeQuietly(getAllRegionsStatement); - } - - for (Map.Entry entry : regions.entrySet()) { - String name = entry.getKey(); - ProtectedRegion region = entry.getValue(); - - try { - if (regionsInDatabase.contains(name)) { - regionsInDatabase.remove(name); - - if (region instanceof ProtectedCuboidRegion) { - updateRegionCuboid( (ProtectedCuboidRegion) region ); - } else if (region instanceof ProtectedPolygonalRegion) { - updateRegionPoly2D( (ProtectedPolygonalRegion) region ); - } else if (region instanceof GlobalProtectedRegion) { - updateRegionGlobal( (GlobalProtectedRegion) region ); - } else { - this.updateRegion(region, region.getClass().getCanonicalName()); - } - } else { - if (region instanceof ProtectedCuboidRegion) { - insertRegionCuboid( (ProtectedCuboidRegion) region ); - } else if (region instanceof ProtectedPolygonalRegion) { - insertRegionPoly2D( (ProtectedPolygonalRegion) region ); - } else if (region instanceof GlobalProtectedRegion) { - insertRegionGlobal( (GlobalProtectedRegion) region ); - } else { - this.insertRegion(region, region.getClass().getCanonicalName()); - } - } - } catch (SQLException ex) { - logger.warning("Could not save region " + region.getId().toLowerCase() + ": " + ex.getMessage()); - throw new ProtectionDatabaseException(ex); - } - } - - for (Map.Entry entry : regions.entrySet()) { - PreparedStatement setParentStatement = null; - try { - if (entry.getValue().getParent() == null) continue; - - setParentStatement = this.conn.prepareStatement( - "UPDATE `" + config.sqlTablePrefix + "region` SET " + - "`parent` = ? " + - "WHERE `id` = ? AND `world_id` = " + this.worldId - ); - - setParentStatement.setString(1, entry.getValue().getParent().getId().toLowerCase()); - setParentStatement.setString(2, entry.getValue().getId().toLowerCase()); - - setParentStatement.execute(); - } catch (SQLException ex) { - logger.warning("Could not save region parents " + entry.getValue().getId().toLowerCase() + ": " + ex.getMessage()); - throw new ProtectionDatabaseException(ex); - } finally { - closeQuietly(setParentStatement); - } - } - - for (String name : regionsInDatabase) { - PreparedStatement removeRegion = null; - try { - removeRegion = this.conn.prepareStatement( - "DELETE FROM `" + config.sqlTablePrefix + "region` WHERE `id` = ? " - ); - - removeRegion.setString(1, name); - removeRegion.execute(); - } catch (SQLException ex) { - logger.warning("Could not remove region from database " + name + ": " + ex.getMessage()); - } finally { - closeQuietly(removeRegion); - } - } - } - - /* - * Returns the database id for the groups - * If it doesn't exits it adds the group and returns the id. - */ - private Map getGroupIds(String... groupnames) { - Map groups = new HashMap(); - - if (groupnames.length < 1) return groups; - - PreparedStatement findGroupsStatement = null; - ResultSet findGroupsResults = null; - PreparedStatement insertGroupStatement = null; - try { - findGroupsStatement = this.conn.prepareStatement( - String.format( - "SELECT " + - "`group`.`id`, " + - "`group`.`name` " + - "FROM `" + config.sqlTablePrefix + "group` AS `group` " + - "WHERE `name` IN (%s)", - StatementUtils.preparePlaceHolders(groupnames.length) - ) - ); - - StatementUtils.setValues(findGroupsStatement, groupnames); - - findGroupsResults = findGroupsStatement.executeQuery(); - - while(findGroupsResults.next()) { - groups.put(findGroupsResults.getString("name"), findGroupsResults.getInt("id")); - } - - insertGroupStatement = this.conn.prepareStatement( - "INSERT INTO " + - "`" + config.sqlTablePrefix + "group` ( " + - "`id`, " + - "`name`" + - ") VALUES (null, ?)", - Statement.RETURN_GENERATED_KEYS - ); - - for (String groupname : groupnames) { - if (!groups.containsKey(groupname)) { - insertGroupStatement.setString(1, groupname); - insertGroupStatement.execute(); - ResultSet generatedKeys = insertGroupStatement.getGeneratedKeys(); - if (generatedKeys.first()) { - groups.put(groupname, generatedKeys.getInt(1)); - } else { - logger.warning("Could not get the database id for user " + groupname); - } - } - } - } catch (SQLException ex) { - logger.warning("Could not get the database id for the groups " + groupnames.toString() + ex.getMessage()); - } finally { - closeQuietly(findGroupsResults); - closeQuietly(findGroupsStatement); - closeQuietly(insertGroupStatement); - } - - return groups; - } - - private void updateFlags(ProtectedRegion region) throws SQLException { - PreparedStatement clearCurrentFlagStatement = null; - try { - clearCurrentFlagStatement = this.conn.prepareStatement( - "DELETE FROM `" + config.sqlTablePrefix + "region_flag` " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldId - ); - - clearCurrentFlagStatement.setString(1, region.getId().toLowerCase()); - clearCurrentFlagStatement.execute(); - - for (Map.Entry, Object> entry : region.getFlags().entrySet()) { - if (entry.getValue() == null) continue; - - Object flag = sqlMarshal(marshalFlag(entry.getKey(), entry.getValue())); - - PreparedStatement insertFlagStatement = null; - try { - insertFlagStatement = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_flag` ( " + - "`id`, " + - "`region_id`, " + - "`world_id`, " + - "`flag`, " + - "`value` " + - ") VALUES (null, ?, " + this.worldId + ", ?, ?)" - ); - - insertFlagStatement.setString(1, region.getId().toLowerCase()); - insertFlagStatement.setString(2, entry.getKey().getName()); - insertFlagStatement.setObject(3, flag); - - insertFlagStatement.execute(); - } finally { - closeQuietly(insertFlagStatement); - } - } - } finally { - closeQuietly(clearCurrentFlagStatement); - } - } - - private void updatePlayerAndGroups(ProtectedRegion region, Boolean owners) throws SQLException { - DefaultDomain domain; - - if (owners) { - domain = region.getOwners(); - } else { - domain = region.getMembers(); - } - - PreparedStatement deleteUsersForRegion = null; - PreparedStatement insertUsersForRegion = null; - PreparedStatement deleteGroupsForRegion = null; - PreparedStatement insertGroupsForRegion = null; - - try { - deleteUsersForRegion = this.conn.prepareStatement( - "DELETE FROM `" + config.sqlTablePrefix + "region_players` " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldId + " " + - "AND `owner` = ?" - ); - - deleteUsersForRegion.setString(1, region.getId().toLowerCase()); - deleteUsersForRegion.setBoolean(2, owners); - deleteUsersForRegion.execute(); - - insertUsersForRegion = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_players` " + - "(`region_id`, `world_id`, `user_id`, `owner`) " + - "VALUES (?, " + this.worldId + ", ?, ?)" - ); - - // Map players to IDs - usernameCache.fetch(domain.getPlayers()); - uuidCache.fetch(domain.getUniqueIds()); - - for (String name : domain.getPlayers()) { - Integer id = usernameCache.find(name); - if (id != null) { - insertUsersForRegion.setString(1, region.getId().toLowerCase()); - insertUsersForRegion.setInt(2, id); - insertUsersForRegion.setBoolean(3, owners); - insertUsersForRegion.execute(); - } else { - logger.log(Level.WARNING, "Did not find an ID for the user identified as '" + name + "'"); - } - } - - for (UUID uuid : domain.getUniqueIds()) { - Integer id = uuidCache.find(uuid); - if (id != null) { - insertUsersForRegion.setString(1, region.getId().toLowerCase()); - insertUsersForRegion.setInt(2, id); - insertUsersForRegion.setBoolean(3, owners); - insertUsersForRegion.execute(); - } else { - logger.log(Level.WARNING, "Did not find an ID for the user identified by '" + uuid + "'"); - } - } - - deleteGroupsForRegion = this.conn.prepareStatement( - "DELETE FROM `" + config.sqlTablePrefix + "region_groups` " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldId + " " + - "AND `owner` = ?" - ); - - deleteGroupsForRegion.setString(1, region.getId().toLowerCase()); - deleteGroupsForRegion.setBoolean(2, owners); - deleteGroupsForRegion.execute(); - - insertGroupsForRegion = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_groups` " + - "(`region_id`, `world_id`, `group_id`, `owner`) " + - "VALUES (?, " + this.worldId + ", ?, ?)" - ); - - Set groupVar = domain.getGroups(); - for (Integer group : getGroupIds(groupVar.toArray(new String[groupVar.size()])).values()) { - insertGroupsForRegion.setString(1, region.getId().toLowerCase()); - insertGroupsForRegion.setInt(2, group); - insertGroupsForRegion.setBoolean(3, owners); - - insertGroupsForRegion.execute(); - } - } finally { - closeQuietly(deleteGroupsForRegion); - closeQuietly(deleteUsersForRegion); - closeQuietly(insertGroupsForRegion); - closeQuietly(insertUsersForRegion); - } - } - - @SuppressWarnings("unchecked") - private Object marshalFlag(Flag flag, Object val) { - return flag.marshal( (V) val ); - } - - private void insertRegion(ProtectedRegion region, String type) throws SQLException { - PreparedStatement insertRegionStatement = null; - try { - insertRegionStatement = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region` (" + - "`id`, " + - "`world_id`, " + - "`type`, " + - "`priority`, " + - "`parent` " + - ") VALUES (?, ?, ?, ?, null)" - ); - - insertRegionStatement.setString(1, region.getId().toLowerCase()); - insertRegionStatement.setInt(2, this.worldId); - insertRegionStatement.setString(3, type); - insertRegionStatement.setInt(4, region.getPriority()); - - insertRegionStatement.execute(); - } finally { - closeQuietly(insertRegionStatement); - } - - updateFlags(region); - - updatePlayerAndGroups(region, false); - updatePlayerAndGroups(region, true); - } - - private void insertRegionCuboid(ProtectedCuboidRegion region) throws SQLException { - insertRegion(region, "cuboid"); - - PreparedStatement insertCuboidRegionStatement = null; - try { - insertCuboidRegionStatement = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_cuboid` (" + - "`region_id`, " + - "`world_id`, " + - "`min_z`, " + - "`min_y`, " + - "`min_x`, " + - "`max_z`, " + - "`max_y`, " + - "`max_x` " + - ") VALUES (?, " + this.worldId + ", ?, ?, ?, ?, ?, ?)" - ); - - BlockVector min = region.getMinimumPoint(); - BlockVector max = region.getMaximumPoint(); - - insertCuboidRegionStatement.setString(1, region.getId().toLowerCase()); - insertCuboidRegionStatement.setInt(2, min.getBlockZ()); - insertCuboidRegionStatement.setInt(3, min.getBlockY()); - insertCuboidRegionStatement.setInt(4, min.getBlockX()); - insertCuboidRegionStatement.setInt(5, max.getBlockZ()); - insertCuboidRegionStatement.setInt(6, max.getBlockY()); - insertCuboidRegionStatement.setInt(7, max.getBlockX()); - - insertCuboidRegionStatement.execute(); - } finally { - closeQuietly(insertCuboidRegionStatement); - } - } - - private void insertRegionPoly2D(ProtectedPolygonalRegion region) throws SQLException { - insertRegion(region, "poly2d"); - - PreparedStatement insertPoly2dRegionStatement = null; - try { - insertPoly2dRegionStatement = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_poly2d` (" + - "`region_id`, " + - "`world_id`, " + - "`max_y`, " + - "`min_y` " + - ") VALUES (?, " + this.worldId + ", ?, ?)" - ); - - insertPoly2dRegionStatement.setString(1, region.getId().toLowerCase()); - insertPoly2dRegionStatement.setInt(2, region.getMaximumPoint().getBlockY()); - insertPoly2dRegionStatement.setInt(3, region.getMinimumPoint().getBlockY()); - - insertPoly2dRegionStatement.execute(); - } finally { - closeQuietly(insertPoly2dRegionStatement); - } - - updatePoly2dPoints(region); - } - - private void updatePoly2dPoints(ProtectedPolygonalRegion region) throws SQLException { - PreparedStatement clearPoly2dPointsForRegionStatement = null; - PreparedStatement insertPoly2dPointStatement = null; - - try { - clearPoly2dPointsForRegionStatement = this.conn.prepareStatement( - "DELETE FROM `" + config.sqlTablePrefix + "region_poly2d_point` " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldId - ); - - clearPoly2dPointsForRegionStatement.setString(1, region.getId().toLowerCase()); - - clearPoly2dPointsForRegionStatement.execute(); - - insertPoly2dPointStatement = this.conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "region_poly2d_point` (" + - "`id`, " + - "`region_id`, " + - "`world_id`, " + - "`z`, " + - "`x` " + - ") VALUES (null, ?, " + this.worldId + ", ?, ?)" - ); - - String lowerId = region.getId().toLowerCase(); - for (BlockVector2D point : region.getPoints()) { - insertPoly2dPointStatement.setString(1, lowerId); - insertPoly2dPointStatement.setInt(2, point.getBlockZ()); - insertPoly2dPointStatement.setInt(3, point.getBlockX()); - - insertPoly2dPointStatement.execute(); - } - } finally { - closeQuietly(clearPoly2dPointsForRegionStatement); - closeQuietly(insertPoly2dPointStatement); - } - } - - private void insertRegionGlobal(GlobalProtectedRegion region) throws SQLException { - insertRegion(region, "global"); - } - - private void updateRegion(ProtectedRegion region, String type) throws SQLException { - PreparedStatement updateRegionStatement = null; - try { - updateRegionStatement = this.conn.prepareStatement( - "UPDATE `" + config.sqlTablePrefix + "region` SET " + - "`priority` = ? WHERE `id` = ? AND `world_id` = " + this.worldId - ); - - updateRegionStatement.setInt(1, region.getPriority()); - updateRegionStatement.setString(2, region.getId().toLowerCase()); - - updateRegionStatement.execute(); - } finally { - closeQuietly(updateRegionStatement); - } - - updateFlags(region); - - updatePlayerAndGroups(region, false); - updatePlayerAndGroups(region, true); - } - - private void updateRegionCuboid(ProtectedCuboidRegion region) throws SQLException { - updateRegion(region, "cuboid"); - - PreparedStatement updateCuboidRegionStatement = null; - try { - updateCuboidRegionStatement = this.conn.prepareStatement( - "UPDATE `" + config.sqlTablePrefix + "region_cuboid` SET " + - "`min_z` = ?, " + - "`min_y` = ?, " + - "`min_x` = ?, " + - "`max_z` = ?, " + - "`max_y` = ?, " + - "`max_x` = ? " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldId - ); - - BlockVector min = region.getMinimumPoint(); - BlockVector max = region.getMaximumPoint(); - - updateCuboidRegionStatement.setInt(1, min.getBlockZ()); - updateCuboidRegionStatement.setInt(2, min.getBlockY()); - updateCuboidRegionStatement.setInt(3, min.getBlockX()); - updateCuboidRegionStatement.setInt(4, max.getBlockZ()); - updateCuboidRegionStatement.setInt(5, max.getBlockY()); - updateCuboidRegionStatement.setInt(6, max.getBlockX()); - updateCuboidRegionStatement.setString(7, region.getId().toLowerCase()); - - updateCuboidRegionStatement.execute(); - } finally { - closeQuietly(updateCuboidRegionStatement); - } - } - - private void updateRegionPoly2D(ProtectedPolygonalRegion region) throws SQLException { - updateRegion(region, "poly2d"); - - PreparedStatement updatePoly2dRegionStatement = null; - try { - updatePoly2dRegionStatement = this.conn.prepareStatement( - "UPDATE `" + config.sqlTablePrefix + "region_poly2d` SET " + - "`max_y` = ?, " + - "`min_y` = ? " + - "WHERE `region_id` = ? " + - "AND `world_id` = " + this.worldId - ); - - updatePoly2dRegionStatement.setInt(1, region.getMaximumPoint().getBlockY()); - updatePoly2dRegionStatement.setInt(2, region.getMinimumPoint().getBlockY()); - updatePoly2dRegionStatement.setString(3, region.getId().toLowerCase()); - - updatePoly2dRegionStatement.execute(); - } finally { - closeQuietly(updatePoly2dRegionStatement); - } - updatePoly2dPoints(region); - } - - private void updateRegionGlobal(GlobalProtectedRegion region) throws SQLException { - updateRegion(region, "global"); - } - -} diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/EnumFlag.java b/src/main/java/com/sk89q/worldguard/protection/flags/EnumFlag.java index d4cff48d..b1633ad4 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/EnumFlag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/EnumFlag.java @@ -41,6 +41,15 @@ public EnumFlag(String name, Class enumClass) { this.enumClass = enumClass; } + /** + * Get the enum class. + * + * @return the enum class + */ + public Class getEnumClass() { + return enumClass; + } + private T findValue(String input) throws IllegalArgumentException { if (input != null) { input = input.toUpperCase(); diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java b/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java index 6ecf5bf0..f3ee58c6 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java @@ -23,6 +23,8 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import javax.annotation.Nullable; + /** * * @author sk89q @@ -56,7 +58,14 @@ public RegionGroupFlag getRegionGroupFlag() { public abstract T parseInput(WorldGuardPlugin plugin, CommandSender sender, String input) throws InvalidFlagFormat; - public abstract T unmarshal(Object o); + public abstract T unmarshal(@Nullable Object o); public abstract Object marshal(T o); + + @Override + public String toString() { + return getClass().getName() + "{" + + "name='" + name + '\'' + + '}'; + } } diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java b/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java index 8003bd76..772eb529 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java @@ -23,6 +23,8 @@ import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import javax.annotation.Nullable; + /** * * @author sk89q @@ -85,8 +87,7 @@ public static boolean isMember(ProtectedRegion region, RegionGroup group, LocalP return false; } - public static boolean isMember(ApplicableRegionSet set, - RegionGroup group, LocalPlayer player) { + public static boolean isMember(ApplicableRegionSet set, @Nullable RegionGroup group, LocalPlayer player) { if (group == null || group == RegionGroup.ALL) { return true; } else if (group == RegionGroup.OWNERS) { diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/SetFlag.java b/src/main/java/com/sk89q/worldguard/protection/flags/SetFlag.java index 6d2d5639..adb44706 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/SetFlag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/SetFlag.java @@ -19,16 +19,15 @@ package com.sk89q.worldguard.protection.flags; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import org.bukkit.command.CommandSender; + import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.bukkit.command.CommandSender; - -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; - /** * Represents a flag that consists of a set. * @@ -48,6 +47,15 @@ public SetFlag(String name, Flag subFlag) { this.subFlag = subFlag; } + /** + * Get the flag that is stored in this flag. + * + * @return the stored flag type + */ + public Flag getType() { + return subFlag; + } + @Override public Set parseInput(WorldGuardPlugin plugin, CommandSender sender, String input) throws InvalidFlagFormat { diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java deleted file mode 100644 index aa929763..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/managers/FlatRegionManager.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.managers; - -import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.UnsupportedIntersectionException; -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeSet; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * A very simple implementation of the region manager that uses a flat list - * and iterates through the list to identify applicable regions. This method - * is not very efficient. - */ -public class FlatRegionManager extends RegionManager { - - /** - * List of protected regions. - */ - private ConcurrentMap regions = new ConcurrentHashMap(); - - /** - * Construct the manager. - * - * @param regionLoader The loader for regions - */ - public FlatRegionManager(ProtectionDatabase regionLoader) { - super(regionLoader); - } - - @Override - public Map getRegions() { - return regions; - } - - @Override - public void setRegions(Map regions) { - this.regions = new ConcurrentHashMap(regions); - } - - @Override - public void addRegion(ProtectedRegion region) { - regions.put(region.getId().toLowerCase(), region); - } - - @Override - public void removeRegion(String id) { - ProtectedRegion region = regions.get(id.toLowerCase()); - regions.remove(id.toLowerCase()); - - if (region != null) { - List removeRegions = new ArrayList(); - for (ProtectedRegion curRegion : regions.values()) { - if (curRegion.getParent() == region) { - removeRegions.add(curRegion.getId().toLowerCase()); - } - } - - for (String remId : removeRegions) { - removeRegion(remId); - } - } - } - - @Override - public boolean hasRegion(String id) { - return regions.containsKey(id.toLowerCase()); - } - - @Override - public ApplicableRegionSet getApplicableRegions(Vector pt) { - TreeSet appRegions = - new TreeSet(); - - for (ProtectedRegion region : regions.values()) { - if (region.contains(pt)) { - appRegions.add(region); - - ProtectedRegion parent = region.getParent(); - - while (parent != null) { - if (!appRegions.contains(parent)) { - appRegions.add(parent); - } - - parent = parent.getParent(); - } - } - } - - return new ApplicableRegionSet(appRegions, regions.get("__global__")); - } - - /** - * Get an object for a region for rules to be applied with. - * - * @return - */ - /*@Override - public ApplicableRegionSet getApplicableRegions(ProtectedRegion checkRegion) { - - List appRegions = new ArrayList(); - appRegions.addAll(regions.values()); - - List intersectRegions; - try { - intersectRegions = checkRegion.getIntersectingRegions(appRegions); - } catch (Exception e) { - intersectRegions = new ArrayList(); - } - - return new ApplicableRegionSet(intersectRegions, regions.get("__global__")); - }*/ - - @Override - public List getApplicableRegionsIDs(Vector pt) { - List applicable = new ArrayList(); - - for (Map.Entry entry : regions.entrySet()) { - if (entry.getValue().contains(pt)) { - applicable.add(entry.getKey()); - } - } - - return applicable; - } - - @Override - public ApplicableRegionSet getApplicableRegions(ProtectedRegion checkRegion) { - - List appRegions = new ArrayList(); - appRegions.addAll(regions.values()); - - List intersectRegions; - - try { - intersectRegions = checkRegion.getIntersectingRegions(appRegions); - } catch (Exception e) { - intersectRegions = new ArrayList(); - } - - return new ApplicableRegionSet(intersectRegions, regions.get("__global__")); - } - - @Override - public boolean overlapsUnownedRegion(ProtectedRegion checkRegion, LocalPlayer player) { - List appRegions = new ArrayList(); - - for (ProtectedRegion other : regions.values()) { - if (other.getOwners().contains(player)) { - continue; - } - - appRegions.add(other); - } - - List intersectRegions; - try { - intersectRegions = checkRegion.getIntersectingRegions(appRegions); - } catch (UnsupportedIntersectionException e) { - intersectRegions = new ArrayList(); - } - - return !intersectRegions.isEmpty(); - } - - @Override - public int size() { - return regions.size(); - } - - @Override - public int getRegionCountOfPlayer(LocalPlayer player) { - int count = 0; - - for (ProtectedRegion region : regions.values()) { - if (region.getOwners().contains(player)) { - count++; - } - } - - return count; - } -} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java deleted file mode 100644 index 487e5e29..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/managers/PRTreeRegionManager.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.managers; - -import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegionMBRConverter; -import org.khelekore.prtree.MBR; -import org.khelekore.prtree.MBRConverter; -import org.khelekore.prtree.PRTree; -import org.khelekore.prtree.SimpleMBR; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -public class PRTreeRegionManager extends RegionManager { - - private static final int BRANCH_FACTOR = 30; - - private MBRConverter converter = new ProtectedRegionMBRConverter(); - private RegionsContainer data = new RegionsContainer(); - - /** - * Construct the manager. - * - * @param regionLoader The region loader to use - */ - public PRTreeRegionManager(ProtectionDatabase regionLoader) { - super(regionLoader); - } - - @Override - public Map getRegions() { - return data.regions; - } - - @Override - public void setRegions(Map regions) { - ConcurrentMap newRegions = new ConcurrentHashMap(regions); - PRTree tree = new PRTree(converter, BRANCH_FACTOR); - tree.load(newRegions.values()); - this.data = new RegionsContainer(newRegions, tree); - } - - @Override - public void addRegion(ProtectedRegion region) { - RegionsContainer data = this.data; - data.regions.put(region.getId().toLowerCase(), region); - PRTree tree = new PRTree(converter, BRANCH_FACTOR); - tree.load(data.regions.values()); - this.data = new RegionsContainer(data.regions, tree); - } - - @Override - public boolean hasRegion(String id) { - return data.regions.containsKey(id.toLowerCase()); - } - - @Override - public void removeRegion(String id) { - RegionsContainer data = this.data; - - ProtectedRegion region = data.regions.get(id.toLowerCase()); - - data.regions.remove(id.toLowerCase()); - - if (region != null) { - List removeRegions = new ArrayList(); - for (ProtectedRegion curRegion : data.regions.values()) { - if (curRegion.getParent() == region) { - removeRegions.add(curRegion.getId().toLowerCase()); - } - } - - for (String remId : removeRegions) { - removeRegion(remId); - } - } - - PRTree tree = new PRTree(converter, BRANCH_FACTOR); - tree.load(data.regions.values()); - this.data = new RegionsContainer(data.regions, tree); - } - - @Override - public ApplicableRegionSet getApplicableRegions(Vector pt) { - RegionsContainer data = this.data; - - // Floor the vector to ensure we get accurate points - pt = pt.floor(); - - List appRegions = new ArrayList(); - MBR pointMBR = new SimpleMBR(pt.getX(), pt.getX(), pt.getY(), pt.getY(), pt.getZ(), pt.getZ()); - - for (ProtectedRegion region : data.tree.find(pointMBR)) { - if (region.contains(pt) && !appRegions.contains(region)) { - appRegions.add(region); - - ProtectedRegion parent = region.getParent(); - - while (parent != null) { - if (!appRegions.contains(parent)) { - appRegions.add(parent); - } - - parent = parent.getParent(); - } - } - } - - Collections.sort(appRegions); - - return new ApplicableRegionSet(appRegions, data.regions.get("__global__")); - } - - @Override - public ApplicableRegionSet getApplicableRegions(ProtectedRegion checkRegion) { - RegionsContainer data = this.data; - - List appRegions = new ArrayList(); - appRegions.addAll(data.regions.values()); - - List intersectRegions; - try { - intersectRegions = checkRegion.getIntersectingRegions(appRegions); - } catch (Exception e) { - intersectRegions = new ArrayList(); - } - - return new ApplicableRegionSet(intersectRegions, data.regions.get("__global__")); - } - - @Override - public List getApplicableRegionsIDs(Vector pt) { - RegionsContainer data = this.data; - - // Floor the vector to ensure we get accurate points - pt = pt.floor(); - - List applicable = new ArrayList(); - MBR pointMBR = new SimpleMBR(pt.getX(), pt.getX(), pt.getY(), pt.getY(), pt.getZ(), pt.getZ()); - - for (ProtectedRegion region : data.tree.find(pointMBR)) { - if (region.contains(pt) && !applicable.contains(region.getId())) { - applicable.add(region.getId()); - - ProtectedRegion parent = region.getParent(); - - while (parent != null) { - if (!applicable.contains(parent.getId())) { - applicable.add(parent.getId()); - } - - parent = parent.getParent(); - } - } - } - - return applicable; - } - - @Override - public boolean overlapsUnownedRegion(ProtectedRegion checkRegion, LocalPlayer player) { - RegionsContainer data = this.data; - - List appRegions = new ArrayList(); - - for (ProtectedRegion other : data.regions.values()) { - if (other.getOwners().contains(player)) { - continue; - } - - appRegions.add(other); - } - - List intersectRegions; - try { - intersectRegions = checkRegion.getIntersectingRegions(appRegions); - } catch (Exception e) { - intersectRegions = new ArrayList(); - } - - return !intersectRegions.isEmpty(); - } - - @Override - public int size() { - return data.regions.size(); - } - - @Override - public int getRegionCountOfPlayer(LocalPlayer player) { - int count = 0; - - for (Map.Entry entry : data.regions.entrySet()) { - if (entry.getValue().getOwners().contains(player)) { - count++; - } - } - - return count; - } - - private class RegionsContainer { - private final ConcurrentMap regions; - private final PRTree tree; - - private RegionsContainer() { - regions = new ConcurrentHashMap(); - tree = new PRTree(converter, BRANCH_FACTOR); - } - - private RegionsContainer(ConcurrentMap regions, PRTree tree) { - this.regions = regions; - this.tree = tree; - } - } - -} \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/RegionCollectionConsumer.java b/src/main/java/com/sk89q/worldguard/protection/managers/RegionCollectionConsumer.java new file mode 100644 index 00000000..0ab9d6fd --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/RegionCollectionConsumer.java @@ -0,0 +1,70 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers; + +import com.google.common.base.Predicate; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.util.Collection; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A consumer predicate that adds regions to a collection. + * + *

This class can also add the parents of regions that are visited + * to the collection, although it may result in duplicates in the collection + * if the collection is not a set.

+ */ +class RegionCollectionConsumer implements Predicate { + + private final Collection collection; + private final boolean addParents; + + /** + * Create a new instance. + * + * @param collection the collection to add regions to + * @param addParents true to also add the parents to the collection + */ + RegionCollectionConsumer(Collection collection, boolean addParents) { + checkNotNull(collection); + + this.collection = collection; + this.addParents = addParents; + } + + @Override + public boolean apply(ProtectedRegion region) { + collection.add(region); + + if (addParents) { + ProtectedRegion parent = region.getParent(); + + while (parent != null) { + collection.add(parent); + parent = parent.getParent(); + } + } + + return true; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/RegionDifference.java b/src/main/java/com/sk89q/worldguard/protection/managers/RegionDifference.java new file mode 100644 index 00000000..29411f27 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/RegionDifference.java @@ -0,0 +1,83 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers; + +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.util.Collections; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Describes the difference in region data. + */ +public final class RegionDifference { + + private final Set changed; + private final Set removed; + + /** + * Create a new instance. + * + * @param changed a set of regions that were changed or added + * @param removed a set of regions that were removed + */ + public RegionDifference(Set changed, Set removed) { + checkNotNull(changed); + checkNotNull(removed); + + this.changed = changed; + this.removed = removed; + } + + /** + * Get the regions that were changed or added. + * + * @return regions + */ + public Set getChanged() { + return Collections.unmodifiableSet(changed); + } + + /** + * Get the regions that were removed. + * + * @return regions + */ + public Set getRemoved() { + return Collections.unmodifiableSet(removed); + } + + /** + * Test whether there are changes or removals. + * + * @return true if there are changes + */ + public boolean containsChanges() { + return !changed.isEmpty() || !removed.isEmpty(); + } + + public void addAll(RegionDifference diff) { + changed.addAll(diff.getChanged()); + removed.addAll(diff.getRemoved()); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java index 51ad8698..3ccfe844 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java @@ -19,123 +19,204 @@ package com.sk89q.worldguard.protection.managers; -import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; import com.sk89q.worldedit.Vector; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.databases.ProtectionDatabase; -import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException; +import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex; +import com.sk89q.worldguard.protection.managers.index.RegionIndex; +import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException; +import com.sk89q.worldguard.protection.managers.storage.RegionStore; import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.Normal; +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; -/** - * An abstract class for getting, setting, and looking up regions. The most - * simple implementation uses a flat list and iterates through the entire list - * to look for applicable regions, but a more complicated (and more efficient) - * implementation may use space partitioning techniques. - * - * @author sk89q - */ -public abstract class RegionManager { +import static com.google.common.base.Preconditions.checkNotNull; - protected ProtectionDatabase loader; +public final class RegionManager { + + private final RegionStore store; + private final Supplier indexFactory; + private ConcurrentRegionIndex index; /** - * Construct the object. + * Create a new index. * - * @param loader The loader for this region + * @param store the region store + * @param indexFactory the factory for creating new instances of the index */ - public RegionManager(ProtectionDatabase loader) { - this.loader = loader; + public RegionManager(RegionStore store, Supplier indexFactory) { + checkNotNull(store); + checkNotNull(indexFactory); + + this.store = store; + this.indexFactory = indexFactory; + this.index = indexFactory.get(); } /** - * Load the list of regions. If the regions do not load properly, then - * the existing list should be used (as stored previously). + * Load regions from storage and replace the index on this manager with + * the regions loaded from the store. * - * @throws ProtectionDatabaseException when an error occurs + *

This method will block until the save completes, but it will + * not block access to the region data from other threads, nor will it + * prevent the creation or modification of regions in the index while + * a new collection of regions is loaded from storage.

+ * + * @throws IOException thrown when loading fails */ - public void load() throws ProtectionDatabaseException { - loader.load(this); + public void load() throws IOException { + setRegions(store.loadAll()); } /** - * Load the list of regions. If the regions do not load properly, then - * the existing list should be used (as stored previously). + * Save a snapshot of all the regions as it is right now to storage. * - * @param async true to attempt to save the data asynchronously + * @throws IOException */ - public ListenableFuture load(boolean async) { - return loader.load(this, async); + public void save() throws IOException { + store.saveAll(new HashSet(getValuesCopy())); } /** - * Save the list of regions. + * Save changes to the region index to disk, preferring to only save + * the changes (rather than the whole index), but choosing to save the + * whole index if the underlying store does not support partial saves. * - * @throws ProtectionDatabaseException when an error occurs while saving + *

This method does nothing if there are no changes.

+ * + * @throws IOException thrown on save error */ - public void save() throws ProtectionDatabaseException { - loader.save(this); + public void saveChanges() throws IOException { + RegionDifference diff = index.getAndClearDifference(); + + if (diff.containsChanges()) { + try { + store.saveChanges(diff); + } catch (DifferenceSaveException e) { + save(); // Partial save is not supported + } + } } /** - * Save the list of regions. + * Get an unmodifiable map of regions containing the state of the + * index at the time of call. * - * @param async true to attempt to save the data asynchronously + *

This call is relatively heavy (and may block other threads), + * so refrain from calling it frequently.

+ * + * @return a map of regions */ - public ListenableFuture save(boolean async) { - return loader.save(this, async); + public Map getRegions() { + Map map = new HashMap(); + for (ProtectedRegion region : index.values()) { + map.put(Normal.normalize(region.getId()), region); + } + return Collections.unmodifiableMap(map); } /** - * Get a map of protected regions. Use one of the region manager methods - * if possible if working with regions. + * Replace the index with the regions in the given map. * - * @return map of regions, with keys being region IDs (lowercase) + *

The parents of the regions will also be added to the index, even + * if they are not in the provided map.

+ * + * @param regions a map of regions */ - public abstract Map getRegions(); + public void setRegions(Map regions) { + checkNotNull(regions); + + setRegions(regions.values()); + } /** - * Set a list of protected regions. Keys should be lowercase in the given - * map fo regions. + * Replace the index with the regions in the given collection. * - * @param regions map of regions + *

The parents of the regions will also be added to the index, even + * if they are not in the provided map.

+ * + * @param regions a collection of regions */ - public abstract void setRegions(Map regions); + public void setRegions(Collection regions) { + checkNotNull(regions); + + ConcurrentRegionIndex newIndex = indexFactory.get(); + newIndex.addAll(regions); + newIndex.getAndClearDifference(); // Clear changes + this.index = newIndex; + } /** - * Adds a region. If a region by the given name already exists, then - * the existing region will be replaced. + * Aad a region to the manager. * - * @param region region to add + *

The parents of the region will also be added to the index.

+ * + * @param region the region */ - public abstract void addRegion(ProtectedRegion region); + public void addRegion(ProtectedRegion region) { + checkNotNull(region); + index.add(region); + } /** - * Return whether a region exists by an ID. + * Return whether the index contains a region by the given name, + * with equality determined by {@link Normal}. * - * @param id id of the region, can be mixed-case - * @return whether the region exists + * @param id the name of the region + * @return true if this index contains the region */ - public abstract boolean hasRegion(String id); + public boolean hasRegion(String id) { + return index.contains(id); + } /** - * Get a region by its ID. Includes symbolic names like #<index> + * Get the region named by the given name (equality determined using + * {@link Normal}). * - * @param id id of the region, can be mixed-case - * @return region or null if it doesn't exist + * @param id the name of the region + * @return a region or {@code null} + * @deprecated use {@link #matchRegion(String)} */ + @Nullable public ProtectedRegion getRegion(String id) { - if (id.startsWith("#")) { + checkNotNull(id); + return index.get(id); + } + + /** + * Matches a region using either the pattern {@code #{region_index}} or + * simply by the exact name of the region. + * + * @param pattern the pattern + * @return a region + */ + @Nullable + public ProtectedRegion matchRegion(String pattern) { + checkNotNull(pattern); + + if (pattern.startsWith("#")) { int index; try { - index = Integer.parseInt(id.substring(1)) - 1; + index = Integer.parseInt(pattern.substring(1)) - 1; } catch (NumberFormatException e) { return null; } - for (ProtectedRegion region : getRegions().values()) { + for (ProtectedRegion region : this.index.values()) { if (index == 0) { return region; } @@ -144,87 +225,167 @@ public ProtectedRegion getRegion(String id) { return null; } - return getRegionExact(id); + return getRegion(pattern); } /** - * Get a region by its ID. + * Remove a region from the index with the given name, opting to remove + * the children of the removed region. * - * @param id id of the region, can be mixed-case - * @return region or null if it doesn't exist + * @param id the name of the region + * @return a list of removed regions where the first entry is the region specified by {@code id} */ - public ProtectedRegion getRegionExact(String id) { - return getRegions().get(id.toLowerCase()); + @Nullable + public Set removeRegion(String id) { + return removeRegion(id, RemovalStrategy.REMOVE_CHILDREN); } /** - * Removes a region, including inheriting children. + * Remove a region from the index with the given name. * - * @param id id of the region, can be mixed-case + * @param id the name of the region + * @param strategy what to do with children + * @return a list of removed regions where the first entry is the region specified by {@code id} */ - public abstract void removeRegion(String id); + @Nullable + public Set removeRegion(String id, RemovalStrategy strategy) { + return index.remove(id, strategy); + } /** - * Get an object for a point for rules to be applied with. Use this in order - * to query for flag data or membership data for a given point. + * Query for effective flags and owners for the given positive. * - * @param loc Bukkit location - * @return applicable region set + * @param position the position + * @return the query object + */ + public ApplicableRegionSet getApplicableRegions(Vector position) { + checkNotNull(position); + + TreeSet regions = new TreeSet(); + index.applyContaining(position, new RegionCollectionConsumer(regions, true)); + return new ApplicableRegionSet(regions, index.get("__global__")); + } + + /** + * Query for effective flags and owners for the area represented + * by the given region. + * + * @param region the region + * @return the query object + */ + public ApplicableRegionSet getApplicableRegions(ProtectedRegion region) { + checkNotNull(region); + + TreeSet regions = new TreeSet(); + index.applyIntersecting(region, new RegionCollectionConsumer(regions, true)); + return new ApplicableRegionSet(regions, index.get("__global__")); + } + + /** + * Get a list of region names for regions that contain the given position. + * + * @param position the position + * @return a list of names + */ + public List getApplicableRegionsIDs(Vector position) { + checkNotNull(position); + + final List names = new ArrayList(); + + index.applyContaining(position, new Predicate() { + @Override + public boolean apply(ProtectedRegion region) { + return names.add(region.getId()); + } + }); + + return names; + } + + /** + * Return whether there are any regions intersecting the given region that + * are not owned by the given player. + * + * @param region the region + * @param player the player + * @return true if there are such intersecting regions + */ + public boolean overlapsUnownedRegion(ProtectedRegion region, final LocalPlayer player) { + checkNotNull(region); + checkNotNull(player); + + RegionIndex index = this.index; + + final AtomicBoolean overlapsUnowned = new AtomicBoolean(); + + index.applyIntersecting(region, new Predicate() { + @Override + public boolean apply(ProtectedRegion test) { + if (!test.getOwners().contains(player)) { + overlapsUnowned.set(true); + return false; + } else { + return true; + } + } + }); + + return overlapsUnowned.get(); + } + + /** + * Get the number of regions. + * + * @return the number of regions + */ + public int size() { + return index.size(); + } + + /** + * Get the number of regions that are owned by the given player. + * + * @param player the player + * @return name number of regions that a player owns + */ + public int getRegionCountOfPlayer(final LocalPlayer player) { + checkNotNull(player); + + final AtomicInteger count = new AtomicInteger(); + + index.apply(new Predicate() { + @Override + public boolean apply(ProtectedRegion test) { + if (test.getOwners().contains(player)) { + count.incrementAndGet(); + } + return true; + } + }); + + return count.get(); + } + + /** + * Get an {@link ArrayList} copy of regions in the index. + * + * @return a list + */ + private List getValuesCopy() { + return new ArrayList(index.values()); + } + + // =============== HELPER METHODS =============== + + /** + * Helper method for {@link #getApplicableRegions(Vector)} using Bukkit + * locations. + * + * @param loc the location + * @return an {@code ApplicableRegionSet} */ public ApplicableRegionSet getApplicableRegions(org.bukkit.Location loc) { return getApplicableRegions(com.sk89q.worldedit.bukkit.BukkitUtil.toVector(loc)); } - /** - * Get an object for a point for rules to be applied with. Use this in order - * to query for flag data or membership data for a given point. - * - * @param pt point - * @return applicable region set - */ - public abstract ApplicableRegionSet getApplicableRegions(Vector pt); - - /** - * Get an object for a point for rules to be applied with. This gets - * a set for the given reason. - * - * @param region region - * @return regino set - */ - public abstract ApplicableRegionSet getApplicableRegions( - ProtectedRegion region); - - /** - * Get a list of region IDs that contain a point. - * - * @param pt point - * @return list of region Ids - */ - public abstract List getApplicableRegionsIDs(Vector pt); - - /** - * Returns true if the provided region overlaps with any other region that - * is not owned by the player. - * - * @param region region to check - * @param player player to check against - * @return whether there is an overlap - */ - public abstract boolean overlapsUnownedRegion(ProtectedRegion region, - LocalPlayer player); - - /** - * Get the number of regions. - * - * @return number of regions - */ - public abstract int size(); - - /** - * Get the number of regions for a player. - * - * @param player player - * @return name number of regions that a player owns - */ - public abstract int getRegionCountOfPlayer(LocalPlayer player); } diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/RemovalStrategy.java b/src/main/java/com/sk89q/worldguard/protection/managers/RemovalStrategy.java new file mode 100644 index 00000000..5b136651 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/RemovalStrategy.java @@ -0,0 +1,40 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers; + +import com.sk89q.worldguard.protection.managers.index.RegionIndex; + +/** + * Determines the strategy regarding child regions when regions are + * removed from a {@link RegionIndex}. + */ +public enum RemovalStrategy { + + /** + * Unset the parent in children regions. + */ + UNSET_PARENT_IN_CHILDREN, + + /** + * Remove any children under the removed regions. + */ + REMOVE_CHILDREN + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/DatabaseMigrator.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/AbstractRegionIndex.java similarity index 76% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrator/DatabaseMigrator.java rename to src/main/java/com/sk89q/worldguard/protection/managers/index/AbstractRegionIndex.java index 56623766..c66e9b67 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/DatabaseMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/AbstractRegionIndex.java @@ -17,15 +17,11 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.databases.migrator; +package com.sk89q.worldguard.protection.managers.index; /** - * Represents a method of converting from one database to another. - * - * @author Nicholas Steicke + * An abstract implementation of a region index to make implementations easier. */ -public interface DatabaseMigrator { +abstract class AbstractRegionIndex implements RegionIndex { - public void migrate() throws MigrationException; } - diff --git a/src/test/java/com/sk89q/worldguard/protection/FlatRegionManagerTest.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/ConcurrentRegionIndex.java similarity index 65% rename from src/test/java/com/sk89q/worldguard/protection/FlatRegionManagerTest.java rename to src/main/java/com/sk89q/worldguard/protection/managers/index/ConcurrentRegionIndex.java index 91336e57..0f8ff47a 100644 --- a/src/test/java/com/sk89q/worldguard/protection/FlatRegionManagerTest.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/ConcurrentRegionIndex.java @@ -17,13 +17,16 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection; +package com.sk89q.worldguard.protection.managers.index; -import com.sk89q.worldguard.protection.managers.FlatRegionManager; -import com.sk89q.worldguard.protection.managers.RegionManager; +import java.util.concurrent.ConcurrentMap; -public class FlatRegionManagerTest extends RegionOverlapTest { - protected RegionManager createRegionManager() throws Exception { - return new FlatRegionManager(null); - } +/** + * An implementation of a region index that supports concurrent access. + * + *

The mechanics of concurrent access should be similar to that of + * {@link ConcurrentMap}. Spatial queries can lag behind changes on the data + * for brief periods of time.

+ */ +public interface ConcurrentRegionIndex extends RegionIndex { } diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/index/HashMapIndex.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/HashMapIndex.java new file mode 100644 index 00000000..aaa86a93 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/HashMapIndex.java @@ -0,0 +1,263 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.index; + +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldguard.protection.managers.RegionDifference; +import com.sk89q.worldguard.protection.managers.RemovalStrategy; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldguard.util.Normal.normalize; + +/** + * An index that stores regions in a hash map, which allows for fast lookup + * by ID but O(n) performance for spatial queries. + * + *

This implementation supports concurrency to the extent that + * a {@link ConcurrentMap} does.

+ */ +public class HashMapIndex extends AbstractRegionIndex implements ConcurrentRegionIndex { + + private final ConcurrentMap regions = new ConcurrentHashMap(); + private Set removed = new HashSet(); + private final Object lock = new Object(); + + /** + * Called to rebuild the index after changes. + */ + protected void rebuildIndex() { + // Can be implemented by subclasses + } + + /** + * Perform the add operation. + * + * @param region the region + */ + private void performAdd(ProtectedRegion region) { + checkNotNull(region); + + region.setDirty(true); + + synchronized (lock) { + String normalId = normalize(region.getId()); + + ProtectedRegion existing = regions.get(normalId); + + // Casing / form of ID has changed + if (existing != null && !existing.getId().equals(region.getId())) { + removed.add(existing); + } + + regions.put(normalId, region); + + removed.remove(region); + + ProtectedRegion parent = region.getParent(); + if (parent != null) { + performAdd(parent); + } + } + } + + @Override + public void addAll(Collection regions) { + checkNotNull(regions); + + synchronized (lock) { + for (ProtectedRegion region : regions) { + performAdd(region); + } + + rebuildIndex(); + } + } + + @Override + public void add(ProtectedRegion region) { + synchronized (lock) { + performAdd(region); + + rebuildIndex(); + } + } + + @Override + public Set remove(String id, RemovalStrategy strategy) { + checkNotNull(id); + checkNotNull(strategy); + + Set removedSet = new HashSet(); + + synchronized (lock) { + ProtectedRegion removed = regions.remove(normalize(id)); + + if (removed != null) { + removedSet.add(removed); + + Iterator it = regions.values().iterator(); + + // Handle children + while (it.hasNext()) { + ProtectedRegion current = it.next(); + ProtectedRegion parent = current.getParent(); + + if (parent != null && parent == removed) { + switch (strategy) { + case REMOVE_CHILDREN: + removedSet.add(current); + it.remove(); + break; + case UNSET_PARENT_IN_CHILDREN: + current.clearParent(); + } + } + } + } + + this.removed.addAll(removedSet); + + rebuildIndex(); + } + + return removedSet; + } + + @Override + public boolean contains(String id) { + return regions.containsKey(normalize(id)); + } + + @Nullable + @Override + public ProtectedRegion get(String id) { + return regions.get(normalize(id)); + } + + @Override + public void apply(Predicate consumer) { + for (ProtectedRegion region : regions.values()) { + if (!consumer.apply(region)) { + break; + } + } + } + + @Override + public void applyContaining(final Vector position, final Predicate consumer) { + apply(new Predicate() { + @Override + public boolean apply(ProtectedRegion region) { + return !region.contains(position) || consumer.apply(region); + } + }); + } + + @Override + public void applyIntersecting(ProtectedRegion region, Predicate consumer) { + for (ProtectedRegion found : region.getIntersectingRegions(regions.values())) { + if (!consumer.apply(found)) { + break; + } + } + } + + @Override + public int size() { + return regions.size(); + } + + @Override + public RegionDifference getAndClearDifference() { + synchronized (lock) { + Set changed = new HashSet(); + Set removed = this.removed; + + for (ProtectedRegion region : regions.values()) { + if (region.isDirty()) { + changed.add(region); + region.setDirty(false); + } + } + + this.removed = new HashSet(); + + return new RegionDifference(changed, removed); + } + } + + @Override + public Collection values() { + return Collections.unmodifiableCollection(regions.values()); + } + + @Override + public boolean isDirty() { + synchronized (lock) { + if (!removed.isEmpty()) { + return true; + } + + for (ProtectedRegion region : regions.values()) { + if (region.isDirty()) { + return true; + } + } + } + + return false; + } + + @Override + public void setDirty(boolean dirty) { + synchronized (lock) { + if (!dirty) { + removed.clear(); + } + + for (ProtectedRegion region : regions.values()) { + region.setDirty(dirty); + } + } + } + + /** + * A factory for new instances using this index. + */ + public static final class Factory implements Supplier { + @Override + public HashMapIndex get() { + return new HashMapIndex(); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/index/PriorityRTreeIndex.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/PriorityRTreeIndex.java new file mode 100644 index 00000000..ea54da7e --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/PriorityRTreeIndex.java @@ -0,0 +1,102 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.index; + +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegionMBRConverter; +import org.khelekore.prtree.MBR; +import org.khelekore.prtree.MBRConverter; +import org.khelekore.prtree.PRTree; +import org.khelekore.prtree.SimpleMBR; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * An implementation of an index that uses {@link HashMapIndex} for queries + * by region name and a priority R-tree for spatial queries. + * + *

At the moment, the R-tree is only utilized for the + * {@link #applyContaining(Vector, Predicate)} method, and the underlying + * hash map-based index is used for the other spatial queries. In addition, + * every modification to the index requires the entire R-tree to be rebuilt, + * although this operation is reasonably quick.

+ * + *

This implementation is as thread-safe as the underlying + * {@link HashMapIndex}, although spatial queries may lag behind changes + * for very brief periods of time as the tree is rebuilt.

+ */ +public class PriorityRTreeIndex extends HashMapIndex { + + private static final int BRANCH_FACTOR = 30; + private static final MBRConverter CONVERTER = new ProtectedRegionMBRConverter(); + + private PRTree tree; + + public PriorityRTreeIndex() { + tree = new PRTree(CONVERTER, BRANCH_FACTOR); + tree.load(Collections.emptyList()); + } + + @Override + protected void rebuildIndex() { + PRTree newTree = new PRTree(CONVERTER, BRANCH_FACTOR); + newTree.load(values()); + this.tree = newTree; + } + + @Override + public void applyContaining(Vector position, Predicate consumer) { + // Floor the vector to ensure we get accurate points + position = position.floor(); + + Set seen = new HashSet(); + MBR pointMBR = new SimpleMBR(position.getX(), position.getX(), position.getY(), position.getY(), position.getZ(), position.getZ()); + + for (ProtectedRegion region : tree.find(pointMBR)) { + if (region.contains(position) && !seen.contains(region)) { + seen.add(region); + if (!consumer.apply(region)) { + break; + } + } + } + } + + @Override + public void applyIntersecting(ProtectedRegion region, Predicate consumer) { + super.applyIntersecting(region, consumer); + } + + /** + * A factory for new instances using this index. + */ + public static final class Factory implements Supplier { + @Override + public PriorityRTreeIndex get() { + return new PriorityRTreeIndex(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/index/RegionIndex.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/RegionIndex.java new file mode 100644 index 00000000..76e43812 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/RegionIndex.java @@ -0,0 +1,138 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.index; + +import com.google.common.base.Predicate; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldguard.protection.managers.RegionDifference; +import com.sk89q.worldguard.protection.managers.RemovalStrategy; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.ChangeTracked; +import com.sk89q.worldguard.util.Normal; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Set; + +/** + * An index of regions to allow for fast lookups of regions by their ID and + * through spatial queries. + * + *

Indexes may be thread-unsafe.

+ */ +public interface RegionIndex extends ChangeTracked { + + /** + * Add a region to this index, replacing any existing one with the same + * name (equality determined using {@link Normal}). + * + *

The parents of the region will also be added to the index.

+ * + * @param region the region + */ + void add(ProtectedRegion region); + + /** + * Add a list of regions to this index, replacing any existing one + * with the same name (equality determined using {@link Normal}). + * + *

The parents of the region will also be added to the index.

+ * + * @param regions a collections of regions + */ + void addAll(Collection regions); + + /** + * Remove a region from the index with the given name. + * + * @param id the name of the region + * @param strategy what to do with children + * @return a list of removed regions where the first entry is the region specified by {@code id} + */ + Set remove(String id, RemovalStrategy strategy); + + /** + * Test whether the index contains a region named by the given name + * (equality determined using {@link Normal}). + * + * @param id the name of the region + * @return true if the index contains the region + */ + boolean contains(String id); + + /** + * Get the region named by the given name (equality determined using + * {@link Normal}). + * + * @param id the name of the region + * @return a region or {@code null} + */ + @Nullable + ProtectedRegion get(String id); + + /** + * Apply the given predicate to all the regions in the index + * until there are no more regions or the predicate returns false. + * + * @param consumer a predicate that returns true to continue iterating + */ + void apply(Predicate consumer); + + /** + * Apply the given predicate to all regions that contain the given + * position until there are no more regions or the predicate returns false. + * + * @param position the position + * @param consumer a predicate that returns true to continue iterating + */ + void applyContaining(Vector position, Predicate consumer); + + /** + * Apply the given predicate to all regions that intersect the given + * region until there are no more regions or the predicate returns false. + * + * @param region the intersecting region + * @param consumer a predicate that returns true to continue iterating + */ + void applyIntersecting(ProtectedRegion region, Predicate consumer); + + /** + * Return the number of regions in the index. + * + * @return the number of regions + */ + int size(); + + /** + * Get the list of changed or removed regions since last call and + * clear those lists. + * + * @return the difference + */ + RegionDifference getAndClearDifference(); + + /** + * Get an unmodifiable collection of regions stored in this index. + * + * @return a collection of regions + */ + Collection values(); + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/DifferenceSaveException.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/DifferenceSaveException.java new file mode 100644 index 00000000..9620c172 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/DifferenceSaveException.java @@ -0,0 +1,41 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage; + +/** + * Thrown when a partial save is not supported. + */ +public class DifferenceSaveException extends RegionStoreException { + + public DifferenceSaveException() { + } + + public DifferenceSaveException(String message) { + super(message); + } + + public DifferenceSaveException(String message, Throwable cause) { + super(message, cause); + } + + public DifferenceSaveException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/MemoryRegionStore.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/MemoryRegionStore.java new file mode 100644 index 00000000..149634ea --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/MemoryRegionStore.java @@ -0,0 +1,52 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage; + +import com.sk89q.worldguard.protection.managers.RegionDifference; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * A memory store that saves the memory to a temporary variable. + */ +public class MemoryRegionStore implements RegionStore { + + private Set regions = Collections.emptySet(); + + @Override + public Set loadAll() throws IOException { + return regions; + } + + @Override + public void saveAll(Set regions) throws IOException { + this.regions = Collections.unmodifiableSet(new HashSet(regions)); + } + + @Override + public void saveChanges(RegionDifference difference) throws DifferenceSaveException, IOException { + throw new DifferenceSaveException(); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStore.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStore.java new file mode 100644 index 00000000..2d6da8be --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStore.java @@ -0,0 +1,69 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage; + +import com.sk89q.worldguard.protection.managers.RegionDifference; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import java.io.IOException; +import java.util.Set; + +/** + * An object that persists region data to a persistent store. + */ +public interface RegionStore { + + /** + * Load all regions from storage and place them into the passed map. + * + *

The map will only be modified from one thread. The keys of + * each map entry will be in respect to the ID of the region but + * transformed to be lowercase. Until this method returns, the map may not + * be modified by any other thread simultaneously. If an exception is + * thrown, then the state in which the map is left is undefined.

+ * + *

The provided map should have reasonably efficient + * {@code get()} and {@code put()} calls in order to maximize performance. + *

+ * + * @return a setf loaded regions + * @throws IOException thrown on read error + */ + Set loadAll() throws IOException; + + /** + * Replace all the data in the store with the given collection of regions. + * + * @param regions a set of regions + * @throws IOException thrown on write error + */ + void saveAll(Set regions) throws IOException; + + /** + * Perform a partial save that only commits changes, rather than the + * entire region index. + * + * @param difference the difference + * @throws DifferenceSaveException thrown if partial saves are not supported + * @throws IOException thrown on write error + */ + void saveChanges(RegionDifference difference) throws DifferenceSaveException, IOException; + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabaseException.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStoreException.java similarity index 67% rename from src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabaseException.java rename to src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStoreException.java index b4f2f883..c818e096 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/ProtectionDatabaseException.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStoreException.java @@ -17,26 +17,26 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.databases; +package com.sk89q.worldguard.protection.managers.storage; -import java.lang.Exception; +/** + * Exceptions related to region stores inherit from this exception. + */ +public class RegionStoreException extends Exception { -public class ProtectionDatabaseException extends Exception { - private static final long serialVersionUID = 1L; - - public ProtectionDatabaseException() { - super(); + public RegionStoreException() { } - public ProtectionDatabaseException(String message) { + public RegionStoreException(String message) { super(message); } - public ProtectionDatabaseException(String message, Throwable cause) { + public RegionStoreException(String message, Throwable cause) { super(message, cause); } - public ProtectionDatabaseException(Throwable cause) { + public RegionStoreException(Throwable cause) { super(cause); } + } diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStoreUtils.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStoreUtils.java new file mode 100644 index 00000000..c533ae9e --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/RegionStoreUtils.java @@ -0,0 +1,125 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage; + +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; + +import javax.annotation.Nullable; +import java.util.Map; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Utility methods for region stores. + */ +public final class RegionStoreUtils { + + private static final Logger log = Logger.getLogger(RegionStoreUtils.class.getCanonicalName()); + + private RegionStoreUtils() { + } + + /** + * Try setting the given map of flags onto the region. + * + * @param region the region + * @param flagData the map of flag data + */ + public static void trySetFlagMap(ProtectedRegion region, Map flagData) { + checkNotNull(region); + checkNotNull(flagData); + + for (Flag flag : DefaultFlag.getFlags()) { + if (flag == null) { + // Some plugins that add custom flags to WorldGuard are doing + // something very wrong -- see WORLDGUARD-3094 + continue; + } + + Object o = flagData.get(flag.getName()); + if (o != null) { + RegionStoreUtils.trySetFlag(region, flag, o); + } + + // Set group + if (flag.getRegionGroupFlag() != null) { + Object o2 = flagData.get(flag.getRegionGroupFlag().getName()); + if (o2 != null) { + RegionStoreUtils.trySetFlag(region, flag.getRegionGroupFlag(), o2); + } + } + } + } + + /** + * Try to set a flag on the region. + * + * @param region the region + * @param flag the flag + * @param value the value of the flag, which may be {@code null} + * @param the flag's type + * @return true if the set succeeded + */ + public static boolean trySetFlag(ProtectedRegion region, Flag flag, @Nullable Object value) { + checkNotNull(region); + checkNotNull(flag); + + T val = flag.unmarshal(value); + + if (val != null) { + region.setFlag(flag, val); + return true; + } else { + log.warning("Failed to parse flag '" + flag.getName() + "' with value '" + value + "'"); + return false; + } + } + + /** + * Re-link parent regions on each provided region using the two + * provided maps. + * + * @param regions the map of regions from which parent regions are found + * @param parentSets a mapping of region to parent name + */ + public static void relinkParents(Map regions, Map parentSets) { + checkNotNull(regions); + checkNotNull(parentSets); + + for (Map.Entry entry : parentSets.entrySet()) { + ProtectedRegion target = entry.getKey(); + ProtectedRegion parent = regions.get(entry.getValue()); + if (parent != null) { + try { + target.setParent(parent); + } catch (CircularInheritanceException e) { + log.warning("Circular inheritance detected! Can't set the parent of '" + target + "' to parent '" + parent.getId() + "'"); + } + } else { + log.warning("Unknown region parent: " + entry.getValue()); + } + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DirectoryYamlDriver.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DirectoryYamlDriver.java new file mode 100644 index 00000000..28e06bd6 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DirectoryYamlDriver.java @@ -0,0 +1,103 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.driver; + +import com.sk89q.worldguard.protection.managers.storage.RegionStore; +import com.sk89q.worldguard.protection.managers.storage.file.YamlFileStore; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Stores region data in a {root_dir}/{id}/{filename} pattern on disk + * using {@link YamlFileStore}. + */ +public class DirectoryYamlDriver implements RegionStoreDriver { + + private final File rootDir; + private final String filename; + + /** + * Create a new instance. + * + * @param rootDir the directory where the world folders reside + * @param filename the filename (i.e. "regions.yml") + */ + public DirectoryYamlDriver(File rootDir, String filename) { + checkNotNull(rootDir); + checkNotNull(filename); + this.rootDir = rootDir; + this.filename = filename; + } + + /** + * Get the path for the given ID. + * + * @param id the ID + * @return the file path + */ + private File getPath(String id) { + checkNotNull(id); + + File f = new File(rootDir, id + File.separator + filename); + try { + f.getCanonicalPath(); + return f; + } catch (IOException e) { + throw new IllegalArgumentException("Invalid file path for the world's regions file"); + } + } + + @Override + public RegionStore get(String id) throws IOException { + checkNotNull(id); + + File file = getPath(id); + File parentDir = file.getParentFile(); + if (!parentDir.exists()) { + if (!parentDir.mkdirs()) { + throw new IOException("Failed to create the parent directory (" + parentDir.getAbsolutePath() + ") to store the regions file in"); + } + } + + return new YamlFileStore(file); + } + + @Override + public List fetchAllExisting() { + List names = new ArrayList(); + + File files[] = rootDir.listFiles(); + if (files != null) { + for (File dir : files) { + if (dir.isDirectory() && new File(dir, "regions.yml").isFile()) { + names.add(dir.getName()); + } + } + } + + return names; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DriverType.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DriverType.java new file mode 100644 index 00000000..9e45691b --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/DriverType.java @@ -0,0 +1,52 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.driver; + +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.util.sql.DataSourceConfig; + +/** + * An enumeration of supported drivers. + */ +public enum DriverType { + + YAML { + @Override + public RegionStoreDriver create(ConfigurationManager config) { + return new DirectoryYamlDriver(config.getWorldsDataFolder(), "regions.yml"); + } + }, + SQL { + @Override + public RegionStoreDriver create(ConfigurationManager config) { + DataSourceConfig dataSourceConfig = new DataSourceConfig(config.sqlDsn, config.sqlUsername, config.sqlPassword, config.sqlTablePrefix); + return new SQLDriver(dataSourceConfig); + } + }; + + /** + * Create a driver instance from a configuration. + * + * @param config a configuration + * @return a driver + */ + public abstract RegionStoreDriver create(ConfigurationManager config); + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/RegionStoreDriver.java old mode 100755 new mode 100644 similarity index 50% rename from src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java rename to src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/RegionStoreDriver.java index aff2c284..55ba195a --- a/src/main/java/com/sk89q/worldguard/protection/databases/MySQLDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/RegionStoreDriver.java @@ -17,30 +17,33 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.databases; +package com.sk89q.worldguard.protection.managers.storage.driver; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.protection.databases.mysql.MySQLDatabaseImpl; +import com.sk89q.worldguard.protection.managers.storage.RegionStore; -import java.util.logging.Logger; +import java.io.IOException; +import java.util.List; /** - * A store that persists regions in a MySQL database. + * A driver is able to create new {@code RegionStore}s for named worlds. */ -public class MySQLDatabase extends MySQLDatabaseImpl { +public interface RegionStoreDriver { /** - * Create a new instance. + * Get a region store for the named world. * - * @param executor the executor to perform loads and saves in - * @param config the configuration - * @param worldName the world name - * @param logger a logger - * @throws ProtectionDatabaseException thrown on error + * @param name the name + * @return the world + * @throws IOException thrown if the region store can't be created due to an I/O error */ - public MySQLDatabase(ConfigurationManager config, String worldName, Logger logger) throws ProtectionDatabaseException { - super(config, worldName, logger); - } + RegionStore get(String name) throws IOException; + + /** + * Fetch the names of all worlds that are stored with this driver. + * + * @return a list of names + * @throws IOException thrown if the fetch operation fails + */ + List fetchAllExisting() throws IOException; } diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/SQLDriver.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/SQLDriver.java new file mode 100644 index 00000000..04c9d25e --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/driver/SQLDriver.java @@ -0,0 +1,101 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.driver; + +import com.jolbox.bonecp.BoneCP; +import com.sk89q.worldguard.protection.managers.storage.RegionStore; +import com.sk89q.worldguard.protection.managers.storage.sql.SQLRegionStore; +import com.sk89q.worldguard.util.io.Closer; +import com.sk89q.worldguard.util.sql.DataSourceConfig; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Stores regions using {@link SQLRegionStore}. + */ +public class SQLDriver implements RegionStoreDriver { + + private final DataSourceConfig config; + private final Object lock = new Object(); + private BoneCP connectionPool; + + /** + * Create a new instance. + * + * @param config a configuration + */ + public SQLDriver(DataSourceConfig config) { + checkNotNull(config); + this.config = config; + } + + /** + * Get an instance of the connection pool. + * + * @return the connection pool + * @throws SQLException occurs when the connection pool can't be created + */ + protected BoneCP getConnectionPool() throws SQLException { + synchronized (lock) { + if (connectionPool == null) { + connectionPool = new BoneCP(config.createBoneCPConfig()); + } + + return connectionPool; + } + } + + @Override + public RegionStore get(String name) throws IOException { + try { + return new SQLRegionStore(config, getConnectionPool(), name); + } catch (SQLException e) { + throw new IOException("Failed to get a connection pool for storing regions (are the SQL details correct?)"); + } + } + + @Override + public List fetchAllExisting() throws IOException { + Closer closer = Closer.create(); + try { + List names = new ArrayList(); + Connection connection = closer.register(getConnectionPool().getConnection()); + Statement stmt = connection.createStatement(); + ResultSet rs = closer.register(stmt.executeQuery("SELECT name FROM world")); + while (rs.next()) { + names.add(rs.getString(1)); + } + return names; + } catch (SQLException e) { + throw new IOException("Failed to fetch list of worlds", e); + } finally { + closer.close(); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlFileStore.java similarity index 56% rename from src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java rename to src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlFileStore.java index a1e26b4a..b8c98018 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/YAMLDatabase.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlFileStore.java @@ -1,405 +1,349 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases; - -import com.sk89q.util.yaml.YAMLFormat; -import com.sk89q.util.yaml.YAMLNode; -import com.sk89q.util.yaml.YAMLProcessor; -import com.sk89q.worldedit.BlockVector; -import com.sk89q.worldedit.BlockVector2D; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.flags.Flag; -import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; -import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.DumperOptions.FlowStyle; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.SafeConstructor; -import org.yaml.snakeyaml.representer.Representer; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * A store that persists regions in a YAML-encoded file. - */ -public class YAMLDatabase extends AbstractAsynchronousDatabase { - - /** - * Used to dump YAML when an error occurs - */ - private static Yaml yaml; - - private Map regions; - private final File file; - private final Logger logger; - - /** - * Create a new instance. - * - * @param file the file - * @param logger a logger - * @throws ProtectionDatabaseException - * @throws FileNotFoundException - */ - public YAMLDatabase(File file, Logger logger) throws ProtectionDatabaseException, FileNotFoundException { - this.logger = logger; - this.file = file; - - if (!file.exists()) { // shouldn't be necessary, but check anyways - try { - file.createNewFile(); - } catch (IOException e) { - throw new FileNotFoundException(file.getAbsolutePath()); - } - } - } - - private YAMLProcessor createYamlProcessor(File file) { - return new YAMLProcessor(file, false, YAMLFormat.COMPACT); - } - - @Override - public void performLoad() throws ProtectionDatabaseException { - YAMLProcessor config = createYamlProcessor(file); - - try { - config.load(); - } catch (IOException e) { - throw new ProtectionDatabaseException(e); - } - - Map regionData = config.getNodes("regions"); - - // No regions are even configured - if (regionData == null) { - this.regions = new HashMap(); - return; - } - - // Warning for WORLDGUARD-3094 - for (Flag flag : DefaultFlag.getFlags()) { - if (flag == null) { - logger.severe("Some 3rd-party plugin has registered an invalid 'null' custom flag with WorldGuard, though we can't tell you which plugin did it - this may cause major problems in other places"); - break; - } - } - - Map regions = new HashMap(); - Map parentSets = new LinkedHashMap(); - - for (Map.Entry entry : regionData.entrySet()) { - String id = entry.getKey().toLowerCase().replace(".", ""); - YAMLNode node = entry.getValue(); - - String type = node.getString("type"); - ProtectedRegion region; - - try { - if (type == null) { - logger.warning("Undefined region type for region '" + id + "'!\n" + - "Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + "\n"); - continue; - } else if (type.equals("cuboid")) { - Vector pt1 = checkNonNull(node.getVector("min")); - Vector pt2 = checkNonNull(node.getVector("max")); - BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector(); - BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector(); - region = new ProtectedCuboidRegion(id, min, max); - } else if (type.equals("poly2d")) { - Integer minY = checkNonNull(node.getInt("min-y")); - Integer maxY = checkNonNull(node.getInt("max-y")); - List points = node.getBlockVector2dList("points", null); - region = new ProtectedPolygonalRegion(id, points, minY, maxY); - } else if (type.equals("global")) { - region = new GlobalProtectedRegion(id); - } else { - logger.warning("Unknown region type for region '" + id + "'!\n" + - "Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + "\n"); - continue; - } - - Integer priority = checkNonNull(node.getInt("priority")); - region.setPriority(priority); - setFlags(region, node.getNode("flags")); - region.setOwners(parseDomain(node.getNode("owners"))); - region.setMembers(parseDomain(node.getNode("members"))); - regions.put(id, region); - - String parentId = node.getString("parent"); - if (parentId != null) { - parentSets.put(region, parentId); - } - } catch (NullPointerException e) { - logger.log(Level.WARNING, - "Unexpected NullPointerException encountered during parsing for the region '" + id + "'!\n" + - "Here is what the region data looks like:\n\n" + dumpAsYaml(entry.getValue().getMap()) + - "\n\nNote: This region will disappear as a result!", e); - } - } - - // Relink parents - for (Map.Entry entry : parentSets.entrySet()) { - ProtectedRegion parent = regions.get(entry.getValue()); - if (parent != null) { - try { - entry.getKey().setParent(parent); - } catch (CircularInheritanceException e) { - logger.warning("Circular inheritance detect with '" + entry.getValue() + "' detected as a parent"); - } - } else { - logger.warning("Unknown region parent: " + entry.getValue()); - } - } - - this.regions = regions; - } - - private V checkNonNull(V val) throws NullPointerException { - if (val == null) { - throw new NullPointerException(); - } - - return val; - } - - private void setFlags(ProtectedRegion region, YAMLNode flagsData) { - if (flagsData == null) { - return; - } - - // @TODO: Make this better - for (Flag flag : DefaultFlag.getFlags()) { - if (flag == null) { - // Some plugins that add custom flags to WorldGuard are doing - // something very wrong -- see WORLDGUARD-3094 - continue; - } - - Object o = flagsData.getProperty(flag.getName()); - if (o != null) { - setFlag(region, flag, o); - } - - if (flag.getRegionGroupFlag() != null) { - Object o2 = flagsData.getProperty(flag.getRegionGroupFlag().getName()); - if (o2 != null) { - setFlag(region, flag.getRegionGroupFlag(), o2); - } - } - } - } - - private void setFlag(ProtectedRegion region, Flag flag, Object rawValue) { - T val = flag.unmarshal(rawValue); - if (val == null) { - logger.warning("Failed to parse flag '" + flag.getName() - + "' with value '" + rawValue.toString() + "'"); - return; - } - region.setFlag(flag, val); - } - - @SuppressWarnings("deprecation") - private DefaultDomain parseDomain(YAMLNode node) { - if (node == null) { - return new DefaultDomain(); - } - - DefaultDomain domain = new DefaultDomain(); - - for (String name : node.getStringList("players", null)) { - domain.addPlayer(name); - } - - for (String stringId : node.getStringList("unique-ids", null)) { - try { - domain.addPlayer(UUID.fromString(stringId)); - } catch (IllegalArgumentException e) { - logger.log(Level.WARNING, "Failed to parse UUID '" + stringId +"'", e); - } - } - - for (String name : node.getStringList("groups", null)) { - domain.addGroup(name); - } - - return domain; - } - - @Override - protected void performSave() throws ProtectionDatabaseException { - File tempFile = new File(file.getParentFile(), file.getName() + ".tmp"); - YAMLProcessor config = createYamlProcessor(tempFile); - - config.clear(); - - for (Map.Entry entry : regions.entrySet()) { - ProtectedRegion region = entry.getValue(); - YAMLNode node = config.addNode("regions." + entry.getKey()); - - if (region instanceof ProtectedCuboidRegion) { - ProtectedCuboidRegion cuboid = (ProtectedCuboidRegion) region; - node.setProperty("type", "cuboid"); - node.setProperty("min", cuboid.getMinimumPoint()); - node.setProperty("max", cuboid.getMaximumPoint()); - } else if (region instanceof ProtectedPolygonalRegion) { - ProtectedPolygonalRegion poly = (ProtectedPolygonalRegion) region; - node.setProperty("type", "poly2d"); - node.setProperty("min-y", poly.getMinimumPoint().getBlockY()); - node.setProperty("max-y", poly.getMaximumPoint().getBlockY()); - - List> points = new ArrayList>(); - for (BlockVector2D point : poly.getPoints()) { - Map data = new HashMap(); - data.put("x", point.getBlockX()); - data.put("z", point.getBlockZ()); - points.add(data); - } - - node.setProperty("points", points); - } else if (region instanceof GlobalProtectedRegion) { - node.setProperty("type", "global"); - } else { - node.setProperty("type", region.getClass().getCanonicalName()); - } - - node.setProperty("priority", region.getPriority()); - node.setProperty("flags", getFlagData(region)); - node.setProperty("owners", getDomainData(region.getOwners())); - node.setProperty("members", getDomainData(region.getMembers())); - ProtectedRegion parent = region.getParent(); - if (parent != null) { - node.setProperty("parent", parent.getId()); - } - } - - config.setHeader("#\r\n" + - "# WorldGuard regions file\r\n" + - "#\r\n" + - "# WARNING: THIS FILE IS AUTOMATICALLY GENERATED. If you modify this file by\r\n" + - "# hand, be aware that A SINGLE MISTYPED CHARACTER CAN CORRUPT THE FILE. If\r\n" + - "# WorldGuard is unable to parse the file, your regions will FAIL TO LOAD and\r\n" + - "# the contents of this file will reset. Please use a YAML validator such as\r\n" + - "# http://yaml-online-parser.appspot.com (for smaller files).\r\n" + - "#\r\n" + - "# REMEMBER TO KEEP PERIODICAL BACKUPS.\r\n" + - "#"); - config.save(); - - file.delete(); - if (!tempFile.renameTo(file)) { - throw new ProtectionDatabaseException("Failed to rename temporary regions file to " + file.getAbsolutePath()); - } - } - - private Map getFlagData(ProtectedRegion region) { - Map flagData = new HashMap(); - - for (Map.Entry, Object> entry : region.getFlags().entrySet()) { - Flag flag = entry.getKey(); - addMarshalledFlag(flagData, flag, entry.getValue()); - } - - return flagData; - } - - @SuppressWarnings("unchecked") - private void addMarshalledFlag(Map flagData, Flag flag, Object val) { - if (val == null) { - return; - } - - flagData.put(flag.getName(), flag.marshal((V) val)); - } - - @SuppressWarnings("deprecation") - private Map getDomainData(DefaultDomain domain) { - Map domainData = new HashMap(); - - setDomainData(domainData, "players", domain.getPlayers()); - setDomainData(domainData, "unique-ids", domain.getUniqueIds()); - setDomainData(domainData, "groups", domain.getGroups()); - - return domainData; - } - - private void setDomainData(Map domainData, String key, Set domain) { - if (domain.isEmpty()) { - return; - } - - List list = new ArrayList(); - - for (Object str : domain) { - list.add(String.valueOf(str)); - } - - domainData.put(key, list); - } - - @Override - public Map getRegions() { - return regions; - } - - @Override - public void setRegions(Map regions) { - this.regions = regions; - } - - /** - * Dump the given object as YAML for debugging purposes. - * - * @param object the object - * @return the YAML string or an error string if dumping fals - */ - private static String dumpAsYaml(Object object) { - if (yaml == null) { - DumperOptions options = new DumperOptions(); - options.setIndent(4); - options.setDefaultFlowStyle(FlowStyle.AUTO); - - yaml = new Yaml(new SafeConstructor(), new Representer(), options); - } - - try { - return yaml.dump(object).replaceAll("(?m)^", "\t"); - } catch (Throwable t) { - return ""; - } - } - -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.file; + +import com.sk89q.util.yaml.YAMLFormat; +import com.sk89q.util.yaml.YAMLNode; +import com.sk89q.util.yaml.YAMLProcessor; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.managers.RegionDifference; +import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException; +import com.sk89q.worldguard.protection.managers.storage.RegionStore; +import com.sk89q.worldguard.protection.managers.storage.RegionStoreUtils; +import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.DumperOptions.FlowStyle; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.representer.Representer; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A store that persists regions in a YAML-encoded file. + */ +public class YamlFileStore implements RegionStore { + + private static final Logger log = Logger.getLogger(YamlFileStore.class.getCanonicalName()); + private static final Yaml ERROR_DUMP_YAML; + + private static final String FILE_HEADER = "#\r\n" + + "# WorldGuard regions file\r\n" + + "#\r\n" + + "# WARNING: THIS FILE IS AUTOMATICALLY GENERATED. If you modify this file by\r\n" + + "# hand, be aware that A SINGLE MISTYPED CHARACTER CAN CORRUPT THE FILE. If\r\n" + + "# WorldGuard is unable to parse the file, your regions will FAIL TO LOAD and\r\n" + + "# the contents of this file will reset. Please use a YAML validator such as\r\n" + + "# http://yaml-online-parser.appspot.com (for smaller files).\r\n" + + "#\r\n" + + "# REMEMBER TO KEEP PERIODICAL BACKUPS.\r\n" + + "#"; + + private final File file; + + static { + DumperOptions options = new DumperOptions(); + options.setIndent(4); + options.setDefaultFlowStyle(FlowStyle.AUTO); + + ERROR_DUMP_YAML = new Yaml(new SafeConstructor(), new Representer(), options); + } + + /** + * Create a new instance. + * + * @param file the file + * @throws IOException thrown if the file cannot be written to + */ + public YamlFileStore(File file) throws IOException { + checkNotNull(file); + this.file = file; + if (!file.exists()) { + //noinspection ResultOfMethodCallIgnored + file.createNewFile(); + } + } + + @Override + public Set loadAll() throws IOException { + Map loaded = new HashMap(); + + YAMLProcessor config = createYamlProcessor(file); + config.load(); + + Map regionData = config.getNodes("regions"); + + if (regionData == null) { + return Collections.emptySet(); // No regions are even configured + } + + Map parentSets = new LinkedHashMap(); + + for (Map.Entry entry : regionData.entrySet()) { + String id = entry.getKey(); + YAMLNode node = entry.getValue(); + + String type = node.getString("type"); + ProtectedRegion region; + + try { + if (type == null) { + log.warning("Undefined region type for region '" + id + "'!\n" + + "Here is what the region data looks like:\n\n" + toYamlOutput(entry.getValue().getMap()) + "\n"); + continue; + } else if (type.equals("cuboid")) { + Vector pt1 = checkNotNull(node.getVector("min")); + Vector pt2 = checkNotNull(node.getVector("max")); + BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector(); + BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector(); + region = new ProtectedCuboidRegion(id, min, max); + } else if (type.equals("poly2d")) { + Integer minY = checkNotNull(node.getInt("min-y")); + Integer maxY = checkNotNull(node.getInt("max-y")); + List points = node.getBlockVector2dList("points", null); + region = new ProtectedPolygonalRegion(id, points, minY, maxY); + } else if (type.equals("global")) { + region = new GlobalProtectedRegion(id); + } else { + log.warning("Unknown region type for region '" + id + "'!\n" + + "Here is what the region data looks like:\n\n" + toYamlOutput(entry.getValue().getMap()) + "\n"); + continue; + } + + Integer priority = checkNotNull(node.getInt("priority")); + region.setPriority(priority); + setFlags(region, node.getNode("flags")); + region.setOwners(parseDomain(node.getNode("owners"))); + region.setMembers(parseDomain(node.getNode("members"))); + + loaded.put(id, region); + + String parentId = node.getString("parent"); + if (parentId != null) { + parentSets.put(region, parentId); + } + } catch (NullPointerException e) { + log.log(Level.WARNING, + "Unexpected NullPointerException encountered during parsing for the region '" + id + "'!\n" + + "Here is what the region data looks like:\n\n" + toYamlOutput(entry.getValue().getMap()) + + "\n\nNote: This region will disappear as a result!", e); + } + } + + // Relink parents + RegionStoreUtils.relinkParents(loaded, parentSets); + + return new HashSet(loaded.values()); + } + + @Override + public void saveAll(Set regions) throws IOException { + checkNotNull(regions); + + File tempFile = new File(file.getParentFile(), file.getName() + ".tmp"); + YAMLProcessor config = createYamlProcessor(tempFile); + + config.clear(); + + YAMLNode regionsNode = config.addNode("regions"); + Map map = regionsNode.getMap(); + + for (ProtectedRegion region : regions) { + Map nodeMap = new HashMap(); + map.put(region.getId(), nodeMap); + YAMLNode node = new YAMLNode(nodeMap, false); + + if (region instanceof ProtectedCuboidRegion) { + ProtectedCuboidRegion cuboid = (ProtectedCuboidRegion) region; + node.setProperty("type", "cuboid"); + node.setProperty("min", cuboid.getMinimumPoint()); + node.setProperty("max", cuboid.getMaximumPoint()); + } else if (region instanceof ProtectedPolygonalRegion) { + ProtectedPolygonalRegion poly = (ProtectedPolygonalRegion) region; + node.setProperty("type", "poly2d"); + node.setProperty("min-y", poly.getMinimumPoint().getBlockY()); + node.setProperty("max-y", poly.getMaximumPoint().getBlockY()); + + List> points = new ArrayList>(); + for (BlockVector2D point : poly.getPoints()) { + Map data = new HashMap(); + data.put("x", point.getBlockX()); + data.put("z", point.getBlockZ()); + points.add(data); + } + + node.setProperty("points", points); + } else if (region instanceof GlobalProtectedRegion) { + node.setProperty("type", "global"); + } else { + node.setProperty("type", region.getClass().getCanonicalName()); + } + + node.setProperty("priority", region.getPriority()); + node.setProperty("flags", getFlagData(region)); + node.setProperty("owners", getDomainData(region.getOwners())); + node.setProperty("members", getDomainData(region.getMembers())); + + ProtectedRegion parent = region.getParent(); + if (parent != null) { + node.setProperty("parent", parent.getId()); + } + } + + config.setHeader(FILE_HEADER); + config.save(); + + //noinspection ResultOfMethodCallIgnored + file.delete(); + if (!tempFile.renameTo(file)) { + throw new IOException("Failed to rename temporary regions file to " + file.getAbsolutePath()); + } + } + + @Override + public void saveChanges(RegionDifference difference) throws DifferenceSaveException { + throw new DifferenceSaveException("Not supported"); + } + + @SuppressWarnings("deprecation") + private DefaultDomain parseDomain(YAMLNode node) { + if (node == null) { + return new DefaultDomain(); + } + + DefaultDomain domain = new DefaultDomain(); + + for (String name : node.getStringList("players", null)) { + domain.addPlayer(name); + } + + for (String stringId : node.getStringList("unique-ids", null)) { + try { + domain.addPlayer(UUID.fromString(stringId)); + } catch (IllegalArgumentException e) { + log.log(Level.WARNING, "Failed to parse UUID '" + stringId + "'", e); + } + } + + for (String name : node.getStringList("groups", null)) { + domain.addGroup(name); + } + + return domain; + } + + private Map getFlagData(ProtectedRegion region) { + Map flagData = new HashMap(); + + for (Map.Entry, Object> entry : region.getFlags().entrySet()) { + Flag flag = entry.getKey(); + addMarshalledFlag(flagData, flag, entry.getValue()); + } + + return flagData; + } + + private void setFlags(ProtectedRegion region, YAMLNode flagsData) { + if (flagsData != null) { + RegionStoreUtils.trySetFlagMap(region, flagsData.getMap()); + } + } + + @SuppressWarnings("unchecked") + private void addMarshalledFlag(Map flagData, Flag flag, Object val) { + if (val == null) { + return; + } + + flagData.put(flag.getName(), flag.marshal((V) val)); + } + + private Map getDomainData(DefaultDomain domain) { + Map domainData = new HashMap(); + + //noinspection deprecation + setDomainData(domainData, "players", domain.getPlayers()); + setDomainData(domainData, "unique-ids", domain.getUniqueIds()); + setDomainData(domainData, "groups", domain.getGroups()); + + return domainData; + } + + private void setDomainData(Map domainData, String key, Set domain) { + if (domain.isEmpty()) { + return; + } + + List list = new ArrayList(); + + for (Object str : domain) { + list.add(String.valueOf(str)); + } + + domainData.put(key, list); + } + + /** + * Create a YAML processer instance. + * + * @param file the file + * @return a processor instance + */ + private YAMLProcessor createYamlProcessor(File file) { + checkNotNull(file); + return new YAMLProcessor(file, false, YAMLFormat.COMPACT); + } + + /** + * Dump the given object as YAML for debugging purposes. + * + * @param object the object + * @return the YAML string or an error string if dumping fals + */ + private static String toYamlOutput(Object object) { + try { + return ERROR_DUMP_YAML.dump(object).replaceAll("(?m)^", "\t"); + } catch (Throwable t) { + return ""; + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DataLoader.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DataLoader.java new file mode 100644 index 00000000..33fc1cdb --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DataLoader.java @@ -0,0 +1,335 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.sql; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Table; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.managers.storage.RegionStoreUtils; +import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.io.Closer; +import com.sk89q.worldguard.util.sql.DataSourceConfig; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +class DataLoader { + + private static final Logger log = Logger.getLogger(DataLoader.class.getCanonicalName()); + + final Connection conn; + final DataSourceConfig config; + final int worldId; + + private final Map loaded = new HashMap(); + private final Map parentSets = new HashMap(); + private final Yaml yaml = SQLRegionStore.createYaml(); + + DataLoader(SQLRegionStore regionStore, Connection conn) throws SQLException { + checkNotNull(regionStore); + + this.conn = conn; + this.config = regionStore.getDataSourceConfig(); + this.worldId = regionStore.getWorldId(); + } + + public Set load() throws SQLException { + loadCuboids(); + loadPolygons(); + loadGlobals(); + + loadFlags(); + loadDomainUsers(); + loadDomainGroups(); + + RegionStoreUtils.relinkParents(loaded, parentSets); + + return new HashSet(loaded.values()); + } + + private void loadCuboids() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "SELECT g.min_z, g.min_y, g.min_x, " + + " g.max_z, g.max_y, g.max_x, " + + " r.id, r.priority, p.id AS parent " + + "FROM " + config.getTablePrefix() + "region_cuboid AS g " + + "LEFT JOIN " + config.getTablePrefix() + "region AS r " + + " ON (g.region_id = r.id AND g.world_id = r.world_id) " + + "LEFT JOIN " + config.getTablePrefix() + "region AS p " + + " ON (r.parent = p.id AND r.world_id = p.world_id) " + + "WHERE r.world_id = " + worldId)); + + ResultSet rs = closer.register(stmt.executeQuery()); + + while (rs.next()) { + Vector pt1 = new Vector(rs.getInt("min_x"), rs.getInt("min_y"), rs.getInt("min_z")); + Vector pt2 = new Vector(rs.getInt("max_x"), rs.getInt("max_y"), rs.getInt("max_z")); + + BlockVector min = Vector.getMinimum(pt1, pt2).toBlockVector(); + BlockVector max = Vector.getMaximum(pt1, pt2).toBlockVector(); + ProtectedRegion region = new ProtectedCuboidRegion(rs.getString("id"), min, max); + + region.setPriority(rs.getInt("priority")); + + loaded.put(rs.getString("id"), region); + + String parentId = rs.getString("parent"); + if (parentId != null) { + parentSets.put(region, parentId); + } + } + } finally { + closer.closeQuietly(); + } + } + + private void loadGlobals() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "SELECT r.id, r.priority, p.id AS parent " + + "FROM " + config.getTablePrefix() + "region AS r " + + "LEFT JOIN " + config.getTablePrefix() + "region AS p " + + " ON (r.parent = p.id AND r.world_id = p.world_id) " + + "WHERE r.type = 'global' AND r.world_id = " + worldId)); + + ResultSet rs = closer.register(stmt.executeQuery()); + + while (rs.next()) { + ProtectedRegion region = new GlobalProtectedRegion(rs.getString("id")); + + region.setPriority(rs.getInt("priority")); + + loaded.put(rs.getString("id"), region); + + String parentId = rs.getString("parent"); + if (parentId != null) { + parentSets.put(region, parentId); + } + } + } finally { + closer.closeQuietly(); + } + } + + private void loadPolygons() throws SQLException { + ListMultimap pointsCache = ArrayListMultimap.create(); + + // First get all the vertices and store them in memory + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "SELECT region_id, x, z " + + "FROM " + config.getTablePrefix() + "region_poly2d_point " + + "WHERE world_id = " + worldId)); + + ResultSet rs = closer.register(stmt.executeQuery()); + + while (rs.next()) { + pointsCache.put(rs.getString("region_id"), new BlockVector2D(rs.getInt("x"), rs.getInt("z"))); + } + } finally { + closer.closeQuietly(); + } + + // Now we pull the regions themselves + closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "SELECT g.min_y, g.max_y, r.id, r.priority, p.id AS parent " + + "FROM " + config.getTablePrefix() + "region_poly2d AS g " + + "LEFT JOIN " + config.getTablePrefix() + "region AS r " + + " ON (g.region_id = r.id AND g.world_id = r.world_id) " + + "LEFT JOIN " + config.getTablePrefix() + "region AS p " + + " ON (r.parent = p.id AND r.world_id = p.world_id) " + + "WHERE r.world_id = " + worldId + )); + + ResultSet rs = closer.register(stmt.executeQuery()); + + while (rs.next()) { + String id = rs.getString("id"); + + // Get the points from the cache + List points = pointsCache.get(id); + + if (points.size() < 3) { + log.log(Level.WARNING, "Invalid polygonal region '" + id + "': region has " + points.size() + " point(s) (less than the required 3). Skipping this region."); + continue; + } + + Integer minY = rs.getInt("min_y"); + Integer maxY = rs.getInt("max_y"); + + ProtectedRegion region = new ProtectedPolygonalRegion(id, points, minY, maxY); + region.setPriority(rs.getInt("priority")); + + loaded.put(id, region); + + String parentId = rs.getString("parent"); + if (parentId != null) { + parentSets.put(region, parentId); + } + } + } finally { + closer.closeQuietly(); + } + } + + private void loadFlags() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "SELECT region_id, flag, value " + + "FROM " + config.getTablePrefix() + "region_flag " + + "WHERE world_id = " + worldId)); + + ResultSet rs = closer.register(stmt.executeQuery()); + + Table data = HashBasedTable.create(); + while (rs.next()) { + data.put( + rs.getString("region_id"), + rs.getString("flag"), + unmarshalFlagValue(rs.getString("value"))); + } + + for (Map.Entry> entry : data.rowMap().entrySet()) { + ProtectedRegion region = loaded.get(entry.getKey()); + if (region != null) { + RegionStoreUtils.trySetFlagMap(region, entry.getValue()); + } else { + throw new RuntimeException("An unexpected error occurred (loaded.get() returned null)"); + } + } + } finally { + closer.closeQuietly(); + } + } + + private void loadDomainUsers() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "SELECT p.region_id, u.name, u.uuid, p.owner " + + "FROM " + config.getTablePrefix() + "region_players AS p " + + "LEFT JOIN " + config.getTablePrefix() + "user AS u " + + " ON (p.user_id = u.id) " + + "WHERE p.world_id = " + worldId)); + + ResultSet rs = closer.register(stmt.executeQuery()); + + while (rs.next()) { + ProtectedRegion region = loaded.get(rs.getString("region_id")); + + if (region != null) { + DefaultDomain domain; + + if (rs.getBoolean("owner")) { + domain = region.getOwners(); + } else { + domain = region.getMembers(); + } + + String name = rs.getString("name"); + String uuid = rs.getString("uuid"); + + if (name != null) { + //noinspection deprecation + domain.addPlayer(name); + } else if (uuid != null) { + try { + domain.addPlayer(UUID.fromString(uuid)); + } catch (IllegalArgumentException e) { + log.warning("Invalid UUID '" + uuid + "' for region '" + region.getId() + "'"); + } + } + } + } + } finally { + closer.closeQuietly(); + } + } + + private void loadDomainGroups() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "SELECT rg.region_id, g.name, rg.owner " + + "FROM `" + config.getTablePrefix() + "region_groups` AS rg " + + "INNER JOIN `" + config.getTablePrefix() + "group` AS g ON (rg.group_id = g.id) " + + // LEFT JOIN is returning NULLS for reasons unknown + "AND rg.world_id = " + this.worldId)); + + ResultSet rs = closer.register(stmt.executeQuery()); + + while (rs.next()) { + ProtectedRegion region = loaded.get(rs.getString("region_id")); + + if (region != null) { + DefaultDomain domain; + + if (rs.getBoolean("owner")) { + domain = region.getOwners(); + } else { + domain = region.getMembers(); + } + + domain.addGroup(rs.getString("name")); + } + } + } finally { + closer.closeQuietly(); + } + } + + private Object unmarshalFlagValue(String rawValue) { + try { + return yaml.load(rawValue); + } catch (YAMLException e) { + return String.valueOf(rawValue); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DataUpdater.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DataUpdater.java new file mode 100644 index 00000000..2420f9be --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DataUpdater.java @@ -0,0 +1,168 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.sql; + +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.io.Closer; +import com.sk89q.worldguard.util.sql.DataSourceConfig; + +import javax.annotation.Nullable; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; + +class DataUpdater { + + final Connection conn; + final DataSourceConfig config; + final int worldId; + final DomainTableCache domainTableCache; + + DataUpdater(SQLRegionStore regionStore, Connection conn) throws SQLException { + checkNotNull(regionStore); + + this.conn = conn; + this.config = regionStore.getDataSourceConfig(); + this.worldId = regionStore.getWorldId(); + this.domainTableCache = new DomainTableCache(config, conn); + } + + /** + * Save the given set of regions to the database. + * + * @param regions a set of regions to save + * @throws SQLException thrown on a fatal SQL error + */ + public void saveAll(Set regions) throws SQLException { + executeSave(regions, null); + } + + /** + * Save the given set of regions to the database. + * + * @param changed a set of changed regions + * @param removed a set of removed regions + * @throws SQLException thrown on a fatal SQL error + */ + public void saveChanges(Set changed, Set removed) throws SQLException { + executeSave(changed, removed); + } + + /** + * Execute the save operation. + * + * @param toUpdate a list of regions to update + * @param toRemove a list of regions to remove, or {@code null} to remove + * regions in the database that were not in {@code toUpdate} + * @throws SQLException thrown on a fatal SQL error + */ + private void executeSave(Set toUpdate, @Nullable Set toRemove) throws SQLException { + Map existing = getExistingRegions(); // Map of regions that already exist in the database + + // WARNING: The database uses utf8_bin for its collation, so + // we have to remove the exact same ID (it is case-sensitive!) + + try { + conn.setAutoCommit(false); + + RegionUpdater updater = new RegionUpdater(this); + RegionInserter inserter = new RegionInserter(this); + RegionRemover remover = new RegionRemover(this); + + for (ProtectedRegion region : toUpdate) { + if (toRemove != null && toRemove.contains(region)) { + continue; + } + + String currentType = existing.get(region.getId()); + + // Check if the region + if (currentType != null) { // Region exists in the database + existing.remove(region.getId()); + + updater.updateRegionType(region); + remover.removeGeometry(region, currentType); + } else { + inserter.insertRegionType(region); + } + + inserter.insertGeometry(region); + updater.updateRegionProperties(region); + } + + if (toRemove != null) { + List removeNames = new ArrayList(); + for (ProtectedRegion region : toRemove) { + removeNames.add(region.getId()); + } + remover.removeRegionsEntirely(removeNames); + } else { + remover.removeRegionsEntirely(existing.keySet()); + } + + remover.apply(); + inserter.apply(); + updater.apply(); + + conn.commit(); + } catch (SQLException e) { + conn.rollback(); + throw e; + } catch (RuntimeException e) { + conn.rollback(); + throw e; + } finally { + conn.setAutoCommit(true); + } + + // Connection to be closed by caller + } + + private Map getExistingRegions() throws SQLException { + Map existing = new HashMap(); + + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "SELECT id, type " + + "FROM " + config.getTablePrefix() + "region " + + "WHERE world_id = " + worldId)); + + ResultSet resultSet = closer.register(stmt.executeQuery()); + + while (resultSet.next()) { + existing.put(resultSet.getString("id"), resultSet.getString("type")); + } + + return existing; + } finally { + closer.closeQuietly(); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DomainTableCache.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DomainTableCache.java new file mode 100644 index 00000000..9ac4cf35 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/DomainTableCache.java @@ -0,0 +1,53 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.sql; + +import com.sk89q.worldguard.protection.managers.storage.sql.TableCache.GroupNameCache; +import com.sk89q.worldguard.protection.managers.storage.sql.TableCache.UserNameCache; +import com.sk89q.worldguard.protection.managers.storage.sql.TableCache.UserUuidCache; +import com.sk89q.worldguard.util.sql.DataSourceConfig; + +import java.sql.Connection; + +class DomainTableCache { + + private final UserNameCache userNameCache; + private final UserUuidCache userUuidCache; + private final GroupNameCache groupNameCache; + + DomainTableCache(DataSourceConfig config, Connection conn) { + userNameCache = new UserNameCache(config, conn); + userUuidCache = new UserUuidCache(config, conn); + groupNameCache = new GroupNameCache(config, conn); + } + + public UserNameCache getUserNameCache() { + return userNameCache; + } + + public UserUuidCache getUserUuidCache() { + return userUuidCache; + } + + public GroupNameCache getGroupNameCache() { + return groupNameCache; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionInserter.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionInserter.java new file mode 100644 index 00000000..8886e458 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionInserter.java @@ -0,0 +1,189 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.sql; + +import com.google.common.collect.Lists; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.BlockVector2D; +import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.io.Closer; +import com.sk89q.worldguard.util.sql.DataSourceConfig; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Insert regions that don't exist in the database yet. + */ +class RegionInserter { + + private final DataSourceConfig config; + private final Connection conn; + private final int worldId; + private final List all = new ArrayList(); + private final List cuboids = new ArrayList(); + private final List polygons = new ArrayList(); + + RegionInserter(DataUpdater updater) { + this.config = updater.config; + this.conn = updater.conn; + this.worldId = updater.worldId; + } + + public void insertRegionType(ProtectedRegion region) throws SQLException { + all.add(region); + } + + @SuppressWarnings("StatementWithEmptyBody") + public void insertGeometry(ProtectedRegion region) throws SQLException { + if (region instanceof ProtectedCuboidRegion) { + cuboids.add((ProtectedCuboidRegion) region); + + } else if (region instanceof ProtectedPolygonalRegion) { + polygons.add((ProtectedPolygonalRegion) region); + + } else if (region instanceof GlobalProtectedRegion) { + // Nothing special to do about them + + } else { + throw new IllegalArgumentException("Unknown type of region: " + region.getClass().getName()); + } + } + + private void insertRegionTypes() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "INSERT INTO " + config.getTablePrefix() + "region " + + "(id, world_id, type, priority, parent) " + + "VALUES " + + "(?, ?, ?, ?, NULL)")); + + for (List partition : Lists.partition(all, StatementBatch.MAX_BATCH_SIZE)) { + for (ProtectedRegion region : partition) { + stmt.setString(1, region.getId()); + stmt.setInt(2, worldId); + stmt.setString(3, SQLRegionStore.getRegionTypeName(region)); + stmt.setInt(4, region.getPriority()); + stmt.addBatch(); + } + + stmt.executeBatch(); + } + } finally { + closer.closeQuietly(); + } + } + + private void insertCuboids() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "INSERT INTO " + config.getTablePrefix() + "region_cuboid " + + "(region_id, world_id, min_z, min_y, min_x, max_z, max_y, max_x ) " + + "VALUES " + + "(?, " + worldId + ", ?, ?, ?, ?, ?, ?)")); + + for (List partition : Lists.partition(cuboids, StatementBatch.MAX_BATCH_SIZE)) { + for (ProtectedCuboidRegion region : partition) { + BlockVector min = region.getMinimumPoint(); + BlockVector max = region.getMaximumPoint(); + + stmt.setString(1, region.getId()); + stmt.setInt(2, min.getBlockZ()); + stmt.setInt(3, min.getBlockY()); + stmt.setInt(4, min.getBlockX()); + stmt.setInt(5, max.getBlockZ()); + stmt.setInt(6, max.getBlockY()); + stmt.setInt(7, max.getBlockX()); + stmt.addBatch(); + } + + stmt.executeBatch(); + } + } finally { + closer.closeQuietly(); + } + } + + private void insertPolygons() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "INSERT INTO " + config.getTablePrefix() + "region_poly2d " + + "(region_id, world_id, max_y, min_y) " + + "VALUES " + + "(?, " + worldId + ", ?, ?)")); + + for (List partition : Lists.partition(polygons, StatementBatch.MAX_BATCH_SIZE)) { + for (ProtectedPolygonalRegion region : partition) { + stmt.setString(1, region.getId()); + stmt.setInt(2, region.getMaximumPoint().getBlockY()); + stmt.setInt(3, region.getMinimumPoint().getBlockY()); + stmt.addBatch(); + } + + stmt.executeBatch(); + } + } finally { + closer.closeQuietly(); + } + } + + private void insertPolygonVertices() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "INSERT INTO " + config.getTablePrefix() + "region_poly2d_point" + + "(region_id, world_id, z, x) " + + "VALUES " + + "(?, " + worldId + ", ?, ?)")); + + StatementBatch batch = new StatementBatch(stmt, StatementBatch.MAX_BATCH_SIZE); + + for (ProtectedPolygonalRegion region : polygons) { + for (BlockVector2D point : region.getPoints()) { + stmt.setString(1, region.getId()); + stmt.setInt(2, point.getBlockZ()); + stmt.setInt(3, point.getBlockX()); + batch.addBatch(); + } + } + + batch.executeRemaining(); + } finally { + closer.closeQuietly(); + } + } + + public void apply() throws SQLException { + insertRegionTypes(); + insertCuboids(); + insertPolygons(); + insertPolygonVertices(); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionRemover.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionRemover.java new file mode 100644 index 00000000..f6de8ccd --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionRemover.java @@ -0,0 +1,88 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.sql; + +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.io.Closer; +import com.sk89q.worldguard.util.sql.DataSourceConfig; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +class RegionRemover { + + private final DataSourceConfig config; + private final Connection conn; + private final int worldId; + private final List regionQueue = new ArrayList(); + private final List cuboidGeometryQueue = new ArrayList(); + private final List polygonGeometryQueue = new ArrayList(); + + RegionRemover(DataUpdater updater) { + this.config = updater.config; + this.conn = updater.conn; + this.worldId = updater.worldId; + } + + public void removeRegionsEntirely(Collection names) { + regionQueue.addAll(names); + } + + public void removeGeometry(ProtectedRegion region, String currentType) { + if (currentType.equals("cuboid")) { + cuboidGeometryQueue.add(region.getId()); + } else if (currentType.equals("poly2d")) { + polygonGeometryQueue.add(region.getId()); + } else if (currentType.equals("global")) { + // Nothing to do + } else { + throw new RuntimeException("Unknown type of region in the database: " + currentType); + } + + } + + private void removeRows(Collection names, String table, String field) throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "DELETE FROM " + config.getTablePrefix() + table + " WHERE " + field + " = ? AND world_id = " + worldId)); + + StatementBatch batch = new StatementBatch(stmt, StatementBatch.MAX_BATCH_SIZE); + for (String name : names) { + stmt.setString(1, name); + batch.addBatch(); + } + + batch.executeRemaining(); + } finally { + closer.closeQuietly(); + } + } + + public void apply() throws SQLException { + removeRows(regionQueue, "region", "id"); + removeRows(cuboidGeometryQueue, "region_cuboid", "region_id"); + removeRows(polygonGeometryQueue, "region_poly2d", "region_id"); + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionUpdater.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionUpdater.java new file mode 100644 index 00000000..7f15ba63 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/RegionUpdater.java @@ -0,0 +1,341 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.sql; + +import com.google.common.collect.Lists; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.io.Closer; +import com.sk89q.worldguard.util.sql.DataSourceConfig; +import org.yaml.snakeyaml.Yaml; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Updates region data that needs to be updated for both inserts and updates. + */ +class RegionUpdater { + + private static final Logger log = Logger.getLogger(RegionUpdater.class.getCanonicalName()); + private final DataSourceConfig config; + private final Connection conn; + private final int worldId; + private final DomainTableCache domainTableCache; + + private final Set userNames = new HashSet(); + private final Set userUuids = new HashSet(); + private final Set groupNames = new HashSet(); + + private final Yaml yaml = SQLRegionStore.createYaml(); + + private final List typesToUpdate = new ArrayList(); + private final List parentsToSet = new ArrayList(); + private final List flagsToReplace = new ArrayList(); + private final List domainsToReplace = new ArrayList(); + + RegionUpdater(DataUpdater updater) { + this.config = updater.config; + this.conn = updater.conn; + this.worldId = updater.worldId; + this.domainTableCache = updater.domainTableCache; + } + + public void updateRegionType(ProtectedRegion region) { + typesToUpdate.add(region); + } + + public void updateRegionProperties(ProtectedRegion region) { + if (region.getParent() != null) { + parentsToSet.add(region); + } + + flagsToReplace.add(region); + domainsToReplace.add(region); + + addDomain(region.getOwners()); + addDomain(region.getMembers()); + } + + private void addDomain(DefaultDomain domain) { + //noinspection deprecation + for (String name : domain.getPlayers()) { + userNames.add(name.toLowerCase()); + } + + for (UUID uuid : domain.getUniqueIds()) { + userUuids.add(uuid); + } + + for (String name : domain.getGroups()) { + groupNames.add(name.toLowerCase()); + } + } + + private void setParents() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "UPDATE " + config.getTablePrefix() + "region " + + "SET parent = ? " + + "WHERE id = ? AND world_id = " + worldId)); + + for (List partition : Lists.partition(parentsToSet, StatementBatch.MAX_BATCH_SIZE)) { + for (ProtectedRegion region : partition) { + ProtectedRegion parent = region.getParent(); + if (parent != null) { // Parent would be null due to a race condition + stmt.setString(1, parent.getId()); + stmt.setString(2, region.getId()); + stmt.addBatch(); + } + } + + stmt.executeBatch(); + } + } finally { + closer.closeQuietly(); + } + } + + private void replaceFlags() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "DELETE FROM " + config.getTablePrefix() + "region_flag " + + "WHERE region_id = ? " + + "AND world_id = " + worldId)); + + for (List partition : Lists.partition(flagsToReplace, StatementBatch.MAX_BATCH_SIZE)) { + for (ProtectedRegion region : partition) { + stmt.setString(1, region.getId()); + stmt.addBatch(); + } + + stmt.executeBatch(); + } + } finally { + closer.closeQuietly(); + } + + closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "INSERT INTO " + config.getTablePrefix() + "region_flag " + + "(id, region_id, world_id, flag, value) " + + "VALUES " + + "(null, ?, " + worldId + ", ?, ?)")); + + StatementBatch batch = new StatementBatch(stmt, StatementBatch.MAX_BATCH_SIZE); + + for (ProtectedRegion region : flagsToReplace) { + for (Map.Entry, Object> entry : region.getFlags().entrySet()) { + if (entry.getValue() == null) continue; + + Object flag = marshalFlagValue(entry.getKey(), entry.getValue()); + + stmt.setString(1, region.getId()); + stmt.setString(2, entry.getKey().getName()); + stmt.setObject(3, flag); + batch.addBatch(); + } + } + + batch.executeRemaining(); + } finally { + closer.closeQuietly(); + } + } + + private void replaceDomainUsers() throws SQLException { + // Remove users + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "DELETE FROM " + config.getTablePrefix() + "region_players " + + "WHERE region_id = ? " + + "AND world_id = " + worldId)); + + for (List partition : Lists.partition(domainsToReplace, StatementBatch.MAX_BATCH_SIZE)) { + for (ProtectedRegion region : partition) { + stmt.setString(1, region.getId()); + stmt.addBatch(); + } + + stmt.executeBatch(); + } + } finally { + closer.closeQuietly(); + } + + // Add users + closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "INSERT INTO " + config.getTablePrefix() + "region_players " + + "(region_id, world_id, user_id, owner) " + + "VALUES (?, " + worldId + ", ?, ?)")); + + StatementBatch batch = new StatementBatch(stmt, StatementBatch.MAX_BATCH_SIZE); + + for (ProtectedRegion region : domainsToReplace) { + insertDomainUsers(stmt, batch, region, region.getMembers(), false); // owner = false + insertDomainUsers(stmt, batch, region, region.getOwners(), true); // owner = true + } + + batch.executeRemaining(); + } finally { + closer.closeQuietly(); + } + } + + private void insertDomainUsers(PreparedStatement stmt, StatementBatch batch, ProtectedRegion region, DefaultDomain domain, boolean owner) throws SQLException { + //noinspection deprecation + for (String name : domain.getPlayers()) { + Integer id = domainTableCache.getUserNameCache().find(name); + if (id != null) { + stmt.setString(1, region.getId()); + stmt.setInt(2, id); + stmt.setBoolean(3, owner); + batch.addBatch(); + } else { + log.log(Level.WARNING, "Did not find an ID for the user identified as '" + name + "'"); + } + } + + for (UUID uuid : domain.getUniqueIds()) { + Integer id = domainTableCache.getUserUuidCache().find(uuid); + if (id != null) { + stmt.setString(1, region.getId()); + stmt.setInt(2, id); + stmt.setBoolean(3, owner); + batch.addBatch(); + } else { + log.log(Level.WARNING, "Did not find an ID for the user identified by '" + uuid + "'"); + } + } + } + + private void replaceDomainGroups() throws SQLException { + // Remove groups + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "DELETE FROM " + config.getTablePrefix() + "region_groups " + + "WHERE region_id = ? " + + "AND world_id = " + worldId)); + + for (List partition : Lists.partition(domainsToReplace, StatementBatch.MAX_BATCH_SIZE)) { + for (ProtectedRegion region : partition) { + stmt.setString(1, region.getId()); + stmt.addBatch(); + } + + stmt.executeBatch(); + } + } finally { + closer.closeQuietly(); + } + + // Add groups + closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "INSERT INTO " + config.getTablePrefix() + "region_groups " + + "(region_id, world_id, group_id, owner) " + + "VALUES (?, " + worldId + ", ?, ?)")); + + StatementBatch batch = new StatementBatch(stmt, StatementBatch.MAX_BATCH_SIZE); + + for (ProtectedRegion region : domainsToReplace) { + insertDomainGroups(stmt, batch, region, region.getMembers(), false); // owner = false + insertDomainGroups(stmt, batch, region, region.getOwners(), true); // owner = true + } + + batch.executeRemaining(); + } finally { + closer.closeQuietly(); + } + } + + private void insertDomainGroups(PreparedStatement stmt, StatementBatch batch, ProtectedRegion region, DefaultDomain domain, boolean owner) throws SQLException { + for (String name : domain.getGroups()) { + Integer id = domainTableCache.getGroupNameCache().find(name); + if (id != null) { + stmt.setString(1, region.getId()); + stmt.setInt(2, id); + stmt.setBoolean(3, owner); + batch.addBatch(); + } else { + log.log(Level.WARNING, "Did not find an ID for the group identified as '" + name + "'"); + } + } + } + + private void updateRegionTypes() throws SQLException { + Closer closer = Closer.create(); + try { + PreparedStatement stmt = closer.register(conn.prepareStatement( + "UPDATE " + config.getTablePrefix() + "region " + + "SET type = ?, priority = ?, parent = NULL " + + "WHERE id = ? AND world_id = " + worldId)); + + for (List partition : Lists.partition(typesToUpdate, StatementBatch.MAX_BATCH_SIZE)) { + for (ProtectedRegion region : partition) { + stmt.setString(1, SQLRegionStore.getRegionTypeName(region)); + stmt.setInt(2, region.getPriority()); + stmt.setString(3, region.getId()); + stmt.addBatch(); + } + + stmt.executeBatch(); + } + } finally { + closer.closeQuietly(); + } + } + + public void apply() throws SQLException { + domainTableCache.getUserNameCache().fetch(userNames); + domainTableCache.getUserUuidCache().fetch(userUuids); + domainTableCache.getGroupNameCache().fetch(groupNames); + + updateRegionTypes(); + setParents(); + replaceFlags(); + replaceDomainUsers(); + replaceDomainGroups(); + } + + @SuppressWarnings("unchecked") + private Object marshalFlagValue(Flag flag, Object val) { + return yaml.dump(flag.marshal((V) val)); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/SQLRegionStore.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/SQLRegionStore.java new file mode 100644 index 00000000..2da61c0a --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/SQLRegionStore.java @@ -0,0 +1,375 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.storage.sql; + +import com.jolbox.bonecp.BoneCP; +import com.jolbox.bonecp.BoneCPConfig; +import com.sk89q.worldguard.protection.managers.RegionDifference; +import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException; +import com.sk89q.worldguard.protection.managers.storage.RegionStore; +import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.io.Closer; +import com.sk89q.worldguard.util.sql.DataSourceConfig; +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.FlywayException; +import org.flywaydb.core.api.MigrationVersion; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.DumperOptions.FlowStyle; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.representer.Representer; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Stores region data into a SQL database in a highly normalized fashion. + */ +public class SQLRegionStore implements RegionStore { + + private static final Logger log = Logger.getLogger(SQLRegionStore.class.getCanonicalName()); + + private final BoneCP connectionPool; + private final DataSourceConfig config; + private final int worldId; + + /** + * Create a new instance. + * + * @param config a configuration object that configures a {@link Connection} + * @param connectionPool a connection pool + * @param worldName the name of the world to store regions by + * @throws IOException thrown on error + */ + public SQLRegionStore(DataSourceConfig config, BoneCP connectionPool, String worldName) throws IOException { + checkNotNull(config); + checkNotNull(connectionPool); + checkNotNull(worldName); + + this.config = config; + this.connectionPool = connectionPool; + + try { + migrate(); + } catch (FlywayException e) { + throw new IOException("Failed to migrate tables", e); + } catch (SQLException e) { + throw new IOException("Failed to migrate tables", e); + } + + try { + worldId = chooseWorldId(worldName); + } catch (SQLException e) { + throw new IOException("Failed to choose the ID for this world", e); + } + } + + /** + * Attempt to migrate the tables to the latest version. + * + * @throws SQLException thrown on SQL errors + */ + private void migrate() throws SQLException { + Closer closer = Closer.create(); + Connection conn = closer.register(getConnection()); + + try { + // Check some tables + boolean tablesExist; + boolean isRecent; + boolean isBeforeMigrations; + boolean hasMigrations; + + try { + tablesExist = tryQuery(conn, "SELECT * FROM " + config.getTablePrefix() + "region_cuboid LIMIT 1"); + isRecent = tryQuery(conn, "SELECT world_id FROM " + config.getTablePrefix() + "region_cuboid LIMIT 1"); + isBeforeMigrations = !tryQuery(conn, "SELECT uuid FROM " + config.getTablePrefix() + "user LIMIT 1"); + hasMigrations = tryQuery(conn, "SELECT * FROM " + config.getTablePrefix() + "migrations LIMIT 1"); + } finally { + closer.closeQuietly(); + } + + // We don't bother with migrating really old tables + if (tablesExist && !isRecent) { + throw new SQLException( + "Sorry, your tables are too old for the region SQL auto-migration system. " + + "Please run region_manual_update_20110325.sql on your database, which comes " + + "with WorldGuard or can be found in http://github.com/sk89q/worldguard"); + } + + // Our placeholders + Map placeHolders = new HashMap(); + placeHolders.put("tablePrefix", config.getTablePrefix()); + + BoneCPConfig boneConfig = connectionPool.getConfig(); + + Flyway flyway = new Flyway(); + + // The SQL support predates the usage of Flyway, so let's do some + // checks and issue messages appropriately + if (!hasMigrations) { + flyway.setInitOnMigrate(true); + + if (tablesExist) { + // Detect if this is before migrations + if (isBeforeMigrations) { + flyway.setInitVersion(MigrationVersion.fromVersion("1")); + } + + log.log(Level.INFO, "The SQL region tables exist but the migrations table seems to not exist yet. Creating the migrations table..."); + } else { + // By default, if Flyway sees any tables at all in the schema, it + // will assume that we are up to date, so we have to manually + // check ourselves and then ask Flyway to start from the beginning + // if our test table doesn't exist + flyway.setInitVersion(MigrationVersion.fromVersion("0")); + + log.log(Level.INFO, "SQL region tables do not exist: creating..."); + } + } + + flyway.setClassLoader(getClass().getClassLoader()); + flyway.setLocations("migrations/region/" + getMigrationFolderName()); + flyway.setDataSource(boneConfig.getJdbcUrl(), boneConfig.getUser(), boneConfig.getPassword()); + flyway.setTable(config.getTablePrefix() + "migrations"); + flyway.setPlaceholders(placeHolders); + flyway.setValidateOnMigrate(false); + flyway.migrate(); + } finally { + closer.closeQuietly(); + } + } + + /** + * Get the ID for this world from the database or pick a new one if + * an entry does not exist yet. + * + * @param worldName the world name + * @return a world ID + * @throws SQLException on a database access error + */ + private int chooseWorldId(String worldName) throws SQLException { + Closer closer = Closer.create(); + try { + Connection conn = closer.register(getConnection()); + + PreparedStatement stmt = closer.register(conn.prepareStatement( + "SELECT id FROM " + config.getTablePrefix() + "world WHERE name = ? LIMIT 0, 1")); + + stmt.setString(1, worldName); + ResultSet worldResult = closer.register(stmt.executeQuery()); + + if (worldResult.next()) { + return worldResult.getInt("id"); + } else { + PreparedStatement stmt2 = closer.register(conn.prepareStatement( + "INSERT INTO " + config.getTablePrefix() + "world (id, name) VALUES (null, ?)", + Statement.RETURN_GENERATED_KEYS)); + + stmt2.setString(1, worldName); + stmt2.execute(); + ResultSet generatedKeys = stmt2.getGeneratedKeys(); + + if (generatedKeys.next()) { + return generatedKeys.getInt(1); + } else { + throw new SQLException("Expected result, got none"); + } + } + } finally { + closer.closeQuietly(); + } + } + + /** + * Return a new database connection. + * + * @return a connection + * @throws SQLException thrown if the connection could not be created + */ + private Connection getConnection() throws SQLException { + return connectionPool.getConnection(); + } + + /** + * Get the data source config. + * + * @return the data source config + */ + public DataSourceConfig getDataSourceConfig() { + return config; + } + + /** + * Get the world ID. + * + * @return the world ID + */ + public int getWorldId() { + return worldId; + } + + /** + * Try to execute a query and return true if it did not fail. + * + * @param conn the connection to run the query on + * @param sql the SQL query + * @return true if the query did not end in error + */ + private boolean tryQuery(Connection conn, String sql) { + Closer closer = Closer.create(); + try { + Statement statement = closer.register(conn.createStatement()); + statement.executeQuery(sql); + return true; + } catch (SQLException ex) { + return false; + } finally { + closer.closeQuietly(); + } + } + + /** + * Get the identifier string for a region's type. + * + * @param region the region + * @return the ID of the region type + */ + static String getRegionTypeName(ProtectedRegion region) { + if (region instanceof ProtectedCuboidRegion) { + return "cuboid"; + } else if (region instanceof ProtectedPolygonalRegion) { + return "poly2d"; // Differs from getTypeName() on ProtectedRegion + } else if (region instanceof GlobalProtectedRegion) { + return "global"; + } else { + throw new IllegalArgumentException("Unexpected region type: " + region.getClass().getName()); + } + } + + /** + * Create a YAML dumper / parser. + * + * @return a YAML dumper / parser + */ + static Yaml createYaml() { + DumperOptions options = new DumperOptions(); + options.setIndent(2); + options.setDefaultFlowStyle(FlowStyle.FLOW); + Representer representer = new Representer(); + representer.setDefaultFlowStyle(FlowStyle.FLOW); + + // We have to use this in order to properly save non-string values + return new Yaml(new SafeConstructor(), new Representer(), options); + } + + /** + * Get the name of the folder in migrations/region containing the migration files. + * + * @return the migration folder name + */ + public String getMigrationFolderName() { + return "mysql"; + } + + @Override + public Set loadAll() throws IOException { + Closer closer = Closer.create(); + DataLoader loader; + + try { + try { + loader = new DataLoader(this, closer.register(getConnection())); + } catch (SQLException e) { + throw new IOException("Failed to get a connection to the database", e); + } + + try { + return loader.load(); + } catch (SQLException e) { + throw new IOException("Failed to save the region data to the database", e); + } + } finally { + closer.closeQuietly(); + } + } + + @Override + public void saveAll(Set regions) throws IOException { + checkNotNull(regions); + + Closer closer = Closer.create(); + DataUpdater updater; + + try { + try { + updater = new DataUpdater(this, closer.register(getConnection())); + } catch (SQLException e) { + throw new IOException("Failed to get a connection to the database", e); + } + + try { + updater.saveAll(regions); + } catch (SQLException e) { + throw new IOException("Failed to save the region data to the database", e); + } + } finally { + closer.closeQuietly(); + } + } + + @Override + public void saveChanges(RegionDifference difference) throws DifferenceSaveException, IOException { + checkNotNull(difference); + + Closer closer = Closer.create(); + DataUpdater updater; + + try { + try { + updater = new DataUpdater(this, closer.register(getConnection())); + } catch (SQLException e) { + throw new IOException("Failed to get a connection to the database", e); + } + + try { + updater.saveChanges(difference.getChanged(), difference.getRemoved()); + } catch (SQLException e) { + throw new IOException("Failed to save the region data to the database", e); + } + } finally { + closer.closeQuietly(); + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigratorKey.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/StatementBatch.java similarity index 52% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigratorKey.java rename to src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/StatementBatch.java index da85cdc4..7a43c3cd 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigratorKey.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/StatementBatch.java @@ -17,27 +17,38 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.databases.migrator; +package com.sk89q.worldguard.protection.managers.storage.sql; -public class MigratorKey { - public final String from; - public final String to; +import java.sql.PreparedStatement; +import java.sql.SQLException; - public MigratorKey(String from, String to) { - this.from = from; - this.to = to; +class StatementBatch { + + public static final int MAX_BATCH_SIZE = 100; + + private final PreparedStatement stmt; + private final int batchSize; + private int count = 0; + + StatementBatch(PreparedStatement stmt, int batchSize) { + this.stmt = stmt; + this.batchSize = batchSize; } - public boolean equals(Object o) { - MigratorKey other = (MigratorKey) o; - - return other.from.equals(this.from) && other.to.equals(this.to); + public void addBatch() throws SQLException { + stmt.addBatch(); + count++; + if (count > batchSize) { + stmt.executeBatch(); + count = 0; + } } - public int hashCode() { - int hash = 17; - hash = hash * 31 + this.from.hashCode(); - hash = hash * 31 + this.to.hashCode(); - return hash; + public void executeRemaining() throws SQLException { + if (count > 0) { + count = 0; + stmt.executeBatch(); + } } -} \ No newline at end of file + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/TableCache.java similarity index 61% rename from src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java rename to src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/TableCache.java index 0ae2ebd7..b0730359 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/mysql/UserRowCache.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/storage/sql/TableCache.java @@ -17,11 +17,12 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.databases.mysql; +package com.sk89q.worldguard.protection.managers.storage.sql; import com.google.common.collect.Lists; import com.sk89q.worldguard.internal.util.sql.StatementUtils; import com.sk89q.worldguard.util.io.Closer; +import com.sk89q.worldguard.util.sql.DataSourceConfig; import javax.annotation.Nullable; import java.sql.Connection; @@ -35,26 +36,66 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; -abstract class UserRowCache extends AbstractJob { +/** + * Stores a cache of entries from a table for fast lookup and + * creates new rows whenever required. + * + * @param the type of entry + */ +abstract class TableCache { + + private static final Logger log = Logger.getLogger(TableCache.class.getCanonicalName()); private static final int MAX_NUMBER_PER_QUERY = 100; private static final Object LOCK = new Object(); private final Map cache = new HashMap(); + private final DataSourceConfig config; + private final Connection conn; + private final String tableName; private final String fieldName; - protected UserRowCache(MySQLDatabaseImpl database, Connection conn, String fieldName) { - super(database, conn); + /** + * Create a new instance. + * + * @param config the data source config + * @param conn the connection to use + * @param tableName the table name + * @param fieldName the field name + */ + protected TableCache(DataSourceConfig config, Connection conn, String tableName, String fieldName) { + this.config = config; + this.conn = conn; + this.tableName = tableName; this.fieldName = fieldName; } + /** + * Convert from the type to the string representation. + * + * @param o the object + * @return the string representation + */ protected abstract String fromType(V o); + /** + * Convert from the string representation to the type. + * + * @param o the string + * @return the object + */ protected abstract V toType(String o); + /** + * Convert the object to the version that is stored as a key in the map. + * + * @param object the object + * @return the key version + */ protected abstract V toKey(V object); @Nullable @@ -62,6 +103,13 @@ public Integer find(V object) { return cache.get(object); } + /** + * Fetch from the database rows that match the given entries, otherwise + * create new entries and assign them an ID. + * + * @param entries a list of entries + * @throws SQLException thrown on SQL error + */ public void fetch(Collection entries) throws SQLException { synchronized (LOCK) { // Lock across all cache instances checkNotNull(entries); @@ -84,9 +132,9 @@ public void fetch(Collection entries) throws SQLException { try { PreparedStatement statement = closer.register(conn.prepareStatement( String.format( - "SELECT `user`.`id`, `user`.`" + fieldName + "` " + - "FROM `" + config.sqlTablePrefix + "user` AS `user` " + - "WHERE `" + fieldName + "` IN (%s)", + "SELECT id, " + fieldName + " " + + "FROM `" + config.getTablePrefix() + tableName + "` " + + "WHERE " + fieldName + " IN (%s)", StatementUtils.preparePlaceHolders(partition.size())))); String[] values = new String[partition.size()]; @@ -118,7 +166,7 @@ public void fetch(Collection entries) throws SQLException { Closer closer = Closer.create(); try { PreparedStatement statement = closer.register(conn.prepareStatement( - "INSERT INTO `" + config.sqlTablePrefix + "user` (`id`, `" + fieldName + "`) VALUES (null, ?)", + "INSERT INTO `" + config.getTablePrefix() + tableName + "` (id, " + fieldName + ") VALUES (null, ?)", Statement.RETURN_GENERATED_KEYS)); for (V entry : missing) { @@ -126,10 +174,10 @@ public void fetch(Collection entries) throws SQLException { statement.execute(); ResultSet generatedKeys = statement.getGeneratedKeys(); - if (generatedKeys.first()) { + if (generatedKeys.next()) { cache.put(toKey(entry), generatedKeys.getInt(1)); } else { - logger.warning("Could not get the database ID for user " + entry); + log.warning("Could not get the database ID for entry " + entry); } } } finally { @@ -139,9 +187,12 @@ public void fetch(Collection entries) throws SQLException { } } - static class NameRowCache extends UserRowCache { - protected NameRowCache(MySQLDatabaseImpl database, Connection conn) { - super(database, conn, "name"); + /** + * An index of user rows that utilize the name field. + */ + static class UserNameCache extends TableCache { + protected UserNameCache(DataSourceConfig config, Connection conn) { + super(config, conn, "user", "name"); } @Override @@ -160,9 +211,12 @@ protected String toKey(String object) { } } - static class UUIDRowCache extends UserRowCache { - protected UUIDRowCache(MySQLDatabaseImpl database, Connection conn) { - super(database, conn, "uuid"); + /** + * An index of user rows that utilize the UUID field. + */ + static class UserUuidCache extends TableCache { + protected UserUuidCache(DataSourceConfig config, Connection conn) { + super(config, conn, "user", "uuid"); } @Override @@ -181,4 +235,28 @@ protected UUID toKey(UUID object) { } } + /** + * An index of group rows. + */ + static class GroupNameCache extends TableCache { + protected GroupNameCache(DataSourceConfig config, Connection conn) { + super(config, conn, "group", "name"); + } + + @Override + protected String fromType(String o) { + return o; + } + + @Override + protected String toType(String o) { + return o; + } + + @Override + protected String toKey(String object) { + return object.toLowerCase(); + } + } + } diff --git a/src/main/java/com/sk89q/worldguard/protection/regions/GlobalProtectedRegion.java b/src/main/java/com/sk89q/worldguard/protection/regions/GlobalProtectedRegion.java index 7b32b6bc..32fe8944 100644 --- a/src/main/java/com/sk89q/worldguard/protection/regions/GlobalProtectedRegion.java +++ b/src/main/java/com/sk89q/worldguard/protection/regions/GlobalProtectedRegion.java @@ -19,25 +19,39 @@ package com.sk89q.worldguard.protection.regions; -import java.util.ArrayList; -import java.util.List; - import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector2D; import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.protection.UnsupportedIntersectionException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * A special region that is not quite "anywhere" (its volume is 0, it + * contains no positions, and it does not intersect with any other region). + * + *

Global regions, however, are used to specify a region with flags that + * are applied with the lowest priority.

+ */ public class GlobalProtectedRegion extends ProtectedRegion { + /** + * Create a new instance. + * + * @param id the ID + */ public GlobalProtectedRegion(String id) { super(id); min = new BlockVector(0, 0, 0); max = new BlockVector(0, 0, 0); } + @Override public List getPoints() { + // This doesn't make sense List pts = new ArrayList(); - pts.add(new BlockVector2D(min.getBlockX(),min.getBlockZ())); + pts.add(new BlockVector2D(min.getBlockX(), min.getBlockZ())); return pts; } @@ -48,18 +62,18 @@ public int volume() { @Override public boolean contains(Vector pt) { + // Global regions are handled separately so it must not contain any positions return false; } @Override - public String getTypeName() { - return "global"; + public RegionType getType() { + return RegionType.GLOBAL; } @Override - public List getIntersectingRegions( - List regions) - throws UnsupportedIntersectionException { + public List getIntersectingRegions(Collection regions) { + // Global regions are handled separately so it must not contain any positions return new ArrayList(); } diff --git a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedCuboidRegion.java b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedCuboidRegion.java index 767054bc..dd178b75 100644 --- a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedCuboidRegion.java +++ b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedCuboidRegion.java @@ -19,13 +19,15 @@ package com.sk89q.worldguard.protection.regions; -import java.util.ArrayList; -import java.util.List; - import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector2D; import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.protection.UnsupportedIntersectionException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; /** * Represents a cuboid region that can be protected. @@ -37,9 +39,9 @@ public class ProtectedCuboidRegion extends ProtectedRegion { /** * Construct a new instance of this cuboid region. * - * @param id The region id - * @param pt1 The first point of this region - * @param pt2 The second point of this region + * @param id the region id + * @param pt1 the first point of this region + * @param pt2 the second point of this region */ public ProtectedCuboidRegion(String id, BlockVector pt1, BlockVector pt2) { super(id); @@ -47,34 +49,37 @@ public ProtectedCuboidRegion(String id, BlockVector pt1, BlockVector pt2) { } /** - * Given any two points, sets the minimum and maximum points + * Given any two points, sets the minimum and maximum points. * - * @param pt1 The first point of this region - * @param pt2 The second point of this region + * @param position1 the first point of this region + * @param position2 the second point of this region */ - private void setMinMaxPoints(BlockVector pt1, BlockVector pt2) { + private void setMinMaxPoints(BlockVector position1, BlockVector position2) { + checkNotNull(position1); + checkNotNull(position2); + List points = new ArrayList(); - points.add(pt1); - points.add(pt2); + points.add(position1); + points.add(position2); setMinMaxPoints(points); } /** * Set the lower point of the cuboid. * - * @param pt The point to set as the minimum point + * @param position the point to set as the minimum point */ - public void setMinimumPoint(BlockVector pt) { - setMinMaxPoints(pt, max); + public void setMinimumPoint(BlockVector position) { + setMinMaxPoints(position, max); } /** * Set the upper point of the cuboid. * - * @param pt The point to set as the maximum point + * @param position the point to set as the maximum point */ - public void setMaximumPoint(BlockVector pt) { - setMinMaxPoints(min, pt); + public void setMaximumPoint(BlockVector position) { + setMinMaxPoints(min, position); } @Override @@ -103,34 +108,15 @@ public boolean contains(Vector pt) { && z >= min.getBlockZ() && z < max.getBlockZ()+1; } - - /* - public boolean intersectsWith(ProtectedRegion region) throws UnsupportedIntersectionException { - - if (region instanceof ProtectedCuboidRegion) { - ProtectedCuboidRegion r1 = (ProtectedCuboidRegion) this; - ProtectedCuboidRegion r2 = (ProtectedCuboidRegion) region; - BlockVector min1 = r1.getMinimumPoint(); - BlockVector max1 = r1.getMaximumPoint(); - BlockVector min2 = r2.getMinimumPoint(); - BlockVector max2 = r2.getMaximumPoint(); - - return !(min1.getBlockX() > max2.getBlockX() - || min1.getBlockY() > max2.getBlockY() - || min1.getBlockZ() > max2.getBlockZ() - || max1.getBlockX() < min2.getBlockX() - || max1.getBlockY() < min2.getBlockY() - || max1.getBlockZ() < min2.getBlockZ()); - } else if (region instanceof ProtectedPolygonalRegion) { - throw new UnsupportedIntersectionException(); - } else { - throw new UnsupportedIntersectionException(); - } + @Override + public RegionType getType() { + return RegionType.CUBOID; } - */ @Override - public List getIntersectingRegions(List regions) throws UnsupportedIntersectionException { + public List getIntersectingRegions(Collection regions) { + checkNotNull(regions); + List intersectingRegions = new ArrayList(); for (ProtectedRegion region : regions) { @@ -139,28 +125,21 @@ public List getIntersectingRegions(List region // If both regions are Cuboids and their bounding boxes intersect, they intersect if (region instanceof ProtectedCuboidRegion) { intersectingRegions.add(region); - continue; } else if (region instanceof ProtectedPolygonalRegion) { // If either region contains the points of the other, // or if any edges intersect, the regions intersect - if (containsAny(region.getPoints()) - || region.containsAny(getPoints()) - || intersectsEdges(region)) { + if (containsAny(region.getPoints()) || region.containsAny(getPoints()) || intersectsEdges(region)) { intersectingRegions.add(region); - continue; } + } else if (region instanceof GlobalProtectedRegion) { + // Never intersects } else { - throw new UnsupportedOperationException("Not supported yet."); + throw new IllegalArgumentException("Not supported yet."); } } return intersectingRegions; } - @Override - public String getTypeName() { - return "cuboid"; - } - @Override public int volume() { int xLength = max.getBlockX() - min.getBlockX() + 1; @@ -169,4 +148,5 @@ public int volume() { return xLength * yLength * zLength; } + } \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedPolygonalRegion.java b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedPolygonalRegion.java index 7b6d474b..0f9c8cbc 100644 --- a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedPolygonalRegion.java +++ b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedPolygonalRegion.java @@ -19,23 +19,25 @@ package com.sk89q.worldguard.protection.regions; -import java.util.ArrayList; -import java.util.List; - import com.sk89q.worldedit.BlockVector2D; import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.protection.UnsupportedIntersectionException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; public class ProtectedPolygonalRegion extends ProtectedRegion { - protected List points; - protected int minY; - protected int maxY; + private List points; + private int minY; + private int maxY; public ProtectedPolygonalRegion(String id, List points, int minY, int maxY) { super(id); - this.points = points; setMinMaxPoints(points, minY, maxY); + this.points = points; this.minY = min.getBlockY(); this.maxY = max.getBlockY(); } @@ -48,6 +50,8 @@ public ProtectedPolygonalRegion(String id, List points, int minY, * @param maxY The maximum y coordinate */ private void setMinMaxPoints(List points2D, int minY, int maxY) { + checkNotNull(points2D); + List points = new ArrayList(); int y = minY; for (BlockVector2D point2D : points2D) { @@ -57,18 +61,18 @@ private void setMinMaxPoints(List points2D, int minY, int maxY) { setMinMaxPoints(points); } + @Override public List getPoints() { return points; } - /** - * Checks to see if a point is inside this region. - */ @Override - public boolean contains(Vector pt) { - int targetX = pt.getBlockX(); //wide - int targetY = pt.getBlockY(); //height - int targetZ = pt.getBlockZ(); //depth + public boolean contains(Vector position) { + checkNotNull(position); + + int targetX = position.getBlockX(); // Width + int targetY = position.getBlockY(); // Height + int targetZ = position.getBlockZ(); // Depth if (targetY < minY || targetY > maxY) { return false; @@ -124,7 +128,14 @@ public boolean contains(Vector pt) { } @Override - public List getIntersectingRegions(List regions) throws UnsupportedIntersectionException { + public RegionType getType() { + return RegionType.POLYGON; + } + + @Override + public List getIntersectingRegions(Collection regions) { + checkNotNull(regions); + List intersectingRegions = new ArrayList(); for (ProtectedRegion region : regions) { @@ -133,67 +144,22 @@ public List getIntersectingRegions(List region if (region instanceof ProtectedPolygonalRegion || region instanceof ProtectedCuboidRegion) { // If either region contains the points of the other, // or if any edges intersect, the regions intersect - if (containsAny(region.getPoints()) - || region.containsAny(getPoints()) - || intersectsEdges(region)) { + if (containsAny(region.getPoints()) || region.containsAny(getPoints()) || intersectsEdges(region)) { intersectingRegions.add(region); - continue; } + } else if (region instanceof GlobalProtectedRegion) { + // Never intersects } else { - throw new UnsupportedOperationException("Not supported yet."); + throw new IllegalArgumentException("Not supported yet."); } } return intersectingRegions; } - - /** - * Return the type of region as a user-friendly name. - * - * @return type of region - */ - @Override - public String getTypeName() { - return "polygon"; - } - @Override public int volume() { - int volume = 0; - // TODO: Fix this - /*int numPoints = points.size(); - if (numPoints < 3) { - return 0; - } - - double area = 0; - int xa, z1, z2; - - for (int i = 0; i < numPoints; i++) { - xa = points.get(i).getBlockX(); - //za = points.get(i).getBlockZ(); - - if (points.get(i + 1) == null) { - z1 = points.get(0).getBlockZ(); - } else { - z1 = points.get(i + 1).getBlockZ(); - } - if (points.get(i - 1) == null) { - z2 = points.get(numPoints - 1).getBlockZ(); - } else { - z2 = points.get(i - 1).getBlockZ(); - } - - area = area + (xa * (z1 - z2)); - } - - xa = points.get(0).getBlockX(); - //za = points.get(0).getBlockZ(); - - area = area + (xa * (points.get(1).getBlockZ() - points.get(numPoints - 1).getBlockZ())); - - volume = (Math.abs(maxY - minY) + 1) * (int) Math.ceil((Math.abs(area) / 2));*/ - - return volume; + // TODO: Fix this -- the previous algorithm returned incorrect results, but the current state of this method is even worse + return 0; } + } diff --git a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java index e3355391..01744050 100644 --- a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java +++ b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java @@ -24,45 +24,62 @@ import com.sk89q.worldedit.Vector; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.UnsupportedIntersectionException; import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.util.ChangeTracked; +import com.sk89q.worldguard.util.Normal; +import javax.annotation.Nullable; import java.awt.geom.Line2D; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; +import static com.google.common.base.Preconditions.checkNotNull; + /** - * Represents a region of any shape and size that can be protected. + * Represents a region that can be indexed and have spatial queries performed + * against it. + * + *

Instances can be modified and access from several threads at a time.

*/ -public abstract class ProtectedRegion implements Comparable { +public abstract class ProtectedRegion implements ChangeTracked, Comparable { + + private static final Pattern VALID_ID_PATTERN = Pattern.compile("^[A-Za-z0-9_,'\\-\\+/]{1,}$"); protected BlockVector min; protected BlockVector max; - private static final Pattern idPattern = Pattern.compile("^[A-Za-z0-9_,'\\-\\+/]{1,}$"); - - private String id; + private final String id; private int priority = 0; private ProtectedRegion parent; private DefaultDomain owners = new DefaultDomain(); private DefaultDomain members = new DefaultDomain(); - private Map, Object> flags = new ConcurrentHashMap, Object>(); + private ConcurrentMap, Object> flags = new ConcurrentHashMap, Object>(); + private boolean dirty = true; /** * Construct a new instance of this region. * - * @param id The id (name) of this region. + * @param id the name of this region + * @throws IllegalArgumentException thrown if the ID is invalid (see {@link #isValidId(String)} */ - public ProtectedRegion(String id) { - this.id = id; + ProtectedRegion(String id) { // Package private because we can't have people creating their own region types + checkNotNull(id); + + if (!isValidId(id)) { + throw new IllegalArgumentException("Invalid region ID: " + id); + } + + this.id = Normal.normalize(id); } /** - * Sets the minimum and maximum points of the bounding box for a region + * Set the minimum and maximum points of the bounding box for a region * - * @param points The points to set. Must have at least one element. + * @param points the points to set with at least one entry */ protected void setMinMaxPoints(List points) { int minX = points.get(0).getBlockX(); @@ -85,39 +102,45 @@ protected void setMinMaxPoints(List points) { if (y > maxY) maxY = y; if (z > maxZ) maxZ = z; } - + + setDirty(true); min = new BlockVector(minX, minY, minZ); max = new BlockVector(maxX, maxY, maxZ); } /** - * Gets the id of this region + * Gets the name of this region * - * @return the id + * @return the name */ public String getId() { return id; } /** - * Get the lower point of the cuboid. + * Get a vector containing the smallest X, Y, and Z components for the + * corner of the axis-aligned bounding box that contains this region. * - * @return min point + * @return the minimum point */ public BlockVector getMinimumPoint() { return min; } /** - * Get the upper point of the cuboid. + * Get a vector containing the highest X, Y, and Z components for the + * corner of the axis-aligned bounding box that contains this region. * - * @return max point + * @return the maximum point */ public BlockVector getMaximumPoint() { return max; } /** + * Get the priority of the region, where higher numbers indicate a higher + * priority. + * * @return the priority */ public int getPriority() { @@ -125,27 +148,36 @@ public int getPriority() { } /** - * @param priority the priority to setFlag + * Set the priority of the region, where higher numbers indicate a higher + * priority. + * + * @param priority the priority to set */ public void setPriority(int priority) { + setDirty(true); this.priority = priority; } /** - * @return the curParent + * Get the parent of the region, if one exists. + * + * @return the parent, or {@code null} */ + @Nullable public ProtectedRegion getParent() { return parent; } /** - * Set the curParent. This checks to make sure that it will not result - * in circular inheritance. + * Set the parent of this region. This checks to make sure that it will + * not result in circular inheritance. * - * @param parent the curParent to setFlag + * @param parent the new parent * @throws CircularInheritanceException when circular inheritance is detected */ - public void setParent(ProtectedRegion parent) throws CircularInheritanceException { + public void setParent(@Nullable ProtectedRegion parent) throws CircularInheritanceException { + setDirty(true); + if (parent == null) { this.parent = null; return; @@ -166,23 +198,38 @@ public void setParent(ProtectedRegion parent) throws CircularInheritanceExceptio this.parent = parent; } + /** + * Clear the parent (set the parent to {@code null}). + */ + public void clearParent() { + setDirty(true); + this.parent = null; + } /** - * @return the owners + * Get the domain that contains the owners of this region. + * + * @return the domain */ public DefaultDomain getOwners() { - return owners; } /** - * @param owners the owners to setFlag + * Set the owner domain. + * + * @param owners the new domain */ public void setOwners(DefaultDomain owners) { + checkNotNull(owners); + setDirty(true); this.owners = owners; } /** + * Get the domain that contains the members of this region, which does + * not automatically include the owners. + * * @return the members */ public DefaultDomain getMembers() { @@ -190,9 +237,13 @@ public DefaultDomain getMembers() { } /** - * @param members the members to setFlag + * Set the members domain. + * + * @param members the new domain */ public void setMembers(DefaultDomain members) { + checkNotNull(members); + setDirty(true); this.members = members; } @@ -212,6 +263,8 @@ public boolean hasMembersOrOwners() { * @return whether an owner */ public boolean isOwner(LocalPlayer player) { + checkNotNull(player); + if (owners.contains(player)) { return true; } @@ -233,8 +286,12 @@ public boolean isOwner(LocalPlayer player) { * * @param playerName player name to check * @return whether an owner + * @deprecated Names are deprecated */ + @Deprecated public boolean isOwner(String playerName) { + checkNotNull(playerName); + if (owners.contains(playerName)) { return true; } @@ -259,6 +316,8 @@ public boolean isOwner(String playerName) { * @return whether an owner or member */ public boolean isMember(LocalPlayer player) { + checkNotNull(player); + if (isOwner(player)) { return true; } @@ -285,8 +344,12 @@ public boolean isMember(LocalPlayer player) { * * @param playerName player name to check * @return whether an owner or member + * @deprecated Names are deprecated */ + @Deprecated public boolean isMember(String playerName) { + checkNotNull(playerName); + if (isOwner(playerName)) { return true; } @@ -308,13 +371,14 @@ public boolean isMember(String playerName) { } /** - * Checks whether a player is a member of the region - * or any of its parents. + * Checks whether a player is a member of the region or any of its parents. * * @param player player to check * @return whether an member */ public boolean isMemberOnly(LocalPlayer player) { + checkNotNull(player); + if (members.contains(player)) { return true; } @@ -334,32 +398,40 @@ public boolean isMemberOnly(LocalPlayer player) { /** * Get a flag's value. * - * @param The flag type - * @param The type of the flag's value - * @param flag The flag to check - * @return value or null if isn't defined + * @param flag the flag to check + * @return the value or null if isn't defined + * @param the flag type + * @param the type of the flag's value */ @SuppressWarnings("unchecked") + @Nullable public , V> V getFlag(T flag) { + checkNotNull(flag); + Object obj = flags.get(flag); V val; + if (obj != null) { val = (V) obj; } else { return null; } + return val; } /** * Set a flag's value. * - * @param The flag type - * @param The type of the flag's value - * @param flag The flag to check - * @param val The value to set + * @param flag the flag to check + * @param val the value to set + * @param the flag type + * @param the type of the flag's value */ - public , V> void setFlag(T flag, V val) { + public , V> void setFlag(T flag, @Nullable V val) { + checkNotNull(flag); + setDirty(true); + if (val == null) { flags.remove(flag); } else { @@ -370,30 +442,35 @@ public , V> void setFlag(T flag, V val) { /** * Get the map of flags. * - * @return The map of flags currently used for this region + * @return the map of flags currently used for this region */ public Map, Object> getFlags() { return flags; } /** - * Get the map of flags. + * Set the map of flags. * - * @param flags The flags to set + *

A copy of the map will be used.

+ * + * @param flags the flags to set */ public void setFlags(Map, Object> flags) { - this.flags = flags; + checkNotNull(flags); + + setDirty(true); + this.flags = new ConcurrentHashMap, Object>(flags); } /** - * Gets the 2D points for this region + * Get points of the region projected onto the X-Z plane. * - * @return The points for this region as (x, z) coordinates + * @return the points */ public abstract List getPoints(); /** - * Get the number of blocks in this region + * Get the number of blocks in this region. * * @return the volume of this region in blocks */ @@ -408,84 +485,79 @@ public void setFlags(Map, Object> flags) { public abstract boolean contains(Vector pt); /** - * Check to see if a point is inside this region. + * Check to see if a position is contained within this region. * - * @param pt The point to check - * @return Whether {@code pt} is in this region + * @param position the position to check + * @return whether {@code position} is in this region */ - public boolean contains(BlockVector2D pt) { - return contains(new Vector(pt.getBlockX(), min.getBlockY(), pt.getBlockZ())); + public boolean contains(BlockVector2D position) { + checkNotNull(position); + return contains(new Vector(position.getBlockX(), min.getBlockY(), position.getBlockZ())); } /** * Check to see if a point is inside this region. * - * @param x The x coordinate to check - * @param y The y coordinate to check - * @param z The z coordinate to check - * @return Whether this region contains the points at the given coordinate + * @param x the x coordinate to check + * @param y the y coordinate to check + * @param z the z coordinate to check + * @return whether this region contains the point */ public boolean contains(int x, int y, int z) { return contains(new Vector(x, y, z)); } /** - * Check to see if any of the 2D points are inside this region. + * Check to see if any of the points are inside this region projected + * onto the X-Z plane. * - * @param pts a list positions + * @param positions a list of positions * @return true if contained */ - public boolean containsAny(List pts) { - for (BlockVector2D pt : pts) { + public boolean containsAny(List positions) { + checkNotNull(positions); + + for (BlockVector2D pt : positions) { if (contains(pt)) { return true; } } + return false; } /** - * Compares to another region.
- *
- * Orders primarily by the priority, descending
- * Orders secondarily by the id, ascending + * Get the type of region. * - * @param other The region to compare to + * @return the type */ - @Override - public int compareTo(ProtectedRegion other) { - if (priority > other.priority) { - return -1; - } else if (priority < other.priority) { - return 1; - } - - return id.compareTo(other.id); - } + public abstract RegionType getType(); /** * Return the type of region as a user-friendly, lowercase name. * * @return type of region + * @deprecated use {@link #getType()} */ - public abstract String getTypeName(); + @Deprecated + public final String getTypeName() { + return getType().getName(); + } /** - * Get a list of intersecting regions. + * Return a list of regions from the given list of regions that intersect + * with this region. * - * @param regions The list of regions to source from - * @return The elements of {@code regions} that intersect with this region - * @throws UnsupportedIntersectionException if an invalid intersection is detected + * @param regions a list of regions to source from + * @return the elements of {@code regions} that intersect with this region */ - public abstract List getIntersectingRegions( - List regions) - throws UnsupportedIntersectionException; + public abstract List getIntersectingRegions(Collection regions); /** * Checks if the bounding box of a region intersects with with the bounding - * box of this region + * box of this region. * - * @param region The region to check + * @param region the region to check * @return whether the given region intersects */ protected boolean intersectsBoundingBox(ProtectedRegion region) { @@ -507,9 +579,9 @@ protected boolean intersectsBoundingBox(ProtectedRegion region) { } /** - * Compares all edges of two regions to see if any of them intersect + * Compares all edges of two regions to see if any of them intersect. * - * @param region The region to check + * @param region the region to check * @return whether any edges of a region intersect */ protected boolean intersectsEdges(ProtectedRegion region) { @@ -540,15 +612,27 @@ protected boolean intersectsEdges(ProtectedRegion region) { return false; } - /** - * Checks to see if the given ID is accurate. - * - * @param id The id to check - * @see #idPattern - * @return Whether the region id given is valid - */ - public static boolean isValidId(String id) { - return idPattern.matcher(id).matches(); + @Override + public boolean isDirty() { + return dirty || owners.isDirty() || members.isDirty(); + } + + @Override + public void setDirty(boolean dirty) { + this.dirty = dirty; + owners.setDirty(dirty); + members.setDirty(dirty); + } + + @Override + public int compareTo(ProtectedRegion other) { + if (getPriority() > other.getPriority()) { + return -1; + } else if (getPriority() < other.getPriority()) { + return 1; + } + + return getId().compareTo(other.getId()); } @Override @@ -556,9 +640,6 @@ public int hashCode(){ return id.hashCode(); } - /** - * Returns whether this region has the same ID as another region. - */ @Override public boolean equals(Object obj) { if (!(obj instanceof ProtectedRegion)) { @@ -569,8 +650,27 @@ public boolean equals(Object obj) { return other.getId().equals(getId()); } + @Override + public String toString() { + return "ProtectedRegion{" + + "id='" + id + "', " + + "type='" + getType() + '\'' + + '}'; + } + /** - * Thrown when setting a curParent would create a circular inheritance + * Checks to see if the given ID is a valid ID. + * + * @param id the id to check + * @return whether the region id given is valid + */ + public static boolean isValidId(String id) { + checkNotNull(id); + return VALID_ID_PATTERN.matcher(id).matches(); + } + + /** + * Thrown when setting a parent would create a circular inheritance * situation. */ public static class CircularInheritanceException extends Exception { diff --git a/src/main/java/com/sk89q/worldguard/protection/regions/RegionType.java b/src/main/java/com/sk89q/worldguard/protection/regions/RegionType.java new file mode 100644 index 00000000..5e57e7c3 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/regions/RegionType.java @@ -0,0 +1,52 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.regions; + +/** + * An enum of supported region types. + */ +public enum RegionType { + + // Do not change the names + CUBOID("cuboid"), + POLYGON("poly2d"), + GLOBAL("global"); + + private final String name; + + /** + * Create a new instance. + * + * @param name the region name + */ + RegionType(String name) { + this.name = name; + } + + /** + * Get the name of the region. + * + * @return the name of the region + */ + public String getName() { + return name; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java b/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java similarity index 98% rename from src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java rename to src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java index c4ff7a75..57dc66b4 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/util/DomainInputResolver.java +++ b/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.databases.util; +package com.sk89q.worldguard.protection.util; import com.google.common.base.Function; import com.google.common.base.Joiner; diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/util/UnresolvedNamesException.java b/src/main/java/com/sk89q/worldguard/protection/util/UnresolvedNamesException.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/protection/databases/util/UnresolvedNamesException.java rename to src/main/java/com/sk89q/worldguard/protection/util/UnresolvedNamesException.java index 25f586c7..c2a28777 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/util/UnresolvedNamesException.java +++ b/src/main/java/com/sk89q/worldguard/protection/util/UnresolvedNamesException.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.databases.util; +package com.sk89q.worldguard.protection.util; /** * Thrown when there are unresolved names. diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigrationException.java b/src/main/java/com/sk89q/worldguard/protection/util/migrator/MigrationException.java similarity index 91% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigrationException.java rename to src/main/java/com/sk89q/worldguard/protection/util/migrator/MigrationException.java index 84aef164..9ea57a4a 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/MigrationException.java +++ b/src/main/java/com/sk89q/worldguard/protection/util/migrator/MigrationException.java @@ -17,10 +17,12 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.databases.migrator; +package com.sk89q.worldguard.protection.util.migrator; +/** + * Thrown when a migration fails. + */ public class MigrationException extends Exception { - private static final long serialVersionUID = 1L; public MigrationException() { super(); @@ -37,4 +39,5 @@ public MigrationException(String message, Throwable cause) { public MigrationException(Throwable cause) { super(cause); } + } diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/UUIDMigrator.java b/src/main/java/com/sk89q/worldguard/protection/util/migrator/UUIDMigrator.java similarity index 99% rename from src/main/java/com/sk89q/worldguard/protection/databases/migrator/UUIDMigrator.java rename to src/main/java/com/sk89q/worldguard/protection/util/migrator/UUIDMigrator.java index 1788c4cd..1071916a 100644 --- a/src/main/java/com/sk89q/worldguard/protection/databases/migrator/UUIDMigrator.java +++ b/src/main/java/com/sk89q/worldguard/protection/util/migrator/UUIDMigrator.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.databases.migrator; +package com.sk89q.worldguard.protection.util.migrator; import com.google.common.base.Predicate; import com.sk89q.squirrelid.Profile; diff --git a/src/main/java/com/sk89q/worldguard/util/ChangeTracked.java b/src/main/java/com/sk89q/worldguard/util/ChangeTracked.java new file mode 100644 index 00000000..c7baaf77 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/ChangeTracked.java @@ -0,0 +1,42 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util; + +/** + * An object that keeps track of a dirty flag that is set to true when changes + * are made to this object. + */ +public interface ChangeTracked { + + /** + * Tests whether changes have been made. + * + * @return true if changes have been made + */ + boolean isDirty(); + + /** + * Set whether changes have been made. + * + * @param dirty a new dirty state + */ + void setDirty(boolean dirty); + +} diff --git a/src/main/java/com/sk89q/worldguard/util/Normal.java b/src/main/java/com/sk89q/worldguard/util/Normal.java new file mode 100644 index 00000000..50d60d0f --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/Normal.java @@ -0,0 +1,119 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util; + +import javax.annotation.Nullable; +import java.text.Normalizer; +import java.text.Normalizer.Form; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Normal names are strings that are considered equal after they have been + * normalized using Unicode's NFC form and made lowercase. + */ +public final class Normal { + + private final String name; + @Nullable + private final String normal; + + /** + * Create a new instance. + * + * @param name a new instance + */ + private Normal(String name) { + checkNotNull(name); + + this.name = name; + String normal = normalize(name); + if (!normal.equals(name)) { // Simple comparison + this.normal = normal; + } else { + this.normal = null; + } + } + + /** + * Get the original name before normalization. + * + * @return the original name before normalization + */ + public String getName() { + return name; + } + + /** + * Get the normalized name. + * + * @return the normal name + */ + public String getNormal() { + return normal != null ? normal : name; + } + + /** + * Normalize a string according to the rules of this class. + * + * @param name an string + * @return the normalized string + */ + public static String normalize(String name) { + return Normalizer.normalize(name.toLowerCase(), Form.NFC); + } + + /** + * Create a new instance. + * + * @param name the name + * @return an instance + */ + public static Normal normal(String name) { + return new Normal(name); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Normal that = (Normal) o; + + return getNormal().equals(that.getNormal()); + + } + + @Override + public int hashCode() { + return getNormal().hashCode(); + } + + /** + * Return the un-normalized name. + * + * @return the un-normalized name + */ + @Override + public String toString() { + return name; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/util/sql/DataSourceConfig.java b/src/main/java/com/sk89q/worldguard/util/sql/DataSourceConfig.java new file mode 100644 index 00000000..144a7ee5 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/sql/DataSourceConfig.java @@ -0,0 +1,145 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.sql; + +import com.jolbox.bonecp.BoneCPConfig; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Describes a data source. + */ +public class DataSourceConfig { + + private final String dsn; + private final String username; + private final String password; + private final String tablePrefix; + + /** + * Create a new instance. + * + * @param dsn the DSN + * @param username the username + * @param password the password + * @param tablePrefix the table prefix + */ + public DataSourceConfig(String dsn, String username, String password, String tablePrefix) { + checkNotNull(dsn); + checkNotNull(username); + checkNotNull(password); + checkNotNull(tablePrefix); + + this.dsn = dsn; + this.username = username; + this.password = password; + this.tablePrefix = tablePrefix; + } + + /** + * Get the DSN. + * + * @return the DSN + */ + public String getDsn() { + return dsn; + } + + /** + * Get the username. + * + * @return the username + */ + public String getUsername() { + return username; + } + + /** + * Get the password. + * + * @return the password + */ + public String getPassword() { + return password; + } + + /** + * Get the table prefix. + * + * @return the table prefix + */ + public String getTablePrefix() { + return tablePrefix; + } + + /** + * Create a new instance with a new DSN. + * + * @param dsn a new DSN string + * @return a new instance + */ + public DataSourceConfig setDsn(String dsn) { + return new DataSourceConfig(dsn, username, password, tablePrefix); + } + + /** + * Create a new instance with a new username. + * + * @param username a new username + * @return a new instance + */ + public DataSourceConfig setUsername(String username) { + return new DataSourceConfig(dsn, username, password, tablePrefix); + } + + /** + * Create a new instance with a new password. + * + * @param password a new password + * @return a new instance + */ + public DataSourceConfig setPassword(String password) { + return new DataSourceConfig(dsn, username, password, tablePrefix); + } + + /** + * Create a new instance with a new table prefix. + * + * @param tablePrefix the new table prefix + * @return a new instance + */ + public DataSourceConfig setTablePrefix(String tablePrefix) { + return new DataSourceConfig(dsn, username, password, tablePrefix); + } + + /** + * Create a new BoneCP configuration object. + * + * @return a new configuration object + */ + public BoneCPConfig createBoneCPConfig() { + BoneCPConfig config = new BoneCPConfig(); + config.setJdbcUrl(dsn); + config.setUsername(username); + config.setPassword(password); + return config; + } + +} diff --git a/src/main/resources/migrations/region/mysql/V2__Bug_fix_and_UUID.sql b/src/main/resources/migrations/region/mysql/V2__Bug_fix_and_UUID.sql index 46789055..b1df899f 100644 --- a/src/main/resources/migrations/region/mysql/V2__Bug_fix_and_UUID.sql +++ b/src/main/resources/migrations/region/mysql/V2__Bug_fix_and_UUID.sql @@ -5,6 +5,10 @@ ALTER TABLE `${tablePrefix}region_players` DROP PRIMARY KEY, ADD PRIMARY KEY (`region_id`, `world_id`, `user_id`, `owner`); +ALTER TABLE `${tablePrefix}region_groups` + DROP PRIMARY KEY, + ADD PRIMARY KEY (`region_id`, `world_id`, `group_id`, `owner`); + -- Fix WORLDGUARD-3030 -- Adds UUID support @@ -14,4 +18,10 @@ ALTER TABLE `${tablePrefix}user` ALTER TABLE `${tablePrefix}user` CHANGE COLUMN `name` `name` VARCHAR(64) NULL COLLATE 'utf8_bin' AFTER `id`, ADD COLUMN `uuid` CHAR(36) NULL AFTER `name`, - ADD UNIQUE INDEX `uuid` (`uuid`); \ No newline at end of file + ADD UNIQUE INDEX `uuid` (`uuid`); + +-- Strings with differing numbers of trailing spaces are equal in MySQL +-- The domains have been updated to trim strings + +UPDATE `${tablePrefix}user` SET `name` = TRIM(`name`); +UPDATE `${tablePrefix}group` SET `name` = TRIM(`name`); \ No newline at end of file diff --git a/src/main/resources/migrations/region/sqlite/V1__Initial.sql b/src/main/resources/migrations/region/sqlite/V1__Initial.sql new file mode 100644 index 00000000..2b5b99e3 --- /dev/null +++ b/src/main/resources/migrations/region/sqlite/V1__Initial.sql @@ -0,0 +1,160 @@ + +CREATE TABLE "${tablePrefix}world" ( + id INTEGER PRIMARY KEY AUTOINCREMENT + NOT NULL, + name TEXT NOT NULL + UNIQUE +); + + +CREATE TABLE "${tablePrefix}region" ( + id TEXT NOT NULL, + world_id INTEGER NOT NULL + REFERENCES "${tablePrefix}world" ( id ) ON DELETE CASCADE + ON UPDATE CASCADE, + type TEXT NOT NULL, + priority INTEGER NOT NULL, + parent TEXT DEFAULT ( NULL ) + --REFERENCES "${tablePrefix}region" ( id ) ON DELETE SET NULL + -- ON UPDATE CASCADE -- Not supported + , + PRIMARY KEY ( id, world_id ) +); + + +CREATE TABLE "${tablePrefix}user" ( + id INTEGER PRIMARY KEY AUTOINCREMENT + NOT NULL, + name TEXT UNIQUE + DEFAULT ( NULL ), + uuid TEXT UNIQUE + DEFAULT ( NULL ) +); + + +CREATE TABLE "${tablePrefix}group" ( + id INTEGER PRIMARY KEY AUTOINCREMENT + NOT NULL, + name TEXT NOT NULL + UNIQUE +); + + +CREATE TABLE "${tablePrefix}region_cuboid" ( + region_id TEXT NOT NULL, + world_id INTEGER NOT NULL, + min_x INTEGER NOT NULL, + min_y INTEGER NOT NULL, + min_z INTEGER NOT NULL, + max_x INTEGER NOT NULL, + max_y INTEGER NOT NULL, + max_z INTEGER NOT NULL, + PRIMARY KEY ( region_id, world_id ), + FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region" ( id, world_id ) ON DELETE CASCADE + ON UPDATE CASCADE +); + + +CREATE TABLE "${tablePrefix}region_poly2d" ( + region_id TEXT NOT NULL, + world_id INTEGER NOT NULL, + min_y INTEGER NOT NULL, + max_y INTEGER NOT NULL, + PRIMARY KEY ( region_id, world_id ), + FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region" ( id, world_id ) ON DELETE CASCADE + ON UPDATE CASCADE +); + + +CREATE TABLE "${tablePrefix}region_poly2d_point" ( + id INTEGER PRIMARY KEY AUTOINCREMENT + NOT NULL, + region_id TEXT NOT NULL, + world_id INTEGER NOT NULL, + x INTEGER NOT NULL, + z INTEGER NOT NULL, + FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region_poly2d" ( region_id, world_id ) ON DELETE CASCADE + ON UPDATE CASCADE +); + + +CREATE TABLE "${tablePrefix}region_groups" ( + region_id TEXT NOT NULL, + world_id INTEGER NOT NULL, + group_id INTEGER NOT NULL + REFERENCES "${tablePrefix}group" ( id ) ON DELETE CASCADE + ON UPDATE CASCADE, + owner BOOLEAN NOT NULL, + PRIMARY KEY ( region_id, world_id, group_id ), + FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region" ( id, world_id ) ON DELETE CASCADE + ON UPDATE CASCADE +); + + +CREATE TABLE "${tablePrefix}region_flag" ( + id INTEGER PRIMARY KEY AUTOINCREMENT + NOT NULL, + region_id TEXT NOT NULL, + world_id INTEGER NOT NULL, + flag TEXT NOT NULL, + value TEXT NOT NULL, + FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region" ( id, world_id ) ON DELETE CASCADE + ON UPDATE CASCADE +); + + +CREATE TABLE "${tablePrefix}region_players" ( + region_id TEXT NOT NULL, + world_id INTEGER NOT NULL, + user_id INTEGER NOT NULL + REFERENCES "${tablePrefix}user" ( id ) ON DELETE CASCADE + ON UPDATE CASCADE, + owner BOOLEAN NOT NULL, + PRIMARY KEY ( region_id, world_id, user_id, owner ), + FOREIGN KEY ( region_id, world_id ) REFERENCES "${tablePrefix}region" ( id, world_id ) ON DELETE CASCADE + ON UPDATE CASCADE +); + + +CREATE INDEX "idx_${tablePrefix}region_cuboid_region_id" ON "${tablePrefix}region_cuboid" ( + region_id +); + + +CREATE INDEX "idx_${tablePrefix}region_world_id" ON "${tablePrefix}region" ( + world_id +); + + +CREATE INDEX "idx_${tablePrefix}region_parent" ON "${tablePrefix}region" ( + parent +); + + +CREATE INDEX "idx_${tablePrefix}region_poly2d_region_id" ON "${tablePrefix}region_poly2d" ( + region_id +); + + +CREATE INDEX "idx_${tablePrefix}region_poly2d_point_region_world_id" ON "${tablePrefix}region_poly2d_point" ( + region_id, + world_id +); + + +CREATE INDEX "idx_${tablePrefix}region_groups_region_id" ON "${tablePrefix}region_groups" ( + region_id +); + + +CREATE INDEX "idx_${tablePrefix}region_groups_group_id" ON "${tablePrefix}region_groups" ( + group_id +); + + +CREATE INDEX "idx_${tablePrefix}region_flag_region_world_id" ON "${tablePrefix}region_flag" ( + region_id, + world_id, + flag +); + diff --git a/src/test/java/com/sk89q/worldguard/protection/FlatRegionOverlapTest.java b/src/test/java/com/sk89q/worldguard/protection/FlatRegionOverlapTest.java deleted file mode 100644 index cd26c221..00000000 --- a/src/test/java/com/sk89q/worldguard/protection/FlatRegionOverlapTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection; - -import com.sk89q.worldguard.protection.managers.FlatRegionManager; -import com.sk89q.worldguard.protection.managers.RegionManager; - -public class FlatRegionOverlapTest extends RegionOverlapTest { - protected RegionManager createRegionManager() throws Exception { - return new FlatRegionManager(null); - } -} diff --git a/src/test/java/com/sk89q/worldguard/protection/PRTreeRegionPriorityTest.java b/src/test/java/com/sk89q/worldguard/protection/HashMapIndexPriorityTest.java similarity index 76% rename from src/test/java/com/sk89q/worldguard/protection/PRTreeRegionPriorityTest.java rename to src/test/java/com/sk89q/worldguard/protection/HashMapIndexPriorityTest.java index e5744c7d..3ae63cc9 100644 --- a/src/test/java/com/sk89q/worldguard/protection/PRTreeRegionPriorityTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/HashMapIndexPriorityTest.java @@ -19,11 +19,15 @@ package com.sk89q.worldguard.protection; -import com.sk89q.worldguard.protection.managers.PRTreeRegionManager; +import com.sk89q.worldguard.protection.managers.index.HashMapIndex; import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore; -public class PRTreeRegionPriorityTest extends RegionPriorityTest { +public class HashMapIndexPriorityTest extends RegionPriorityTest { + + @Override protected RegionManager createRegionManager() throws Exception { - return new PRTreeRegionManager(null); + return new RegionManager(new MemoryRegionStore(), new HashMapIndex.Factory()); } + } diff --git a/src/test/java/com/sk89q/worldguard/protection/PRTreeRegionManagerTest.java b/src/test/java/com/sk89q/worldguard/protection/HashMapIndexRegionOverlapTest.java similarity index 76% rename from src/test/java/com/sk89q/worldguard/protection/PRTreeRegionManagerTest.java rename to src/test/java/com/sk89q/worldguard/protection/HashMapIndexRegionOverlapTest.java index e83ebb6f..7a00755c 100644 --- a/src/test/java/com/sk89q/worldguard/protection/PRTreeRegionManagerTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/HashMapIndexRegionOverlapTest.java @@ -19,11 +19,15 @@ package com.sk89q.worldguard.protection; -import com.sk89q.worldguard.protection.managers.PRTreeRegionManager; +import com.sk89q.worldguard.protection.managers.index.HashMapIndex; import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore; -public class PRTreeRegionManagerTest extends RegionOverlapTest { +public class HashMapIndexRegionOverlapTest extends RegionOverlapTest { + + @Override protected RegionManager createRegionManager() throws Exception { - return new PRTreeRegionManager(null); + return new RegionManager(new MemoryRegionStore(), new HashMapIndex.Factory()); } + } diff --git a/src/test/java/com/sk89q/worldguard/protection/PRTreeRegionEntryExitTest.java b/src/test/java/com/sk89q/worldguard/protection/HashMapIndexTest.java similarity index 77% rename from src/test/java/com/sk89q/worldguard/protection/PRTreeRegionEntryExitTest.java rename to src/test/java/com/sk89q/worldguard/protection/HashMapIndexTest.java index e7e4061c..6ff2a728 100644 --- a/src/test/java/com/sk89q/worldguard/protection/PRTreeRegionEntryExitTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/HashMapIndexTest.java @@ -19,12 +19,15 @@ package com.sk89q.worldguard.protection; -import com.sk89q.worldguard.protection.managers.PRTreeRegionManager; +import com.sk89q.worldguard.protection.managers.index.HashMapIndex; import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore; + +public class HashMapIndexTest extends RegionOverlapTest { -public class PRTreeRegionEntryExitTest extends RegionEntryExitTest { @Override protected RegionManager createRegionManager() throws Exception { - return new PRTreeRegionManager(null); + return new RegionManager(new MemoryRegionStore(), new HashMapIndex.Factory()); } + } diff --git a/src/test/java/com/sk89q/worldguard/protection/MockApplicableRegionSet.java b/src/test/java/com/sk89q/worldguard/protection/MockApplicableRegionSet.java index 543794a2..24939297 100644 --- a/src/test/java/com/sk89q/worldguard/protection/MockApplicableRegionSet.java +++ b/src/test/java/com/sk89q/worldguard/protection/MockApplicableRegionSet.java @@ -79,7 +79,7 @@ public ApplicableRegionSet getApplicableSet() { private String getNextId() { id++; - return "#REGION_" + id; + return "REGION_" + id; } } diff --git a/src/test/java/com/sk89q/worldguard/protection/FlatRegionPriorityTest.java b/src/test/java/com/sk89q/worldguard/protection/PriorityRTreeIndexTest.java similarity index 75% rename from src/test/java/com/sk89q/worldguard/protection/FlatRegionPriorityTest.java rename to src/test/java/com/sk89q/worldguard/protection/PriorityRTreeIndexTest.java index 9536b43b..c67fb0c4 100644 --- a/src/test/java/com/sk89q/worldguard/protection/FlatRegionPriorityTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/PriorityRTreeIndexTest.java @@ -19,11 +19,15 @@ package com.sk89q.worldguard.protection; -import com.sk89q.worldguard.protection.managers.FlatRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex; +import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore; -public class FlatRegionPriorityTest extends RegionPriorityTest { +public class PriorityRTreeIndexTest extends RegionOverlapTest { + + @Override protected RegionManager createRegionManager() throws Exception { - return new FlatRegionManager(null); + return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory()); } + } diff --git a/src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionEntryExitTest.java b/src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionEntryExitTest.java new file mode 100644 index 00000000..facb384c --- /dev/null +++ b/src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionEntryExitTest.java @@ -0,0 +1,33 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection; + +import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore; + +public class PriorityRTreeRegionEntryExitTest extends RegionEntryExitTest { + + @Override + protected RegionManager createRegionManager() throws Exception { + return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory()); + } + +} diff --git a/src/test/java/com/sk89q/worldguard/protection/PRTreeRegionOverlapTest.java b/src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionOverlapTest.java similarity index 75% rename from src/test/java/com/sk89q/worldguard/protection/PRTreeRegionOverlapTest.java rename to src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionOverlapTest.java index 923ee3bd..1ebac794 100644 --- a/src/test/java/com/sk89q/worldguard/protection/PRTreeRegionOverlapTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionOverlapTest.java @@ -19,11 +19,15 @@ package com.sk89q.worldguard.protection; -import com.sk89q.worldguard.protection.managers.PRTreeRegionManager; +import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex; import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore; -public class PRTreeRegionOverlapTest extends RegionOverlapTest { +public class PriorityRTreeRegionOverlapTest extends RegionOverlapTest { + + @Override protected RegionManager createRegionManager() throws Exception { - return new PRTreeRegionManager(null); + return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory()); } + } diff --git a/src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionPriorityTest.java b/src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionPriorityTest.java new file mode 100644 index 00000000..0fddcaf5 --- /dev/null +++ b/src/test/java/com/sk89q/worldguard/protection/PriorityRTreeRegionPriorityTest.java @@ -0,0 +1,33 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection; + +import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore; + +public class PriorityRTreeRegionPriorityTest extends RegionPriorityTest { + + @Override + protected RegionManager createRegionManager() throws Exception { + return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory()); + } + +} diff --git a/src/test/java/com/sk89q/worldguard/protection/RegionEntryExitTest.java b/src/test/java/com/sk89q/worldguard/protection/RegionEntryExitTest.java index ba17582f..d7bad72a 100644 --- a/src/test/java/com/sk89q/worldguard/protection/RegionEntryExitTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/RegionEntryExitTest.java @@ -37,6 +37,7 @@ import static org.junit.Assert.assertTrue; public abstract class RegionEntryExitTest { + static String ENTRY_ID = "entry_rg"; static String EXIT_ID = "exit_rg"; static String BUILDER_GROUP = "builder"; From 4a501a49db5a780fbf2b8e57a4cb9d54e8e2d0be Mon Sep 17 00:00:00 2001 From: sk89q Date: Mon, 11 Aug 2014 17:40:04 -0700 Subject: [PATCH 021/133] Abstract events for regions and blacklists further. --- .../sk89q/worldguard/bukkit/BukkitUtil.java | 19 +- .../bukkit/WorldGuardBlockListener.java | 130 ---- .../bukkit/WorldGuardEntityListener.java | 40 +- .../bukkit/WorldGuardHangingListener.java | 101 +-- .../bukkit/WorldGuardPlayerListener.java | 706 ------------------ .../bukkit/WorldGuardVehicleListener.java | 38 - .../bukkit/listener/CauseListener.java | 490 ++++++++++++ .../util/Blocks.java} | 35 +- .../worldguard/bukkit/util/DamageCauses.java | 54 ++ .../worldguard/bukkit/util/Entities.java | 114 +++ .../worldguard/bukkit/util/Materials.java | 563 ++++++++++++++ .../worldguard/bukkit/util/RegionQuery.java | 102 +++ .../com/sk89q/worldguard/internal/Events.java | 30 +- .../worldguard/internal/cause/Causes.java | 4 + .../internal/event/AbstractInteractEvent.java | 17 +- .../event/block/AbstractBlockEvent.java | 85 +++ .../internal/event/block/BreakBlockEvent.java | 52 ++ .../internal/event/block/PlaceBlockEvent.java | 52 ++ .../internal/event/block/UseBlockEvent.java | 55 ++ .../event/entity/AbstractEntityEvent.java | 87 +++ .../DestroyEntityEvent.java} | 31 +- .../event/entity/SpawnEntityEvent.java | 67 ++ .../UseEntityEvent.java} | 25 +- .../UseItemEvent.java} | 10 +- .../internal/listener/BlacklistListener.java | 217 +++++- .../listener/BlockedPotionsListener.java | 4 +- .../listener/ChestProtectionListener.java | 140 +++- .../listener/RegionProtectionListener.java | 155 +++- 28 files changed, 2278 insertions(+), 1145 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/listener/CauseListener.java rename src/main/java/com/sk89q/worldguard/{internal/event/Interaction.java => bukkit/util/Blocks.java} (51%) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/util/DamageCauses.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/util/Materials.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java create mode 100644 src/main/java/com/sk89q/worldguard/internal/event/block/AbstractBlockEvent.java create mode 100644 src/main/java/com/sk89q/worldguard/internal/event/block/BreakBlockEvent.java create mode 100644 src/main/java/com/sk89q/worldguard/internal/event/block/PlaceBlockEvent.java create mode 100644 src/main/java/com/sk89q/worldguard/internal/event/block/UseBlockEvent.java create mode 100644 src/main/java/com/sk89q/worldguard/internal/event/entity/AbstractEntityEvent.java rename src/main/java/com/sk89q/worldguard/internal/event/{BlockInteractEvent.java => entity/DestroyEntityEvent.java} (67%) create mode 100644 src/main/java/com/sk89q/worldguard/internal/event/entity/SpawnEntityEvent.java rename src/main/java/com/sk89q/worldguard/internal/event/{EntityInteractEvent.java => entity/UseEntityEvent.java} (73%) rename src/main/java/com/sk89q/worldguard/internal/event/{ItemInteractEvent.java => inventory/UseItemEvent.java} (85%) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java b/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java index 16aa971d..b483b9a9 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java @@ -42,6 +42,7 @@ import org.bukkit.entity.Tameable; import org.bukkit.inventory.ItemStack; +import javax.annotation.Nullable; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -303,7 +304,7 @@ public static > T tryEnum(Class enumType, String ... values /** * Get a blacklist target for the given block. - * + * * @param block the block * @return a target */ @@ -312,6 +313,22 @@ public static Target createTarget(Block block) { return new MaterialTarget(block.getTypeId(), block.getData()); } + /** + * Get a blacklist target for the given block. + * + * @param block the block + * @param material a fallback material + * @return a target + */ + public static Target createTarget(@Nullable Block block, Material material) { + checkNotNull(material); + if (block != null) { + return new MaterialTarget(block.getTypeId(), block.getData()); + } else { + return new MaterialTarget(material.getId(), (short) 0); + } + } + /** * Get a blacklist target for the given item. * diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardBlockListener.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardBlockListener.java index f8dc373a..4cab5fab 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardBlockListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardBlockListener.java @@ -23,17 +23,9 @@ import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.blocks.BlockType; import com.sk89q.worldedit.blocks.ItemType; -import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.blacklist.event.BlockDispenseBlacklistEvent; -import com.sk89q.worldguard.internal.Events; -import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.BlockInteractEvent; -import com.sk89q.worldguard.internal.event.Interaction; -import com.sk89q.worldguard.internal.event.ItemInteractEvent; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.managers.RegionManager; -import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; @@ -44,8 +36,6 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBurnEvent; -import org.bukkit.event.block.BlockDamageEvent; -import org.bukkit.event.block.BlockDispenseEvent; import org.bukkit.event.block.BlockExpEvent; import org.bukkit.event.block.BlockFadeEvent; import org.bukkit.event.block.BlockFormEvent; @@ -60,10 +50,8 @@ import org.bukkit.event.block.BlockSpreadEvent; import org.bukkit.event.block.EntityBlockFormEvent; import org.bukkit.event.block.LeavesDecayEvent; -import org.bukkit.event.block.SignChangeEvent; import org.bukkit.inventory.ItemStack; -import static com.sk89q.worldguard.bukkit.BukkitUtil.createTarget; import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; /** @@ -111,20 +99,6 @@ protected WorldConfiguration getWorldConfig(Player player) { return getWorldConfig(player.getWorld()); } - /* - * Called when a block is damaged. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockDamage(BlockDamageEvent event) { - Block target = event.getBlock(); - - // Cake are damaged and not broken when they are eaten, so we must - // handle them a bit separately - if (target.getType() == Material.CAKE_BLOCK) { - Events.fireToCancel(event, new BlockInteractEvent(event, Causes.create(event.getPlayer()), Interaction.INTERACT, target)); - } - } - /* * Called when a block is broken. */ @@ -141,8 +115,6 @@ public void onBlockBreak(BlockBreakEvent event) { player.setItemInHand(held); } } - - Events.fireToCancel(event, new BlockInteractEvent(event, Causes.create(event.getPlayer()), Interaction.BREAK, target)); } /* @@ -301,25 +273,9 @@ public void onBlockIgnite(BlockIgniteEvent event) { if (wcfg.useRegions) { Vector pt = toVector(block); - Player player = event.getPlayer(); RegionManager mgr = plugin.getGlobalRegionManager().get(world); ApplicableRegionSet set = mgr.getApplicableRegions(pt); - if (player != null && !plugin.getGlobalRegionManager().hasBypass(player, world)) { - LocalPlayer localPlayer = plugin.wrapPlayer(player); - - // this is preliminarily handled in the player listener under handleBlockRightClick - // why it's handled here too, no one knows - if (cause == IgniteCause.FLINT_AND_STEEL || cause == IgniteCause.FIREBALL) { - if (!set.allows(DefaultFlag.LIGHTER) - && !set.canBuild(localPlayer) - && !plugin.hasPermission(player, "worldguard.override.lighter")) { - event.setCancelled(true); - return; - } - } - } - if (wcfg.highFreqFlags && isFireSpread && !set.allows(DefaultFlag.FIRE_SPREAD)) { event.setCancelled(true); @@ -469,8 +425,6 @@ public void onBlockPlace(BlockPlaceEvent event) { ConfigurationManager cfg = plugin.getGlobalStateManager(); WorldConfiguration wcfg = cfg.get(world); - Events.fireToCancel(event, new BlockInteractEvent(event, Causes.create(event.getPlayer()), Interaction.PLACE, target)); - if (wcfg.simulateSponge && target.getType() == Material.SPONGE) { if (wcfg.redstoneSponges && target.isBlockIndirectlyPowered()) { return; @@ -519,75 +473,6 @@ public void onBlockRedstoneChange(BlockRedstoneEvent event) { } } - /* - * Called when a sign is changed. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onSignChange(SignChangeEvent event) { - Player player = event.getPlayer(); - WorldConfiguration wcfg = getWorldConfig(player); - - if (wcfg.signChestProtection) { - if (event.getLine(0).equalsIgnoreCase("[Lock]")) { - if (wcfg.isChestProtectedPlacement(event.getBlock(), player)) { - player.sendMessage(ChatColor.DARK_RED + "You do not own the adjacent chest."); - event.getBlock().breakNaturally(); - event.setCancelled(true); - return; - } - - if (event.getBlock().getTypeId() != BlockID.SIGN_POST) { - player.sendMessage(ChatColor.RED - + "The [Lock] sign must be a sign post, not a wall sign."); - - event.getBlock().breakNaturally(); - event.setCancelled(true); - return; - } - - if (!event.getLine(1).equalsIgnoreCase(player.getName())) { - player.sendMessage(ChatColor.RED - + "The first owner line must be your name."); - - event.getBlock().breakNaturally(); - event.setCancelled(true); - return; - } - - int below = event.getBlock().getRelative(0, -1, 0).getTypeId(); - - if (below == BlockID.TNT || below == BlockID.SAND - || below == BlockID.GRAVEL || below == BlockID.SIGN_POST) { - player.sendMessage(ChatColor.RED - + "That is not a safe block that you're putting this sign on."); - - event.getBlock().breakNaturally(); - event.setCancelled(true); - return; - } - - event.setLine(0, "[Lock]"); - player.sendMessage(ChatColor.YELLOW - + "A chest or double chest above is now protected."); - } - } else if (!wcfg.disableSignChestProtectionCheck) { - if (event.getLine(0).equalsIgnoreCase("[Lock]")) { - player.sendMessage(ChatColor.RED - + "WorldGuard's sign chest protection is disabled."); - - event.getBlock().breakNaturally(); - event.setCancelled(true); - return; - } - } - - if (!plugin.getGlobalRegionManager().canBuild(player, event.getBlock())) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); - event.setCancelled(true); - return; - } - } - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onLeavesDecay(LeavesDecayEvent event) { ConfigurationManager cfg = plugin.getGlobalStateManager(); @@ -827,21 +712,6 @@ public void onBlockPistonRetract(BlockPistonRetractEvent event) { } } - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockDispense(BlockDispenseEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - - if (wcfg.getBlacklist() != null) { - if (!wcfg.getBlacklist().check(new BlockDispenseBlacklistEvent(null, toVector(event.getBlock()), createTarget(event.getItem())), false, false)) { - event.setCancelled(true); - return; - } - } - - Events.fireToCancel(event, new ItemInteractEvent(event, Causes.create(event.getBlock()), Interaction.INTERACT, event.getBlock().getWorld(), event.getItem())); - } - /* * Called when a block yields exp */ diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardEntityListener.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardEntityListener.java index 24ababb6..ac5fb2ec 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardEntityListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardEntityListener.java @@ -22,11 +22,6 @@ import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.blacklist.event.ItemUseBlacklistEvent; -import com.sk89q.worldguard.internal.Events; -import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.Interaction; -import com.sk89q.worldguard.internal.event.ItemInteractEvent; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.GlobalRegionManager; import com.sk89q.worldguard.protection.events.DisallowedPVPEvent; @@ -83,7 +78,6 @@ import java.util.Set; -import static com.sk89q.worldguard.bukkit.BukkitUtil.createTarget; import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; /** @@ -225,26 +219,6 @@ private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { Entity attacker = event.getDamager(); Entity defender = event.getEntity(); - if (attacker instanceof Player) { - Player player = (Player) attacker; - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(player.getWorld()); - - ItemStack held = player.getInventory().getItemInHand(); - - if (held != null) { - if (wcfg.getBlacklist() != null) { - if (!wcfg.getBlacklist().check( - new ItemUseBlacklistEvent(plugin.wrapPlayer(player), - toVector(player.getLocation()), createTarget(held)), false, false)) { - event.setCancelled(true); - return; - } - } - } - } - if (defender instanceof ItemFrame) { if (checkItemFrameProtection(attacker, (ItemFrame) defender)) { event.setCancelled(true); @@ -934,9 +908,6 @@ public void onPotionSplash(PotionSplashEvent event) { World world = entity.getWorld(); ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - Events.fireToCancel(event, new ItemInteractEvent(event, Causes.create(potion.getShooter()), Interaction.INTERACT, world, potion.getItem())); GlobalRegionManager regionMan = plugin.getGlobalRegionManager(); @@ -1017,16 +988,7 @@ private boolean checkItemFrameProtection(Entity attacker, ItemFrame defender) { if (wcfg.useRegions) { // bukkit throws this event when a player attempts to remove an item from a frame RegionManager mgr = plugin.getGlobalRegionManager().get(world); - if (attacker instanceof Player) { - Player player = (Player) attacker; - LocalPlayer localPlayer = plugin.wrapPlayer(player); - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !mgr.getApplicableRegions(defender.getLocation()) - .canBuild(localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); - return true; - } - } else { + if (!(attacker instanceof Player)) { if (!plugin.getGlobalRegionManager().allows( DefaultFlag.ENTITY_ITEM_FRAME_DESTROY, defender.getLocation())) { return true; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardHangingListener.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardHangingListener.java index 8c906b71..53a7f141 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardHangingListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardHangingListener.java @@ -19,14 +19,8 @@ package com.sk89q.worldguard.bukkit; -import com.sk89q.worldedit.blocks.ItemID; -import com.sk89q.worldguard.blacklist.event.BlockBreakBlacklistEvent; -import com.sk89q.worldguard.blacklist.event.ItemUseBlacklistEvent; -import com.sk89q.worldguard.blacklist.target.MaterialTarget; import com.sk89q.worldguard.protection.flags.DefaultFlag; -import org.bukkit.ChatColor; import org.bukkit.World; -import org.bukkit.block.Block; import org.bukkit.entity.Creeper; import org.bukkit.entity.Entity; import org.bukkit.entity.Hanging; @@ -41,12 +35,8 @@ import org.bukkit.event.hanging.HangingBreakByEntityEvent; import org.bukkit.event.hanging.HangingBreakEvent; import org.bukkit.event.hanging.HangingBreakEvent.RemoveCause; -import org.bukkit.event.hanging.HangingPlaceEvent; -import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.projectiles.ProjectileSource; -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - /** * Listener for painting related events. * @@ -73,7 +63,7 @@ public void registerEvents() { } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onHangingingBreak(HangingBreakEvent event) { + public void onHangingBreak(HangingBreakEvent event) { Hanging hanging = event.getEntity(); World world = hanging.getWorld(); ConfigurationManager cfg = plugin.getGlobalStateManager(); @@ -88,33 +78,7 @@ public void onHangingingBreak(HangingBreakEvent event) { removerEntity = (remover instanceof LivingEntity ? (LivingEntity) remover : null); } - if (removerEntity instanceof Player) { - Player player = (Player) removerEntity; - - if (wcfg.getBlacklist() != null) { - if (hanging instanceof Painting - && !wcfg.getBlacklist().check( - new BlockBreakBlacklistEvent(plugin.wrapPlayer(player), - toVector(player.getLocation()), new MaterialTarget(ItemID.PAINTING, (short) 0)), false, false)) { - event.setCancelled(true); - return; - } else if (hanging instanceof ItemFrame - && !wcfg.getBlacklist().check( - new BlockBreakBlacklistEvent(plugin.wrapPlayer(player), - toVector(player.getLocation()), new MaterialTarget(ItemID.ITEM_FRAME, (short) 0)), false, false)) { - event.setCancelled(true); - return; - } - } - - if (wcfg.useRegions) { - if (!plugin.getGlobalRegionManager().canBuild(player, hanging.getLocation())) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); - event.setCancelled(true); - return; - } - } - } else { + if (!(removerEntity instanceof Player)) { if (removerEntity instanceof Creeper) { if (wcfg.blockCreeperBlockDamage || wcfg.blockCreeperExplosions) { event.setCancelled(true); @@ -152,65 +116,4 @@ public void onHangingingBreak(HangingBreakEvent event) { } } - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onHangingPlace(HangingPlaceEvent event) { - Block placedOn = event.getBlock(); - Player player = event.getPlayer(); - World world = placedOn.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.getBlacklist() != null) { - - if (event.getEntity() instanceof Painting - && !wcfg.getBlacklist().check( - new ItemUseBlacklistEvent(plugin.wrapPlayer(player), - toVector(player.getLocation()), new MaterialTarget(ItemID.PAINTING, (short) 0)), false, false)) { - event.setCancelled(true); - return; - } else if (event.getEntity() instanceof ItemFrame - && !wcfg.getBlacklist().check( - new ItemUseBlacklistEvent(plugin.wrapPlayer(player), - toVector(player.getLocation()), new MaterialTarget(ItemID.ITEM_FRAME, (short) 0)), false, false)) { - event.setCancelled(true); - return; - } - } - - if (wcfg.useRegions) { - if (!plugin.getGlobalRegionManager().canBuild(player, placedOn.getRelative(event.getBlockFace()))) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); - event.setCancelled(true); - return; - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onEntityInteract(PlayerInteractEntityEvent event) { - Player player = event.getPlayer(); - Entity entity = event.getRightClicked(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(entity.getWorld()); - - if (wcfg.useRegions && (entity instanceof ItemFrame || entity instanceof Painting)) { - if (!plugin.getGlobalRegionManager().canBuild(player, entity.getLocation())) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); - event.setCancelled(true); - return; - } - -// if (entity instanceof ItemFrame -// && ((!plugin.getGlobalRegionManager().allows( -// DefaultFlag.ENTITY_ITEM_FRAME_DESTROY, entity.getLocation())))) { -// event.setCancelled(true); -// } else if (entity instanceof Painting -// && ((!plugin.getGlobalRegionManager().allows( -// DefaultFlag.ENTITY_PAINTING_DESTROY, entity.getLocation())))) { -// event.setCancelled(true); -// } - } - } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlayerListener.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlayerListener.java index 50146b8a..df660abc 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlayerListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlayerListener.java @@ -21,20 +21,9 @@ import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BlockID; -import com.sk89q.worldedit.blocks.BlockType; -import com.sk89q.worldedit.blocks.ItemID; import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.blacklist.event.BlockBreakBlacklistEvent; -import com.sk89q.worldguard.blacklist.event.BlockInteractBlacklistEvent; -import com.sk89q.worldguard.blacklist.event.BlockPlaceBlacklistEvent; -import com.sk89q.worldguard.blacklist.event.ItemAcquireBlacklistEvent; -import com.sk89q.worldguard.blacklist.event.ItemDropBlacklistEvent; import com.sk89q.worldguard.blacklist.event.ItemUseBlacklistEvent; import com.sk89q.worldguard.bukkit.FlagStateManager.PlayerFlagState; -import com.sk89q.worldguard.internal.Events; -import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.Interaction; -import com.sk89q.worldguard.internal.event.ItemInteractEvent; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.managers.RegionManager; @@ -45,19 +34,14 @@ import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.entity.Entity; -import org.bukkit.entity.Item; import org.bukkit.entity.Player; -import org.bukkit.event.Event.Result; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerBedEnterEvent; -import org.bukkit.event.player.PlayerBucketEmptyEvent; -import org.bukkit.event.player.PlayerBucketFillEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerFishEvent; @@ -68,7 +52,6 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerPickupItemEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerTeleportEvent; @@ -474,39 +457,13 @@ public void onPlayerQuit(PlayerQuitEvent event) { plugin.forgetPlayer(player); } - @EventHandler(priority = EventPriority.HIGH) - public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { - // TODO reduce duplication with right-click-air - Player player = event.getPlayer(); - World world = player.getWorld(); - ItemStack item = player.getItemInHand(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.getBlacklist() != null) { - if (!wcfg.getBlacklist().check( - new ItemUseBlacklistEvent(plugin.wrapPlayer(player), toVector(player.getLocation()), createTarget(item)), false, false)) { - event.setCancelled(true); - return; - } - } - } - @EventHandler(priority = EventPriority.HIGH) public void onPlayerInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); World world = player.getWorld(); - ItemStack item = event.getItem(); if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { handleBlockRightClick(event); - } else if (event.getAction() == Action.RIGHT_CLICK_AIR) { - handleAirRightClick(event); - } else if (event.getAction() == Action.LEFT_CLICK_BLOCK) { - handleBlockLeftClick(event); - } else if (event.getAction() == Action.LEFT_CLICK_AIR) { - handleAirLeftClick(event); } else if (event.getAction() == Action.PHYSICAL) { handlePhysicalInteract(event); } @@ -523,115 +480,6 @@ public void onPlayerInteract(PlayerInteractEvent event) { player.sendMessage(ChatColor.RED + "Infinite stack removed."); } } - - if (item != null) { - Events.fireItemEventToCancel(event, new ItemInteractEvent(event, Causes.create(event.getPlayer()), Interaction.INTERACT, world, item)); - } - } - - /** - * Called when a player left clicks air. - * - * @param event Thrown event - */ - private void handleAirLeftClick(PlayerInteractEvent event) { - // I don't think we have to do anything here yet. - return; - } - - /** - * Called when a player left clicks a block. - * - * @param event Thrown event - */ - private void handleBlockLeftClick(PlayerInteractEvent event) { - if (event.isCancelled()) return; - - Player player = event.getPlayer(); - Block block = event.getClickedBlock(); - int type = block.getTypeId(); - World world = player.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.useRegions) { - Vector pt = toVector(block); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - LocalPlayer localPlayer = plugin.wrapPlayer(player); - - /*if (type == BlockID.STONE_BUTTON - || type == BlockID.LEVER - || type == BlockID.WOODEN_DOOR - || type == BlockID.TRAP_DOOR - || type == BlockID.NOTE_BLOCK) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.allows(DefaultFlag.USE, localPlayer) - && !set.canBuild(localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission to use that in this area."); - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - }*/ - - if (type == BlockID.DRAGON_EGG) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You're not allowed to move dragon eggs here!"); - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - } - - if (block.getRelative(event.getBlockFace()).getTypeId() == BlockID.FIRE) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !mgr.getApplicableRegions(block.getRelative(event.getBlockFace()) - .getLocation()).canBuild(localPlayer)) { - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - } - - } - - if (type == BlockID.TNT && player.getItemInHand().getTypeId() == ItemID.FLINT_AND_TINDER) { - if (wcfg.getBlacklist() != null) { - if (!wcfg.getBlacklist().check( - new BlockBreakBlacklistEvent(plugin.wrapPlayer(player), toVector(event.getClickedBlock()), createTarget(event.getClickedBlock())), false, false)) { - event.setUseInteractedBlock(Result.DENY); - event.setUseItemInHand(Result.DENY); - event.setCancelled(true); - return; - } - } - } - } - - /** - * Called when a player right clicks air. - * - * @param event Thrown event - */ - private void handleAirRightClick(PlayerInteractEvent event) { - Player player = event.getPlayer(); - World world = player.getWorld(); - ItemStack item = player.getItemInHand(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.getBlacklist() != null) { - if (!wcfg.getBlacklist().check( - new ItemUseBlacklistEvent(plugin.wrapPlayer(player), toVector(player.getLocation()), createTarget(item)), false, false)) { - event.setCancelled(true); - event.setUseItemInHand(Result.DENY); - return; - } - } } /** @@ -699,396 +547,8 @@ private void handleBlockRightClick(PlayerInteractEvent event) { } event.setCancelled(true); - return; - } - - if (item.getTypeId() == BlockID.TNT) { - // workaround for a bug that allowed tnt to trigger instantly if placed - // next to redstone, without plugins getting the block place event - // (not sure if this actually still happens) - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !placedInSet.allows(DefaultFlag.TNT, localPlayer)) { - event.setUseItemInHand(Result.DENY); - event.setCancelled(true); - } - } - - // hacky workaround for craftbukkit bug - // has since been fixed, but leaving for legacy - if (item.getTypeId() == BlockID.STEP - || item.getTypeId() == BlockID.WOODEN_STEP) { - if (!plugin.getGlobalRegionManager().hasBypass(localPlayer, world)) { - boolean cancel = false; - if ((block.getTypeId() == item.getTypeId()) - && !set.canBuild(localPlayer)) { - // if we are on a step already, the new block will end up in the same block as the interact - cancel = true; - } else if (!placedInSet.canBuild(localPlayer)) { - // if we are on another block, the half-slab in hand will be pushed to the adjacent block - cancel = true; - } - if (cancel) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); - event.setCancelled(true); - return; - } - } - } - - if (item.getTypeId() == ItemID.BED_ITEM) { - // this is mojang-level code, it had better give us the right direction - double yaw = (player.getLocation().getYaw() * 4.0F / 360.0F) + 0.5D; - int i = (int) yaw; - int i1 = (yaw < i ? i - 1 : i) & 3; - byte b0 = 0; - byte b1 = 0; - if (i1 == 0) { - b1 = 1; - } - if (i1 == 1) { - b0 = -1; - } - if (i1 == 2) { - b1 = -1; - } - if (i1 == 3) { - b0 = 1; - } - // end mojang-level code - Location headLoc = placedIn.getRelative(b0, 0, b1).getLocation(); - if (!plugin.getGlobalRegionManager().hasBypass(localPlayer, world) - && !mgr.getApplicableRegions(headLoc).canBuild(localPlayer)) { - // note that normal block placement is handled later, this is just a workaround - // for the location of the head block of the bed - player.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); - event.setCancelled(true); - return; - } - } - - if (block.getTypeId() == BlockID.PISTON_MOVING_PIECE) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer)) { - event.setUseInteractedBlock(Result.DENY); - } - } - - if (item.getTypeId() == ItemID.WOODEN_DOOR_ITEM || item.getTypeId() == ItemID.IRON_DOOR_ITEM) { - if (!plugin.getGlobalRegionManager().hasBypass(localPlayer, world) - && !placedInSet.canBuild(localPlayer)) { - // note that normal block placement is handled later, this is just a workaround - // for the location of the top block of the door - player.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); - event.setCancelled(true); - return; - } - } - - if (item.getTypeId() == ItemID.FIRE_CHARGE || item.getTypeId() == ItemID.FLINT_AND_TINDER) { - if (!plugin.getGlobalRegionManager().hasBypass(localPlayer, world) - && !plugin.canBuild(player, placedIn) - && !placedInSet.allows(DefaultFlag.LIGHTER)) { - event.setCancelled(true); - event.setUseItemInHand(Result.DENY); - player.sendMessage(ChatColor.DARK_RED + "You're not allowed to use that here."); - return; - } - } - - if (item.getTypeId() == ItemID.EYE_OF_ENDER && block.getTypeId() == BlockID.END_PORTAL_FRAME) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer)) { - event.setCancelled(true); - event.setUseItemInHand(Result.DENY); - player.sendMessage(ChatColor.DARK_RED + "You're not allowed to use that here."); - return; - } - } - - if (item.getTypeId() == ItemID.INK_SACK - && item.getData() != null) { - if (item.getData().getData() == 15 // bonemeal - && (type == BlockID.GRASS - || type == BlockID.SAPLING - || type == BlockID.CROPS - || type == BlockID.BROWN_MUSHROOM - || type == BlockID.RED_MUSHROOM - || type == BlockID.PUMPKIN_STEM - || type == BlockID.MELON_STEM - || type == BlockID.POTATOES - || type == BlockID.CARROTS - || type == BlockID.COCOA_PLANT - || type == BlockID.LONG_GRASS)) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer)) { - event.setCancelled(true); - event.setUseItemInHand(Result.DENY); - player.sendMessage(ChatColor.DARK_RED + "You're not allowed to use that here."); - return; - } - } else if (item.getData().getData() == 3) { // cocoa beans - // craftbukkit doesn't throw a block place for this, so workaround - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer)) { - if (!(event.getBlockFace() == BlockFace.DOWN || event.getBlockFace() == BlockFace.UP)) { - event.setCancelled(true); - event.setUseItemInHand(Result.DENY); - player.sendMessage(ChatColor.DARK_RED + "You're not allowed to plant that here."); - return; - } - } - } - } - - if (type == BlockID.FLOWER_POT) { // no api for this atm - if (item.getTypeId() == BlockID.RED_FLOWER - || item.getTypeId() == BlockID.YELLOW_FLOWER - || item.getTypeId() == BlockID.SAPLING - || item.getTypeId() == BlockID.RED_MUSHROOM - || item.getTypeId() == BlockID.BROWN_MUSHROOM - || item.getTypeId() == BlockID.CACTUS - || item.getTypeId() == BlockID.LONG_GRASS - || item.getTypeId() == BlockID.DEAD_BUSH) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer)) { - event.setUseItemInHand(Result.DENY); - event.setCancelled(true); - player.sendMessage(ChatColor.DARK_RED + "You're not allowed to plant that here."); - return; - } - } - } - - if (type == BlockID.BED) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.allows(DefaultFlag.SLEEP, localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You're not allowed to use that bed."); - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - } - - if (type == BlockID.CHEST - || type == BlockID.JUKEBOX //stores the (arguably) most valuable item - || type == BlockID.DISPENSER - || type == BlockID.FURNACE - || type == BlockID.BURNING_FURNACE - || type == BlockID.BREWING_STAND - || type == BlockID.TRAPPED_CHEST - || type == BlockID.HOPPER - || type == BlockID.DROPPER) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer) - && !set.allows(DefaultFlag.CHEST_ACCESS, localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission to open that in this area."); - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - } - - if (type == BlockID.DRAGON_EGG) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You're not allowed to move dragon eggs here!"); - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - } - - if (type == BlockID.LEVER - || type == BlockID.STONE_BUTTON - || type == BlockID.WOODEN_BUTTON - || type == BlockID.NOTE_BLOCK - || type == BlockID.REDSTONE_REPEATER_OFF - || type == BlockID.REDSTONE_REPEATER_ON - || type == BlockID.WOODEN_DOOR - || type == BlockID.TRAP_DOOR - || type == BlockID.FENCE_GATE - || type == BlockID.JUKEBOX //stores the (arguably) most valuable item - || type == BlockID.DISPENSER - || type == BlockID.FURNACE - || type == BlockID.BURNING_FURNACE - || type == BlockID.WORKBENCH - || type == BlockID.BREWING_STAND - || type == BlockID.ENCHANTMENT_TABLE - || type == BlockID.CAULDRON - || type == BlockID.ENDER_CHEST // blah - || type == BlockID.BEACON - || type == BlockID.ANVIL - || type == BlockID.HOPPER - || type == BlockID.DROPPER) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer) - && !set.allows(DefaultFlag.USE, localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission to use that in this area."); - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - } - - if (type == BlockID.REDSTONE_REPEATER_OFF - || type == BlockID.REDSTONE_REPEATER_ON - || type == BlockID.COMPARATOR_OFF - || type == BlockID.COMPARATOR_ON) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer)) { - // using build and not use because it can potentially damage a circuit and use is more general-purposed - player.sendMessage(ChatColor.DARK_RED + "You don't have permission to use that in this area."); - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - } - - if (type == BlockID.CAKE_BLOCK) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer) - && !set.allows(DefaultFlag.USE, localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You're not invited to this tea party!"); - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - } - - if (BlockType.isRailBlock(type) - && (item.getTypeId() == ItemID.MINECART - || item.getTypeId() == ItemID.POWERED_MINECART - || item.getTypeId() == ItemID.STORAGE_MINECART - || item.getTypeId() == ItemID.TNT_MINECART - || item.getTypeId() == ItemID.HOPPER_MINECART)) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !placedInSet.canBuild(localPlayer) - && !placedInSet.allows(DefaultFlag.PLACE_VEHICLE, localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission to place vehicles here."); - event.setUseItemInHand(Result.DENY); - event.setCancelled(true); - return; - } - } - - if (item.getTypeId() == ItemID.WOOD_BOAT) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !placedInSet.canBuild(localPlayer) - && !placedInSet.allows(DefaultFlag.PLACE_VEHICLE, localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission to place vehicles here."); - event.setUseItemInHand(Result.DENY); - event.setCancelled(true); - return; - } } } - - if (wcfg.getBlacklist() != null) { - if (player.isSneaking() // sneak + right clicking no longer opens guis as of some recent version - || (type != BlockID.CHEST - && type != BlockID.DISPENSER - && type != BlockID.FURNACE - && type != BlockID.BURNING_FURNACE - && type != BlockID.BREWING_STAND - && type != BlockID.ENCHANTMENT_TABLE - && type != BlockID.ANVIL - && type != BlockID.ENDER_CHEST - && type != BlockID.TRAPPED_CHEST - && type != BlockID.HOPPER - && type != BlockID.DROPPER)) { - if (!wcfg.getBlacklist().check( - new ItemUseBlacklistEvent(plugin.wrapPlayer(player), toVector(block), createTarget(item)), false, false)) { - event.setUseItemInHand(Result.DENY); - event.setCancelled(true); - return; - } - } - - if (!wcfg.getBlacklist().check( - new BlockInteractBlacklistEvent(plugin.wrapPlayer(player), toVector(block), createTarget(block)), false, false)) { - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - - // Workaround for http://leaky.bukkit.org/issues/1034 - if (item.getTypeId() == BlockID.TNT) { - Block placedOn = block.getRelative(event.getBlockFace()); - if (!wcfg.getBlacklist().check( - new BlockPlaceBlacklistEvent(plugin.wrapPlayer(player), toVector(placedOn), createTarget(item)), false, false)) { - event.setUseItemInHand(Result.DENY); - event.setCancelled(true); - return; - } - } - } - - if ((type == BlockID.CHEST - || type == BlockID.DISPENSER - || type == BlockID.FURNACE - || type == BlockID.BURNING_FURNACE - || type == BlockID.ENCHANTMENT_TABLE - || type == BlockID.BREWING_STAND - || type == BlockID.TRAPPED_CHEST - || type == BlockID.HOPPER - || type == BlockID.DROPPER)) { - - if (wcfg.isChestProtected(block, player)) { - player.sendMessage(ChatColor.DARK_RED + "The chest is protected."); - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - } - - /*if (wcfg.useRegions && wcfg.useiConomy && cfg.getiConomy() != null - && (type == BlockID.SIGN_POST || type == ItemID.SIGN || type == BlockID.WALL_SIGN)) { - BlockState block = blockClicked.getState(); - - if (((Sign)block).getLine(0).equalsIgnoreCase("[WorldGuard]") - && ((Sign)block).getLine(1).equalsIgnoreCase("For sale")) { - String regionId = ((Sign)block).getLine(2); - //String regionComment = ((Sign)block).getLine(3); - - if (regionId != null && regionId != "") { - RegionManager mgr = cfg.getWorldGuardPlugin().getGlobalRegionManager().get(player.getWorld().getName()); - ProtectedRegion region = mgr.getRegion(regionId); - - if (region != null) { - RegionFlags flags = region.getFlags(); - - if (flags.getBooleanFlag(DefaultFlag.BUYABLE).getValue(false)) { - if (iConomy.getBank().hasAccount(player.getName())) { - Account account = iConomy.getBank().getAccount(player.getName()); - double balance = account.getBalance(); - double regionPrice = flags.getDoubleFlag(DefaultFlag.PRICE).getValue(); - - if (balance >= regionPrice) { - account.subtract(regionPrice); - player.sendMessage(ChatColor.YELLOW + "You have bought the region " + regionId + " for " + - iConomy.getBank().format(regionPrice)); - DefaultDomain owners = region.getOwners(); - owners.addPlayer(player.getName()); - region.setOwners(owners); - flags.getBooleanFlag(DefaultFlag.BUYABLE).setValue(false); - account.save(); - } else { - player.sendMessage(ChatColor.YELLOW + "You have not enough money."); - } - } else { - player.sendMessage(ChatColor.YELLOW + "You have not enough money."); - } - } else { - player.sendMessage(ChatColor.RED + "Region: " + regionId + " is not buyable"); - } - } else { - player.sendMessage(ChatColor.DARK_RED + "The region " + regionId + " does not exist."); - } - } else { - player.sendMessage(ChatColor.DARK_RED + "No region specified."); - } - } - }*/ } /** @@ -1111,94 +571,8 @@ private void handlePhysicalInteract(PlayerInteractEvent event) { event.setCancelled(true); return; } - - if (wcfg.useRegions) { - Vector pt = toVector(block); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - LocalPlayer localPlayer = plugin.wrapPlayer(player); - - if (type == BlockID.STONE_PRESSURE_PLATE || type == BlockID.WOODEN_PRESSURE_PLATE - || type == BlockID.TRIPWIRE || type == BlockID.PRESSURE_PLATE_LIGHT - || type == BlockID.PRESSURE_PLATE_HEAVY) { - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer) - && !set.allows(DefaultFlag.USE, localPlayer)) { - event.setUseInteractedBlock(Result.DENY); - event.setCancelled(true); - return; - } - } - } } - /** - * Called when a player uses an item. - *//* - @Override - public void onPlayerItem(PlayerItemEvent event) { - - if (event.isCancelled()) { - return; - } - - Player player = event.getPlayer(); - Block block = event.getBlockClicked(); - ItemStack item = event.getItem(); - int itemId = item.getTypeId(); - - GlobalConfiguration cfg = plugin.getGlobalConfiguration(); - WorldConfiguration wcfg = cfg.getWorldConfig(player.getWorld().getName()); - - if (wcfg.useRegions - && (itemId == 322 || itemId == 320 || itemId == 319 || itemId == 297 || itemId == 260 - || itemId == 350 || itemId == 349 || itemId == 354) ) { - return; - } - - if (!wcfg.itemDurability) { - // Hoes - if (item.getTypeId() >= 290 && item.getTypeId() <= 294) { - item.setDurability((byte) -1); - player.setItemInHand(item); - } - } - - if (wcfg.useRegions && !event.isBlock() && block != null) { - Vector pt = toVector(block.getRelative(event.getBlockFace())); - if (block.getTypeId() == BlockID.WALL_SIGN) { - pt = pt.subtract(0, 1, 0); - } - - if (!cfg.canBuild(player, pt)) { - player.sendMessage(ChatColor.DARK_RED - + "You don't have permission for this area."); - event.setCancelled(true); - return; - } - } - - if (wcfg.getBlacklist() != null && item != null && block != null) { - if (!wcfg.getBlacklist().check( - new ItemUseBlacklistEvent(plugin.wrapPlayer(player), - toVector(block.getRelative(event.getBlockFace())), - item.getTypeId()), false, false)) { - event.setCancelled(true); - return; - } - } - - if (wcfg.useRegions && item != null && block != null && item.getTypeId() == 259) { - Vector pt = toVector(block.getRelative(event.getBlockFace())); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld().getName()); - - if (!mgr.getApplicableRegions(pt).isStateFlagAllowed(DefaultFlag.LIGHTER)) { - event.setCancelled(true); - return; - } - } - }*/ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onPlayerDropItem(PlayerDropItemEvent event) { ConfigurationManager cfg = plugin.getGlobalStateManager(); @@ -1212,61 +586,6 @@ public void onPlayerDropItem(PlayerDropItemEvent event) { player.sendMessage(ChatColor.RED + "You don't have permission to do that in this area."); } } - - if (wcfg.getBlacklist() != null) { - Item ci = event.getItemDrop(); - - if (!wcfg.getBlacklist().check( - new ItemDropBlacklistEvent(plugin.wrapPlayer(event.getPlayer()), - toVector(ci.getLocation()), createTarget(ci.getItemStack())), false, false)) { - event.setCancelled(true); - return; - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPlayerPickupItem(PlayerPickupItemEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getPlayer().getWorld()); - - if (wcfg.getBlacklist() != null) { - Item ci = event.getItem(); - - if (!wcfg.getBlacklist().check( - new ItemAcquireBlacklistEvent(plugin.wrapPlayer(event.getPlayer()), - toVector(ci.getLocation()), createTarget(ci.getItemStack())), false, true)) { - event.setCancelled(true); - return; - } - } - } - - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPlayerBucketFill(PlayerBucketFillEvent event) { - Player player = event.getPlayer(); - World world = player.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (!plugin.getGlobalRegionManager().canBuild( - player, event.getBlockClicked().getRelative(event.getBlockFace())) - && !(event.getItemStack().getTypeId() == ItemID.MILK_BUCKET)) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); - event.setCancelled(true); - return; - } - - if (wcfg.getBlacklist() != null) { - if (!wcfg.getBlacklist().check( - new ItemUseBlacklistEvent(plugin.wrapPlayer(player), - toVector(player.getLocation()), createTarget(event.getBucket())), false, false)) { - event.setCancelled(true); - return; - } - } } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) @@ -1279,31 +598,6 @@ public void onPlayerFish(PlayerFishEvent event) { } } - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) { - Player player = event.getPlayer(); - World world = player.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (!plugin.getGlobalRegionManager().canBuild( - player, event.getBlockClicked().getRelative(event.getBlockFace()))) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); - event.setCancelled(true); - return; - } - - if (wcfg.getBlacklist() != null) { - if (!wcfg.getBlacklist().check( - new ItemUseBlacklistEvent(plugin.wrapPlayer(player), - toVector(player.getLocation()), createTarget(event.getBucket())), false, false)) { - event.setCancelled(true); - return; - } - } - } - @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerRespawn(PlayerRespawnEvent event) { Player player = event.getPlayer(); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardVehicleListener.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardVehicleListener.java index 2f95575a..6cc4c7ad 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardVehicleListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardVehicleListener.java @@ -19,22 +19,12 @@ package com.sk89q.worldguard.bukkit; -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - -import org.bukkit.ChatColor; import org.bukkit.World; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.Vehicle; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.vehicle.VehicleDestroyEvent; import org.bukkit.event.vehicle.VehicleMoveEvent; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; public class WorldGuardVehicleListener implements Listener { @@ -56,34 +46,6 @@ public void registerEvents() { plugin.getServer().getPluginManager().registerEvents(this, plugin); } - @EventHandler - public void onVehicleDestroy(VehicleDestroyEvent event) { - Vehicle vehicle = event.getVehicle(); - Entity destroyer = event.getAttacker(); - - if (!(destroyer instanceof Player)) return; // don't care - Player player = (Player) destroyer; - World world = vehicle.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.useRegions) { - Vector pt = toVector(vehicle.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - LocalPlayer localPlayer = plugin.wrapPlayer(player); - - if (!plugin.getGlobalRegionManager().hasBypass(player, world) - && !set.canBuild(localPlayer) - && !set.allows(DefaultFlag.DESTROY_VEHICLE, localPlayer)) { - player.sendMessage(ChatColor.DARK_RED + "You don't have permission to destroy vehicles here."); - event.setCancelled(true); - return; - } - } - } - @EventHandler public void onVehicleMove(VehicleMoveEvent event) { Vehicle vehicle = event.getVehicle(); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/CauseListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/CauseListener.java new file mode 100644 index 00000000..485b0b31 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/CauseListener.java @@ -0,0 +1,490 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.util.Blocks; +import com.sk89q.worldguard.bukkit.util.Materials; +import com.sk89q.worldguard.internal.Events; +import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.internal.cause.Causes; +import com.sk89q.worldguard.internal.event.block.BreakBlockEvent; +import com.sk89q.worldguard.internal.event.block.PlaceBlockEvent; +import com.sk89q.worldguard.internal.event.block.UseBlockEvent; +import com.sk89q.worldguard.internal.event.entity.DestroyEntityEvent; +import com.sk89q.worldguard.internal.event.entity.SpawnEntityEvent; +import com.sk89q.worldguard.internal.event.entity.UseEntityEvent; +import com.sk89q.worldguard.internal.event.inventory.UseItemEvent; +import org.bukkit.DyeColor; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.event.Event.Result; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockDamageEvent; +import org.bukkit.event.block.BlockDispenseEvent; +import org.bukkit.event.block.BlockFromToEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityCombustByBlockEvent; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityDamageByBlockEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityTameEvent; +import org.bukkit.event.entity.EntityUnleashEvent; +import org.bukkit.event.entity.PotionSplashEvent; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; +import org.bukkit.event.hanging.HangingBreakEvent; +import org.bukkit.event.hanging.HangingPlaceEvent; +import org.bukkit.event.player.PlayerBedEnterEvent; +import org.bukkit.event.player.PlayerBucketEmptyEvent; +import org.bukkit.event.player.PlayerBucketFillEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.player.PlayerShearEntityEvent; +import org.bukkit.event.player.PlayerUnleashEntityEvent; +import org.bukkit.event.vehicle.VehicleDamageEvent; +import org.bukkit.event.vehicle.VehicleDestroyEvent; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.List; + +import static com.sk89q.worldguard.bukkit.util.Materials.isBlockModifiedOnClick; +import static com.sk89q.worldguard.bukkit.util.Materials.isItemAppliedToBlock; +import static com.sk89q.worldguard.internal.cause.Causes.create; + +public class CauseListener implements Listener { + + private final WorldGuardPlugin plugin; + + public CauseListener(WorldGuardPlugin plugin) { + this.plugin = plugin; + } + + public void registerEvents() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + //------------------------------------------------------------------------- + // Block break / place + //------------------------------------------------------------------------- + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + Events.fireToCancel(event, new BreakBlockEvent(event, create(event.getPlayer()), event.getBlock())); + } + + @EventHandler + public void onBlockPlace(BlockPlaceEvent event) { + Events.fireToCancel(event, new UseBlockEvent(event, create(event.getPlayer()), event.getBlock())); + } + + @EventHandler + public void onBlockBurn(BlockBurnEvent event) { + Events.fireToCancel(event, new UseBlockEvent(event, Collections.>emptyList(), event.getBlock())); + } + + // TODO: Handle EntityCreatePortalEvent? + + @EventHandler + public void onEntityChangeBlock(EntityChangeBlockEvent event) { + // Fire two events: one as BREAK and one as PLACE + if (event.getTo() != Material.AIR && event.getBlock().getType() != Material.AIR) { + Events.fireToCancel(event, new BreakBlockEvent(event, create(event.getEntity()), event.getBlock())); + Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getEntity()), event.getBlock())); + } else { + if (event.getTo() == Material.AIR) { + Events.fireToCancel(event, new BreakBlockEvent(event, create(event.getEntity()), event.getBlock())); + } else { + Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getEntity()), event.getBlock())); + } + } + } + + // TODO: Handle pistons + // TODO: Handle EntityExplodeEvent + + //------------------------------------------------------------------------- + // Block external interaction + //------------------------------------------------------------------------- + + @EventHandler + public void onBlockDamage(BlockDamageEvent event) { + Block target = event.getBlock(); + + // Previously, and perhaps still, the only way to catch cake eating + // events was through here + if (target.getType() == Material.CAKE_BLOCK) { + Events.fireToCancel(event, new UseBlockEvent(event, create(event.getPlayer()), target)); + } + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + Player player = event.getPlayer(); + @Nullable ItemStack item = player.getItemInHand(); + Block block = event.getClickedBlock(); + List> causes = create(player); + + switch (event.getAction()) { + case PHYSICAL: + // TODO: Don't fire events for blocks that can't be interacted with using PHYSICAL + if (Events.fireAndTestCancel(new UseBlockEvent(event, causes, block))) { + event.setUseInteractedBlock(Result.DENY); + event.setCancelled(true); + } + break; + + case RIGHT_CLICK_BLOCK: + if (item != null && item.getType() == Material.TNT) { + // Workaround for a bug that allowed tnt to trigger instantly if placed + // next to redstone, without plugins getting the block place event + // (not sure if this actually still happens) + Events.fireToCancel(event, new UseBlockEvent(event, create(event.getPlayer()), block.getLocation(), Material.TNT)); + } + + // Handle created Minecarts + if (item != null && Materials.isMinecart(item.getType())) { + // TODO: Give a more specific minecart type + Block placedBlock = block.getRelative(event.getBlockFace()); + Events.fireToCancel(event, new SpawnEntityEvent(event, create(event.getPlayer()), placedBlock.getLocation().add(0.5, 0, 0.5), EntityType.MINECART)); + } + + // Handle cocoa beans + if (item != null && item.getType() == Material.INK_SACK && Materials.isDyeColor(item.getData(), DyeColor.BROWN)) { + // CraftBukkit doesn't or didn't throw a block place for this + if (!(event.getBlockFace() == BlockFace.DOWN || event.getBlockFace() == BlockFace.UP)) { + Block placedBlock = block.getRelative(event.getBlockFace()); + Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getPlayer()), placedBlock.getLocation(), Material.COCOA)); + } + } + + // Workaround for http://leaky.bukkit.org/issues/1034 + if (item != null && item.getType() == Material.TNT) { + Block placedBlock = block.getRelative(event.getBlockFace()); + Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getPlayer()), placedBlock.getLocation(), Material.TNT)); + } + + // Handle flint and steel and fire charge as fire place + if (item != null && (item.getType() == Material.FIREBALL || item.getType() == Material.FLINT_AND_STEEL)) { + Block placedBlock = block.getRelative(event.getBlockFace()); + if (!Events.fireAndTestCancel(new PlaceBlockEvent(event, create(event.getPlayer()), placedBlock.getLocation(), Material.FIRE))) { + event.setUseItemInHand(Result.DENY); + } + } + + case LEFT_CLICK_BLOCK: + // TODO: Don't fire events for blocks that can't be interacted with using clicks + + // As of MC ~1.6, sneaking blocks the use of blocks with right click + if (!player.isSneaking() || event.getAction() == Action.LEFT_CLICK_BLOCK) { + // Only fire events for blocks that are modified when right clicked + if (isBlockModifiedOnClick(block.getType()) || (item != null && isItemAppliedToBlock(item.getType(), block.getType()))) { + if (Events.fireAndTestCancel(new UseBlockEvent(event, causes, block))) { + event.setUseInteractedBlock(Result.DENY); + } + + // Handle connected blocks (i.e. beds, chests) + for (Block connected : Blocks.getConnected(block)) { + if (Events.fireAndTestCancel(new UseBlockEvent(event, create(event.getPlayer()), connected))) { + event.setUseInteractedBlock(Result.DENY); + break; + } + } + } + + // Special handling of flint and steel on TNT + if (block.getType() == Material.TNT && item != null && item.getType() == Material.FLINT_AND_STEEL) { + if (Events.fireAndTestCancel(new BreakBlockEvent(event, create(event.getPlayer()), block))) { + event.setUseInteractedBlock(Result.DENY); + break; + } + } + } + + // Special handling of putting out fires + if (event.getAction() == Action.LEFT_CLICK_BLOCK && block.getType() == Material.FIRE) { + if (Events.fireAndTestCancel(new BreakBlockEvent(event, create(event.getPlayer()), block))) { + event.setUseInteractedBlock(Result.DENY); + break; + } + } + + case LEFT_CLICK_AIR: + case RIGHT_CLICK_AIR: + if (item != null && Events.fireAndTestCancel(new UseItemEvent(event, causes, player.getWorld(), item))) { + event.setUseItemInHand(Result.DENY); + } + + break; + } + } + + @EventHandler + public void onEntityInteract(org.bukkit.event.entity.EntityInteractEvent event) { + Events.fireToCancel(event, new UseBlockEvent(event, create(event.getEntity()), event.getBlock())); + } + + @EventHandler + public void onBlockIgnite(BlockIgniteEvent event) { + List> causes; + + // Find the cause + if (event.getPlayer() != null) { + causes = create(event.getPlayer()); + } else if (event.getIgnitingEntity() != null) { + causes = create(event.getIgnitingEntity()); + } else if (event.getIgnitingBlock() != null) { + causes = create(event.getIgnitingBlock()); + } else { + causes = Collections.emptyList(); + } + + Events.fireToCancel(event, new BreakBlockEvent(event, causes, event.getBlock())); + + // This is also handled in the PlayerInteractEvent listener + if (event.getCause() == IgniteCause.FLINT_AND_STEEL || event.getCause() == IgniteCause.FIREBALL) { + // TODO: Test location of block + Events.fireToCancel(event, new PlaceBlockEvent(event, causes, event.getBlock().getLocation(), Material.FIRE)); + } + } + + @EventHandler + public void onSignChange(SignChangeEvent event) { + Events.fireToCancel(event, new UseBlockEvent(event, create(event.getPlayer()), event.getBlock())); + } + + @EventHandler + public void onBedEnter(PlayerBedEnterEvent event) { + Events.fireToCancel(event, new UseBlockEvent(event, create(event.getPlayer()), event.getBed())); + } + + @EventHandler + public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) { + Player player = event.getPlayer(); + Block blockAffected = event.getBlockClicked().getRelative(event.getBlockFace()); + + // Milk buckets can't be emptied as of writing + if (event.getBucket() != Material.MILK_BUCKET) { + ItemStack item = new ItemStack(event.getBucket(), 1); + Events.fireToCancel(event, new PlaceBlockEvent(event, create(player), blockAffected)); + Events.fireToCancel(event, new UseItemEvent(event, create(player), player.getWorld(), item)); + } + } + + @EventHandler + public void onPlayerBucketFill(PlayerBucketFillEvent event) { + Player player = event.getPlayer(); + Block blockAffected = event.getBlockClicked().getRelative(event.getBlockFace()); + + // Milk buckets can't be filled by right clicking the ground as of writing + if (event.getBucket() != Material.MILK_BUCKET) { + ItemStack item = new ItemStack(event.getBucket(), 1); + Events.fireToCancel(event, new BreakBlockEvent(event, create(player), blockAffected)); + Events.fireToCancel(event, new UseItemEvent(event, create(player), player.getWorld(), item)); + } + } + + // TODO: Handle EntityPortalEnterEvent + + //------------------------------------------------------------------------- + // Block self-interaction + //------------------------------------------------------------------------- + + @EventHandler + public void onBlockFromTo(BlockFromToEvent event) { + Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getBlock()), event.getToBlock())); + } + + //------------------------------------------------------------------------- + // Entity break / place + //------------------------------------------------------------------------- + + @EventHandler + public void onHangingPlace(HangingPlaceEvent event) { + Events.fireToCancel(event, new SpawnEntityEvent(event, create(event.getPlayer()), event.getEntity())); + } + + @EventHandler + public void onHangingBreak(HangingBreakEvent event) { + if (event instanceof HangingBreakByEntityEvent) { + Events.fireToCancel(event, new DestroyEntityEvent(event, create(((HangingBreakByEntityEvent) event).getRemover()), event.getEntity())); + } else { + Events.fireToCancel(event, new DestroyEntityEvent(event, Collections.>emptyList(), event.getEntity())); + } + } + + @EventHandler + public void onVehicleDestroy(VehicleDestroyEvent event) { + Events.fireToCancel(event, new DestroyEntityEvent(event, create(event.getAttacker()), event.getVehicle())); + } + + //------------------------------------------------------------------------- + // Entity external interaction + //------------------------------------------------------------------------- + + @EventHandler + public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { + Player player = event.getPlayer(); + World world = player.getWorld(); + ItemStack item = player.getItemInHand(); + Entity entity = event.getRightClicked(); + + Events.fireToCancel(event, new UseItemEvent(event, create(player), world, item)); + Events.fireToCancel(event, new UseEntityEvent(event, create(player), entity)); + } + + @EventHandler + public void onEntityDamage(EntityDamageEvent event) { + if (event instanceof EntityDamageByBlockEvent) { + Events.fireToCancel(event, new UseEntityEvent(event, create(((EntityDamageByBlockEvent) event).getDamager()), event.getEntity())); + + } else if (event instanceof EntityDamageByEntityEvent) { + EntityDamageByEntityEvent entityEvent = (EntityDamageByEntityEvent) event; + Entity damager = entityEvent.getDamager(); + Events.fireToCancel(event, new UseEntityEvent(event, create(damager), event.getEntity())); + + // Item use event with the item in hand + // Older blacklist handler code used this, although it suffers from + // race problems + if (damager instanceof Player) { + ItemStack item = ((Player) damager).getItemInHand(); + + if (item != null) { + Events.fireToCancel(event, new UseItemEvent(event, create(damager), event.getEntity().getWorld(), item)); + } + } + + } else { + Events.fireToCancel(event, new UseEntityEvent(event, Collections.>emptyList(), event.getEntity())); + } + } + + @EventHandler + public void onEntityCombust(EntityCombustEvent event) { + if (event instanceof EntityCombustByBlockEvent) { + Events.fireToCancel(event, new UseEntityEvent(event, create(((EntityCombustByBlockEvent) event).getCombuster()), event.getEntity())); + + } else if (event instanceof EntityCombustByEntityEvent) { + Events.fireToCancel(event, new UseEntityEvent(event, create(((EntityCombustByEntityEvent) event).getCombuster()), event.getEntity())); + + } else { + Events.fireToCancel(event, new UseEntityEvent(event, Collections.>emptyList(), event.getEntity())); + } + } + + @EventHandler + public void onEntityUnleash(EntityUnleashEvent event) { + if (event instanceof PlayerUnleashEntityEvent) { + PlayerUnleashEntityEvent playerEvent = (PlayerUnleashEntityEvent) event; + Events.fireToCancel(playerEvent, new UseEntityEvent(playerEvent, create(playerEvent.getPlayer()), event.getEntity())); + } else { + // TODO: Raise anyway? + } + } + + @EventHandler + public void onEntityTame(EntityTameEvent event) { + Events.fireToCancel(event, new UseEntityEvent(event, create(event.getOwner()), event.getEntity())); + } + + @EventHandler + public void onPlayerShearEntity(PlayerShearEntityEvent event) { + Events.fireToCancel(event, new UseEntityEvent(event, create(event.getPlayer()), event.getEntity())); + } + + @EventHandler + public void onPlayerPickupItem(PlayerPickupItemEvent event) { + Events.fireToCancel(event, new DestroyEntityEvent(event, create(event.getPlayer()), event.getItem())); + } + + @EventHandler + public void onPlayerDropItem(PlayerDropItemEvent event) { + Events.fireToCancel(event, new SpawnEntityEvent(event, create(event.getPlayer()), event.getItemDrop())); + } + + @EventHandler + public void onVehicleDamage(VehicleDamageEvent event) { + Events.fireToCancel(event, new DestroyEntityEvent(event, create(event.getAttacker()), event.getVehicle())); + } + + @EventHandler + public void onVehicleEnter(VehicleDamageEvent event) { + Events.fireToCancel(event, new UseEntityEvent(event, create(event.getAttacker()), event.getVehicle())); + } + + //------------------------------------------------------------------------- + // Composite events + //------------------------------------------------------------------------- + + @EventHandler + public void onPotionSplash(PotionSplashEvent event) { + Entity entity = event.getEntity(); + ThrownPotion potion = event.getPotion(); + World world = entity.getWorld(); + List> causes = Causes.create(potion.getShooter()); + + // Fire item interaction event + Events.fireToCancel(event, new UseItemEvent(event, causes, world, potion.getItem())); + + // Fire entity interaction event + if (!event.isCancelled()) { + int blocked = 0; + + for (LivingEntity affected : event.getAffectedEntities()) { + if (Events.fireAndTestCancel(new UseEntityEvent(event, causes, affected))) { + event.setIntensity(affected, 0); + blocked++; + } + } + + if (blocked == event.getAffectedEntities().size()) { + event.setCancelled(true); + } + } + } + + @EventHandler + public void onBlockDispense(BlockDispenseEvent event) { + Events.fireToCancel(event, new UseItemEvent(event, create(event.getBlock()), event.getBlock().getWorld(), event.getItem())); + } + + // TODO: Inventory events? + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/event/Interaction.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Blocks.java similarity index 51% rename from src/main/java/com/sk89q/worldguard/internal/event/Interaction.java rename to src/main/java/com/sk89q/worldguard/bukkit/util/Blocks.java index 583eb4b6..7620fdcf 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/Interaction.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Blocks.java @@ -17,15 +17,34 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event; +package com.sk89q.worldguard.bukkit.util; -/** - * Represents a possible act upon an object. - */ -public enum Interaction { +import org.bukkit.block.Block; +import org.bukkit.material.Bed; +import org.bukkit.material.MaterialData; - PLACE, - BREAK, - INTERACT +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public final class Blocks { + + private Blocks() { + } + + public static List getConnected(Block block) { + MaterialData data = block.getState().getData(); + + if (data instanceof Bed) { + Bed bed = (Bed) data; + if (bed.isHeadOfBed()) { + return Arrays.asList(block.getRelative(bed.getFacing().getOppositeFace())); + } else { + return Arrays.asList(block.getRelative(bed.getFacing())); + } + } else { + return Collections.emptyList(); + } + } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/DamageCauses.java b/src/main/java/com/sk89q/worldguard/bukkit/util/DamageCauses.java new file mode 100644 index 00000000..206be5b0 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/DamageCauses.java @@ -0,0 +1,54 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.util; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; + +public final class DamageCauses { + + private DamageCauses() { + } + + public static boolean isFire(DamageCause cause) { + return cause == DamageCause.FIRE || cause == DamageCause.FIRE_TICK; + } + + public static boolean isExplosion(DamageCause cause) { + return cause == DamageCause.BLOCK_EXPLOSION || cause == DamageCause.ENTITY_EXPLOSION; + } + + public static void restoreStatistic(Entity entity, DamageCause cause) { + if (cause == DamageCause.DROWNING && entity instanceof LivingEntity) { + LivingEntity living = (LivingEntity) entity; + living.setRemainingAir(living.getMaximumAir()); + } + + if (isFire(cause)) { + entity.setFireTicks(0); + } + + if (cause == DamageCause.LAVA) { + entity.setFireTicks(0); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java new file mode 100644 index 00000000..765b9f0e --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java @@ -0,0 +1,114 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.util; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Tameable; +import org.bukkit.projectiles.ProjectileSource; + +import javax.annotation.Nullable; + +public final class Entities { + + private Entities() { + } + + /** + * Test whether the given entity is tameable and tamed. + * + * @param entity the entity, or null + * @return true if tamed + */ + public static boolean isTamed(@Nullable Entity entity) { + return entity instanceof Tameable && ((Tameable) entity).isTamed(); + } + + /** + * Return if the given entity type is TNT-based. + * + * @param type the type + * @return true if TNT based + */ + public static boolean isTNTBased(EntityType type) { + return type == EntityType.PRIMED_TNT || type == EntityType.MINECART_TNT; + } + + /** + * Return if the given entity type is a fireball + * (not including wither skulls). + * + * @param type the type + * @return true if a fireball + */ + public static boolean isFireball(EntityType type) { + return type == EntityType.FIREBALL || type == EntityType.SMALL_FIREBALL; + } + + /** + * Test whether the given entity type is a vehicle type. + * + * @param type the type + * @return true if the type is a vehicle type + */ + public static boolean isVehicle(EntityType type) { + return type == EntityType.BOAT + || isMinecart(type); + } + + /** + * Test whether the given entity type is a Minecart type. + * + * @param type the type + * @return true if the type is a Minecart type + */ + public static boolean isMinecart(EntityType type) { + return type == EntityType.MINECART + || type == EntityType.MINECART_CHEST + || type == EntityType.MINECART_COMMAND + || type == EntityType.MINECART_FURNACE + || type == EntityType.MINECART_HOPPER + || type == EntityType.MINECART_MOB_SPAWNER + || type == EntityType.MINECART_TNT; + } + + /** + * Get the underlying shooter of a projectile if one exists. + * + * @param entity the entity + * @return the shooter + */ + public static Entity getShooter(Entity entity) { + + while (entity instanceof Projectile) { + Projectile projectile = (Projectile) entity; + ProjectileSource remover = projectile.getShooter(); + if (remover instanceof Entity && remover != entity) { + entity = (Entity) remover; + } else { + return entity; + } + } + + return entity; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/Materials.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Materials.java new file mode 100644 index 00000000..317b5a5c --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Materials.java @@ -0,0 +1,563 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.util; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import org.bukkit.DyeColor; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.material.Dye; +import org.bukkit.material.MaterialData; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; + +/** + * Material utility class. + */ +public final class Materials { + + private static final int MODIFED_ON_CLICK = 1; + private static final int MODIFIES_BLOCKS = 1; + + private static final BiMap ENTITY_ITEMS = HashBiMap.create(); + private static final Map MATERIAL_FLAGS = new HashMap(); + + static { + ENTITY_ITEMS.put(EntityType.PAINTING, Material.PAINTING); + ENTITY_ITEMS.put(EntityType.ARROW, Material.ARROW); + ENTITY_ITEMS.put(EntityType.SNOWBALL, Material.SNOW_BALL); + ENTITY_ITEMS.put(EntityType.FIREBALL, Material.FIREBALL); + ENTITY_ITEMS.put(EntityType.SMALL_FIREBALL, Material.FIREWORK_CHARGE); + ENTITY_ITEMS.put(EntityType.ENDER_PEARL, Material.ENDER_PEARL); + ENTITY_ITEMS.put(EntityType.THROWN_EXP_BOTTLE, Material.EXP_BOTTLE); + ENTITY_ITEMS.put(EntityType.ITEM_FRAME, Material.ITEM_FRAME); + ENTITY_ITEMS.put(EntityType.PRIMED_TNT, Material.TNT); + ENTITY_ITEMS.put(EntityType.FIREWORK, Material.FIREWORK); + ENTITY_ITEMS.put(EntityType.MINECART_COMMAND, Material.COMMAND_MINECART); + ENTITY_ITEMS.put(EntityType.BOAT, Material.BOAT); + ENTITY_ITEMS.put(EntityType.MINECART, Material.MINECART); + ENTITY_ITEMS.put(EntityType.MINECART_CHEST, Material.STORAGE_MINECART); + ENTITY_ITEMS.put(EntityType.MINECART_FURNACE, Material.POWERED_MINECART); + ENTITY_ITEMS.put(EntityType.MINECART_TNT, Material.EXPLOSIVE_MINECART); + ENTITY_ITEMS.put(EntityType.MINECART_HOPPER, Material.HOPPER_MINECART); + ENTITY_ITEMS.put(EntityType.SPLASH_POTION, Material.POTION); + ENTITY_ITEMS.put(EntityType.EGG, Material.EGG); + + MATERIAL_FLAGS.put(Material.AIR, 0); + MATERIAL_FLAGS.put(Material.STONE, 0); + MATERIAL_FLAGS.put(Material.GRASS, 0); + MATERIAL_FLAGS.put(Material.DIRT, 0); + MATERIAL_FLAGS.put(Material.COBBLESTONE, 0); + MATERIAL_FLAGS.put(Material.WOOD, 0); + MATERIAL_FLAGS.put(Material.SAPLING, 0); + MATERIAL_FLAGS.put(Material.BEDROCK, 0); + MATERIAL_FLAGS.put(Material.WATER, 0); + MATERIAL_FLAGS.put(Material.STATIONARY_WATER, 0); + MATERIAL_FLAGS.put(Material.LAVA, 0); + MATERIAL_FLAGS.put(Material.STATIONARY_LAVA, 0); + MATERIAL_FLAGS.put(Material.SAND, 0); + MATERIAL_FLAGS.put(Material.GRAVEL, 0); + MATERIAL_FLAGS.put(Material.GOLD_ORE, 0); + MATERIAL_FLAGS.put(Material.IRON_ORE, 0); + MATERIAL_FLAGS.put(Material.COAL_ORE, 0); + MATERIAL_FLAGS.put(Material.LOG, 0); + MATERIAL_FLAGS.put(Material.LEAVES, 0); + MATERIAL_FLAGS.put(Material.SPONGE, 0); + MATERIAL_FLAGS.put(Material.GLASS, 0); + MATERIAL_FLAGS.put(Material.LAPIS_ORE, 0); + MATERIAL_FLAGS.put(Material.LAPIS_BLOCK, 0); + MATERIAL_FLAGS.put(Material.DISPENSER, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.SANDSTONE, 0); + MATERIAL_FLAGS.put(Material.NOTE_BLOCK, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.BED_BLOCK, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.POWERED_RAIL, 0); + MATERIAL_FLAGS.put(Material.DETECTOR_RAIL, 0); + MATERIAL_FLAGS.put(Material.PISTON_STICKY_BASE, 0); + MATERIAL_FLAGS.put(Material.WEB, 0); + MATERIAL_FLAGS.put(Material.LONG_GRASS, 0); + MATERIAL_FLAGS.put(Material.DEAD_BUSH, 0); + MATERIAL_FLAGS.put(Material.PISTON_BASE, 0); + MATERIAL_FLAGS.put(Material.PISTON_EXTENSION, 0); + MATERIAL_FLAGS.put(Material.WOOL, 0); + MATERIAL_FLAGS.put(Material.PISTON_MOVING_PIECE, 0); + MATERIAL_FLAGS.put(Material.YELLOW_FLOWER, 0); + MATERIAL_FLAGS.put(Material.RED_ROSE, 0); + MATERIAL_FLAGS.put(Material.BROWN_MUSHROOM, 0); + MATERIAL_FLAGS.put(Material.RED_MUSHROOM, 0); + MATERIAL_FLAGS.put(Material.GOLD_BLOCK, 0); + MATERIAL_FLAGS.put(Material.IRON_BLOCK, 0); + MATERIAL_FLAGS.put(Material.DOUBLE_STEP, 0); + MATERIAL_FLAGS.put(Material.STEP, 0); + MATERIAL_FLAGS.put(Material.BRICK, 0); + MATERIAL_FLAGS.put(Material.TNT, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.BOOKSHELF, 0); + MATERIAL_FLAGS.put(Material.MOSSY_COBBLESTONE, 0); + MATERIAL_FLAGS.put(Material.OBSIDIAN, 0); + MATERIAL_FLAGS.put(Material.TORCH, 0); + MATERIAL_FLAGS.put(Material.FIRE, 0); + MATERIAL_FLAGS.put(Material.MOB_SPAWNER, 0); + MATERIAL_FLAGS.put(Material.WOOD_STAIRS, 0); + MATERIAL_FLAGS.put(Material.CHEST, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.REDSTONE_WIRE, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_ORE, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_BLOCK, 0); + MATERIAL_FLAGS.put(Material.WORKBENCH, 0); + MATERIAL_FLAGS.put(Material.CROPS, 0); + MATERIAL_FLAGS.put(Material.SOIL, 0); + MATERIAL_FLAGS.put(Material.FURNACE, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.BURNING_FURNACE, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.SIGN_POST, 0); + MATERIAL_FLAGS.put(Material.WOODEN_DOOR, 0); + MATERIAL_FLAGS.put(Material.LADDER, 0); + MATERIAL_FLAGS.put(Material.RAILS, 0); + MATERIAL_FLAGS.put(Material.COBBLESTONE_STAIRS, 0); + MATERIAL_FLAGS.put(Material.WALL_SIGN, 0); + MATERIAL_FLAGS.put(Material.LEVER, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.STONE_PLATE, 0); + MATERIAL_FLAGS.put(Material.IRON_DOOR_BLOCK, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.WOOD_PLATE, 0); + MATERIAL_FLAGS.put(Material.REDSTONE_ORE, 0); + MATERIAL_FLAGS.put(Material.GLOWING_REDSTONE_ORE, 0); + MATERIAL_FLAGS.put(Material.REDSTONE_TORCH_OFF, 0); + MATERIAL_FLAGS.put(Material.REDSTONE_TORCH_ON, 0); + MATERIAL_FLAGS.put(Material.STONE_BUTTON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.SNOW, 0); + MATERIAL_FLAGS.put(Material.ICE, 0); + MATERIAL_FLAGS.put(Material.SNOW_BLOCK, 0); + MATERIAL_FLAGS.put(Material.CACTUS, 0); + MATERIAL_FLAGS.put(Material.CLAY, 0); + MATERIAL_FLAGS.put(Material.SUGAR_CANE_BLOCK, 0); + MATERIAL_FLAGS.put(Material.JUKEBOX, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.FENCE, 0); + MATERIAL_FLAGS.put(Material.PUMPKIN, 0); + MATERIAL_FLAGS.put(Material.NETHERRACK, 0); + MATERIAL_FLAGS.put(Material.SOUL_SAND, 0); + MATERIAL_FLAGS.put(Material.GLOWSTONE, 0); + MATERIAL_FLAGS.put(Material.PORTAL, 0); + MATERIAL_FLAGS.put(Material.JACK_O_LANTERN, 0); + MATERIAL_FLAGS.put(Material.CAKE_BLOCK, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.DIODE_BLOCK_OFF, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.DIODE_BLOCK_ON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.STAINED_GLASS, 0); + MATERIAL_FLAGS.put(Material.TRAP_DOOR, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.MONSTER_EGGS, 0); + MATERIAL_FLAGS.put(Material.SMOOTH_BRICK, 0); + MATERIAL_FLAGS.put(Material.HUGE_MUSHROOM_1, 0); + MATERIAL_FLAGS.put(Material.HUGE_MUSHROOM_2, 0); + MATERIAL_FLAGS.put(Material.IRON_FENCE, 0); + MATERIAL_FLAGS.put(Material.THIN_GLASS, 0); + MATERIAL_FLAGS.put(Material.MELON_BLOCK, 0); + MATERIAL_FLAGS.put(Material.PUMPKIN_STEM, 0); + MATERIAL_FLAGS.put(Material.MELON_STEM, 0); + MATERIAL_FLAGS.put(Material.VINE, 0); + MATERIAL_FLAGS.put(Material.FENCE_GATE, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.BRICK_STAIRS, 0); + MATERIAL_FLAGS.put(Material.SMOOTH_STAIRS, 0); + MATERIAL_FLAGS.put(Material.MYCEL, 0); + MATERIAL_FLAGS.put(Material.WATER_LILY, 0); + MATERIAL_FLAGS.put(Material.NETHER_BRICK, 0); + MATERIAL_FLAGS.put(Material.NETHER_FENCE, 0); + MATERIAL_FLAGS.put(Material.NETHER_BRICK_STAIRS, 0); + MATERIAL_FLAGS.put(Material.NETHER_WARTS, 0); + MATERIAL_FLAGS.put(Material.ENCHANTMENT_TABLE, 0); + MATERIAL_FLAGS.put(Material.BREWING_STAND, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.CAULDRON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.ENDER_PORTAL, 0); + MATERIAL_FLAGS.put(Material.ENDER_PORTAL_FRAME, 0); + MATERIAL_FLAGS.put(Material.ENDER_STONE, 0); + MATERIAL_FLAGS.put(Material.DRAGON_EGG, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.REDSTONE_LAMP_OFF, 0); + MATERIAL_FLAGS.put(Material.REDSTONE_LAMP_ON, 0); + MATERIAL_FLAGS.put(Material.WOOD_DOUBLE_STEP, 0); + MATERIAL_FLAGS.put(Material.WOOD_STEP, 0); + MATERIAL_FLAGS.put(Material.COCOA, 0); + MATERIAL_FLAGS.put(Material.SANDSTONE_STAIRS, 0); + MATERIAL_FLAGS.put(Material.EMERALD_ORE, 0); + MATERIAL_FLAGS.put(Material.ENDER_CHEST, 0); + MATERIAL_FLAGS.put(Material.TRIPWIRE_HOOK, 0); + MATERIAL_FLAGS.put(Material.TRIPWIRE, 0); + MATERIAL_FLAGS.put(Material.EMERALD_BLOCK, 0); + MATERIAL_FLAGS.put(Material.SPRUCE_WOOD_STAIRS, 0); + MATERIAL_FLAGS.put(Material.BIRCH_WOOD_STAIRS, 0); + MATERIAL_FLAGS.put(Material.JUNGLE_WOOD_STAIRS, 0); + MATERIAL_FLAGS.put(Material.COMMAND, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.BEACON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.COBBLE_WALL, 0); + MATERIAL_FLAGS.put(Material.FLOWER_POT, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.CARROT, 0); + MATERIAL_FLAGS.put(Material.POTATO, 0); + MATERIAL_FLAGS.put(Material.WOOD_BUTTON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.SKULL, 0); + MATERIAL_FLAGS.put(Material.ANVIL, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.TRAPPED_CHEST, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.GOLD_PLATE, 0); + MATERIAL_FLAGS.put(Material.IRON_PLATE, 0); + MATERIAL_FLAGS.put(Material.REDSTONE_COMPARATOR_OFF, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.REDSTONE_COMPARATOR_ON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.DAYLIGHT_DETECTOR, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.REDSTONE_BLOCK, 0); + MATERIAL_FLAGS.put(Material.QUARTZ_ORE, 0); + MATERIAL_FLAGS.put(Material.HOPPER, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.QUARTZ_BLOCK, 0); + MATERIAL_FLAGS.put(Material.QUARTZ_STAIRS, 0); + MATERIAL_FLAGS.put(Material.ACTIVATOR_RAIL, 0); + MATERIAL_FLAGS.put(Material.DROPPER, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.STAINED_CLAY, 0); + MATERIAL_FLAGS.put(Material.STAINED_GLASS_PANE, 0); + MATERIAL_FLAGS.put(Material.LEAVES_2, 0); + MATERIAL_FLAGS.put(Material.LOG_2, 0); + MATERIAL_FLAGS.put(Material.ACACIA_STAIRS, 0); + MATERIAL_FLAGS.put(Material.DARK_OAK_STAIRS, 0); + MATERIAL_FLAGS.put(Material.HAY_BLOCK, 0); + MATERIAL_FLAGS.put(Material.CARPET, 0); + MATERIAL_FLAGS.put(Material.HARD_CLAY, 0); + MATERIAL_FLAGS.put(Material.COAL_BLOCK, 0); + MATERIAL_FLAGS.put(Material.PACKED_ICE, 0); + MATERIAL_FLAGS.put(Material.DOUBLE_PLANT, 0); + + MATERIAL_FLAGS.put(Material.IRON_SPADE, 0); + MATERIAL_FLAGS.put(Material.IRON_PICKAXE, 0); + MATERIAL_FLAGS.put(Material.IRON_AXE, 0); + MATERIAL_FLAGS.put(Material.FLINT_AND_STEEL, 0); + MATERIAL_FLAGS.put(Material.APPLE, 0); + MATERIAL_FLAGS.put(Material.BOW, 0); + MATERIAL_FLAGS.put(Material.ARROW, 0); + MATERIAL_FLAGS.put(Material.COAL, 0); + MATERIAL_FLAGS.put(Material.DIAMOND, 0); + MATERIAL_FLAGS.put(Material.IRON_INGOT, 0); + MATERIAL_FLAGS.put(Material.GOLD_INGOT, 0); + MATERIAL_FLAGS.put(Material.IRON_SWORD, 0); + MATERIAL_FLAGS.put(Material.WOOD_SWORD, 0); + MATERIAL_FLAGS.put(Material.WOOD_SPADE, 0); + MATERIAL_FLAGS.put(Material.WOOD_PICKAXE, 0); + MATERIAL_FLAGS.put(Material.WOOD_AXE, 0); + MATERIAL_FLAGS.put(Material.STONE_SWORD, 0); + MATERIAL_FLAGS.put(Material.STONE_SPADE, 0); + MATERIAL_FLAGS.put(Material.STONE_PICKAXE, 0); + MATERIAL_FLAGS.put(Material.STONE_AXE, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_SWORD, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_SPADE, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_PICKAXE, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_AXE, 0); + MATERIAL_FLAGS.put(Material.STICK, 0); + MATERIAL_FLAGS.put(Material.BOWL, 0); + MATERIAL_FLAGS.put(Material.MUSHROOM_SOUP, 0); + MATERIAL_FLAGS.put(Material.GOLD_SWORD, 0); + MATERIAL_FLAGS.put(Material.GOLD_SPADE, 0); + MATERIAL_FLAGS.put(Material.GOLD_PICKAXE, 0); + MATERIAL_FLAGS.put(Material.GOLD_AXE, 0); + MATERIAL_FLAGS.put(Material.STRING, 0); + MATERIAL_FLAGS.put(Material.FEATHER, 0); + MATERIAL_FLAGS.put(Material.SULPHUR, 0); + MATERIAL_FLAGS.put(Material.WOOD_HOE, MODIFIES_BLOCKS); + MATERIAL_FLAGS.put(Material.STONE_HOE, MODIFIES_BLOCKS); + MATERIAL_FLAGS.put(Material.IRON_HOE, MODIFIES_BLOCKS); + MATERIAL_FLAGS.put(Material.DIAMOND_HOE, MODIFIES_BLOCKS); + MATERIAL_FLAGS.put(Material.GOLD_HOE, MODIFIES_BLOCKS); + MATERIAL_FLAGS.put(Material.SEEDS, MODIFIES_BLOCKS); + MATERIAL_FLAGS.put(Material.WHEAT, 0); + MATERIAL_FLAGS.put(Material.BREAD, 0); + MATERIAL_FLAGS.put(Material.LEATHER_HELMET, 0); + MATERIAL_FLAGS.put(Material.LEATHER_CHESTPLATE, 0); + MATERIAL_FLAGS.put(Material.LEATHER_LEGGINGS, 0); + MATERIAL_FLAGS.put(Material.LEATHER_BOOTS, 0); + MATERIAL_FLAGS.put(Material.CHAINMAIL_HELMET, 0); + MATERIAL_FLAGS.put(Material.CHAINMAIL_CHESTPLATE, 0); + MATERIAL_FLAGS.put(Material.CHAINMAIL_LEGGINGS, 0); + MATERIAL_FLAGS.put(Material.CHAINMAIL_BOOTS, 0); + MATERIAL_FLAGS.put(Material.IRON_HELMET, 0); + MATERIAL_FLAGS.put(Material.IRON_CHESTPLATE, 0); + MATERIAL_FLAGS.put(Material.IRON_LEGGINGS, 0); + MATERIAL_FLAGS.put(Material.IRON_BOOTS, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_HELMET, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_CHESTPLATE, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_LEGGINGS, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_BOOTS, 0); + MATERIAL_FLAGS.put(Material.GOLD_HELMET, 0); + MATERIAL_FLAGS.put(Material.GOLD_CHESTPLATE, 0); + MATERIAL_FLAGS.put(Material.GOLD_LEGGINGS, 0); + MATERIAL_FLAGS.put(Material.GOLD_BOOTS, 0); + MATERIAL_FLAGS.put(Material.FLINT, 0); + MATERIAL_FLAGS.put(Material.PORK, 0); + MATERIAL_FLAGS.put(Material.GRILLED_PORK, 0); + MATERIAL_FLAGS.put(Material.PAINTING, 0); + MATERIAL_FLAGS.put(Material.GOLDEN_APPLE, 0); + MATERIAL_FLAGS.put(Material.SIGN, 0); + MATERIAL_FLAGS.put(Material.WOOD_DOOR, 0); + MATERIAL_FLAGS.put(Material.BUCKET, 0); + MATERIAL_FLAGS.put(Material.WATER_BUCKET, 0); + MATERIAL_FLAGS.put(Material.LAVA_BUCKET, 0); + MATERIAL_FLAGS.put(Material.MINECART, 0); + MATERIAL_FLAGS.put(Material.SADDLE, 0); + MATERIAL_FLAGS.put(Material.IRON_DOOR, 0); + MATERIAL_FLAGS.put(Material.REDSTONE, 0); + MATERIAL_FLAGS.put(Material.SNOW_BALL, 0); + MATERIAL_FLAGS.put(Material.BOAT, 0); + MATERIAL_FLAGS.put(Material.LEATHER, 0); + MATERIAL_FLAGS.put(Material.MILK_BUCKET, 0); + MATERIAL_FLAGS.put(Material.CLAY_BRICK, 0); + MATERIAL_FLAGS.put(Material.CLAY_BALL, 0); + MATERIAL_FLAGS.put(Material.SUGAR_CANE, 0); + MATERIAL_FLAGS.put(Material.PAPER, 0); + MATERIAL_FLAGS.put(Material.BOOK, 0); + MATERIAL_FLAGS.put(Material.SLIME_BALL, 0); + MATERIAL_FLAGS.put(Material.STORAGE_MINECART, 0); + MATERIAL_FLAGS.put(Material.POWERED_MINECART, 0); + MATERIAL_FLAGS.put(Material.EGG, 0); + MATERIAL_FLAGS.put(Material.COMPASS, 0); + MATERIAL_FLAGS.put(Material.FISHING_ROD, 0); + MATERIAL_FLAGS.put(Material.WATCH, 0); + MATERIAL_FLAGS.put(Material.GLOWSTONE_DUST, 0); + MATERIAL_FLAGS.put(Material.RAW_FISH, 0); + MATERIAL_FLAGS.put(Material.COOKED_FISH, 0); + MATERIAL_FLAGS.put(Material.INK_SACK, 0); + MATERIAL_FLAGS.put(Material.BONE, 0); + MATERIAL_FLAGS.put(Material.SUGAR, 0); + MATERIAL_FLAGS.put(Material.CAKE, 0); + MATERIAL_FLAGS.put(Material.BED, 0); + MATERIAL_FLAGS.put(Material.DIODE, 0); + MATERIAL_FLAGS.put(Material.COOKIE, 0); + MATERIAL_FLAGS.put(Material.MAP, 0); + MATERIAL_FLAGS.put(Material.SHEARS, MODIFIES_BLOCKS); + MATERIAL_FLAGS.put(Material.MELON, 0); + MATERIAL_FLAGS.put(Material.PUMPKIN_SEEDS, 0); + MATERIAL_FLAGS.put(Material.MELON_SEEDS, 0); + MATERIAL_FLAGS.put(Material.RAW_BEEF, 0); + MATERIAL_FLAGS.put(Material.COOKED_BEEF, 0); + MATERIAL_FLAGS.put(Material.RAW_CHICKEN, 0); + MATERIAL_FLAGS.put(Material.COOKED_CHICKEN, 0); + MATERIAL_FLAGS.put(Material.ROTTEN_FLESH, 0); + MATERIAL_FLAGS.put(Material.ENDER_PEARL, 0); + MATERIAL_FLAGS.put(Material.BLAZE_ROD, 0); + MATERIAL_FLAGS.put(Material.GHAST_TEAR, 0); + MATERIAL_FLAGS.put(Material.GOLD_NUGGET, 0); + MATERIAL_FLAGS.put(Material.NETHER_STALK, 0); + MATERIAL_FLAGS.put(Material.POTION, 0); + MATERIAL_FLAGS.put(Material.GLASS_BOTTLE, 0); + MATERIAL_FLAGS.put(Material.SPIDER_EYE, 0); + MATERIAL_FLAGS.put(Material.FERMENTED_SPIDER_EYE, 0); + MATERIAL_FLAGS.put(Material.BLAZE_POWDER, 0); + MATERIAL_FLAGS.put(Material.MAGMA_CREAM, 0); + MATERIAL_FLAGS.put(Material.BREWING_STAND_ITEM, 0); + MATERIAL_FLAGS.put(Material.CAULDRON_ITEM, 0); + MATERIAL_FLAGS.put(Material.EYE_OF_ENDER, 0); + MATERIAL_FLAGS.put(Material.SPECKLED_MELON, 0); + MATERIAL_FLAGS.put(Material.MONSTER_EGG, 0); + MATERIAL_FLAGS.put(Material.EXP_BOTTLE, 0); + MATERIAL_FLAGS.put(Material.FIREBALL, 0); + MATERIAL_FLAGS.put(Material.BOOK_AND_QUILL, 0); + MATERIAL_FLAGS.put(Material.WRITTEN_BOOK, 0); + MATERIAL_FLAGS.put(Material.EMERALD, 0); + MATERIAL_FLAGS.put(Material.ITEM_FRAME, 0); + MATERIAL_FLAGS.put(Material.FLOWER_POT_ITEM, 0); + MATERIAL_FLAGS.put(Material.CARROT_ITEM, 0); + MATERIAL_FLAGS.put(Material.POTATO_ITEM, 0); + MATERIAL_FLAGS.put(Material.BAKED_POTATO, 0); + MATERIAL_FLAGS.put(Material.POISONOUS_POTATO, 0); + MATERIAL_FLAGS.put(Material.EMPTY_MAP, 0); + MATERIAL_FLAGS.put(Material.GOLDEN_CARROT, 0); + MATERIAL_FLAGS.put(Material.SKULL_ITEM, 0); + MATERIAL_FLAGS.put(Material.CARROT_STICK, 0); + MATERIAL_FLAGS.put(Material.NETHER_STAR, 0); + MATERIAL_FLAGS.put(Material.PUMPKIN_PIE, 0); + MATERIAL_FLAGS.put(Material.FIREWORK, 0); + MATERIAL_FLAGS.put(Material.FIREWORK_CHARGE, 0); + MATERIAL_FLAGS.put(Material.ENCHANTED_BOOK, 0); + MATERIAL_FLAGS.put(Material.REDSTONE_COMPARATOR, 0); + MATERIAL_FLAGS.put(Material.NETHER_BRICK_ITEM, 0); + MATERIAL_FLAGS.put(Material.QUARTZ, 0); + MATERIAL_FLAGS.put(Material.EXPLOSIVE_MINECART, 0); + MATERIAL_FLAGS.put(Material.HOPPER_MINECART, 0); + MATERIAL_FLAGS.put(Material.IRON_BARDING, 0); + MATERIAL_FLAGS.put(Material.GOLD_BARDING, 0); + MATERIAL_FLAGS.put(Material.DIAMOND_BARDING, 0); + MATERIAL_FLAGS.put(Material.LEASH, 0); + MATERIAL_FLAGS.put(Material.NAME_TAG, 0); + MATERIAL_FLAGS.put(Material.COMMAND_MINECART, 0); + MATERIAL_FLAGS.put(Material.GOLD_RECORD, 0); + MATERIAL_FLAGS.put(Material.GREEN_RECORD, 0); + MATERIAL_FLAGS.put(Material.RECORD_3, 0); + MATERIAL_FLAGS.put(Material.RECORD_4, 0); + MATERIAL_FLAGS.put(Material.RECORD_5, 0); + MATERIAL_FLAGS.put(Material.RECORD_6, 0); + MATERIAL_FLAGS.put(Material.RECORD_7, 0); + MATERIAL_FLAGS.put(Material.RECORD_8, 0); + MATERIAL_FLAGS.put(Material.RECORD_9, 0); + MATERIAL_FLAGS.put(Material.RECORD_10, 0); + MATERIAL_FLAGS.put(Material.RECORD_11, 0); + MATERIAL_FLAGS.put(Material.RECORD_12, 0); + } + + private Materials() { + } + + /** + * Get the related material for an entity type. + * + * @param type the entity type + * @return the related material or {@code null} if one is not known or exists + */ + @Nullable + public static Material getRelatedMaterial(EntityType type) { + return ENTITY_ITEMS.get(type); + } + + /** + * Test whether the given material is a mushroom. + * + * @param material the material + * @return true if a mushroom block + */ + public static boolean isMushroom(Material material) { + return material == Material.RED_MUSHROOM || material == Material.BROWN_MUSHROOM; + } + + /** + * Test whether the given material is a leaf block. + * + * @param material the material + * @return true if a leaf block + */ + public static boolean isLeaf(Material material) { + return material == Material.LEAVES || material == Material.LEAVES_2; + } + + /** + * Test whether the given material is water. + * + * @param material the material + * @return true if a water block + */ + public static boolean isWater(Material material) { + return material == Material.WATER || material == Material.STATIONARY_WATER; + } + + /** + * Test whether the given material is lava. + * + * @param material the material + * @return true if a lava block + */ + public static boolean isLava(Material material) { + return material == Material.LAVA || material == Material.STATIONARY_LAVA; + } + + /** + * Test whether the given material is a portal material. + * + * @param material the material + * @return true if a portal block + */ + public static boolean isPortal(Material material) { + return material == Material.PORTAL || material == Material.ENDER_PORTAL; + } + + /** + * Test whether the given material data is of the given dye color. + * + *

Returns false for non-dyed items.

+ * + * @param data the data + * @return true if it is the provided dye color + */ + public static boolean isDyeColor(MaterialData data, DyeColor color) { + return data instanceof Dye && ((Dye) data).getColor() == color; + } + + /** + * Test whether the given material is a rail block. + * + * @param material the material + * @return true if a rail block + */ + public static boolean isRailBlock(Material material) { + return material == Material.RAILS + || material == Material.ACTIVATOR_RAIL + || material == Material.DETECTOR_RAIL + || material == Material.POWERED_RAIL; + } + + /** + * Test whether the given material is a Minecart. + * + * @param material the material + * @return true if a Minecart item + */ + public static boolean isMinecart(Material material) { + return material == Material.MINECART + || material == Material.COMMAND_MINECART + || material == Material.EXPLOSIVE_MINECART + || material == Material.HOPPER_MINECART + || material == Material.POWERED_MINECART + || material == Material.STORAGE_MINECART; + } + + /** + * Test whether the given material is an inventory block. + * + * @param material the material + * @return true if an inventory block + */ + public static boolean isInventoryBlock(Material material) { + return material == Material.CHEST + || material == Material.JUKEBOX + || material == Material.DISPENSER + || material == Material.FURNACE + || material == Material.BURNING_FURNACE + || material == Material.BREWING_STAND + || material == Material.TRAPPED_CHEST + || material == Material.HOPPER + || material == Material.DROPPER; + } + + /** + * Test whether the given material is a block that is modified when it is + * left or right clicked. + * + *

This test is conservative, returning true for blocks that it is not + * aware of.

+ * + * @param material the material + * @return true if the block is modified + */ + public static boolean isBlockModifiedOnClick(Material material) { + Integer flags = MATERIAL_FLAGS.get(material); + return flags == null || (flags & MODIFED_ON_CLICK) == MODIFED_ON_CLICK; + } + + /** + * Test whether the given item modifies a given block when right clicked. + * + *

This test is conservative, returning true for blocks that it is not + * aware of or does not have the details for.

+ * + * @param item the item + * @param block the block + * @return true if the item is applied to the block + */ + public static boolean isItemAppliedToBlock(Material item, Material block) { + Integer flags = MATERIAL_FLAGS.get(item); + return flags == null || (flags & MODIFIES_BLOCKS) == MODIFIES_BLOCKS; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java b/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java new file mode 100644 index 00000000..3df7e293 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java @@ -0,0 +1,102 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.util; + +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.bukkit.BukkitUtil; +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.GlobalRegionManager; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; + +import javax.annotation.Nullable; + +import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; + +public class RegionQuery { + + private final WorldGuardPlugin plugin; + private final ConfigurationManager config; + private final GlobalRegionManager globalManager; + @Nullable + private final LocalPlayer localPlayer; + + public RegionQuery(WorldGuardPlugin plugin, @Nullable Player player) { + this.plugin = plugin; + this.config = plugin.getGlobalStateManager(); + this.globalManager = plugin.getGlobalRegionManager(); + this.localPlayer = player != null ? plugin.wrapPlayer(player) : null; + } + + public boolean canBuild(Block block) { + return canBuild(block.getLocation()); + } + + public boolean canBuild(Location location) { + World world = location.getWorld(); + WorldConfiguration worldConfig = config.get(world); + + if (!worldConfig.useRegions) { + return true; + } + + if (globalManager.hasBypass(localPlayer, world)) { + return true; + } else { + RegionManager manager = globalManager.get(location.getWorld()); + return manager.getApplicableRegions(BukkitUtil.toVector(location)).canBuild(localPlayer); + } + } + + public boolean canConstruct(Location location) { + World world = location.getWorld(); + WorldConfiguration worldConfig = config.get(world); + + if (!worldConfig.useRegions) { + return true; + } + + if (globalManager.hasBypass(localPlayer, world)) { + return true; + } else { + RegionManager manager = globalManager.get(location.getWorld()); + return manager.getApplicableRegions(BukkitUtil.toVector(location)).canConstruct(localPlayer); + } + } + + public boolean allows(StateFlag flag, Location location) { + World world = location.getWorld(); + WorldConfiguration worldConfig = config.get(world); + + if (!worldConfig.useRegions) { + return true; + } + + RegionManager manager = globalManager.get(location.getWorld()); + return manager.getApplicableRegions(toVector(location)).allows(flag, localPlayer); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/Events.java b/src/main/java/com/sk89q/worldguard/internal/Events.java index 6e92be49..4394acef 100644 --- a/src/main/java/com/sk89q/worldguard/internal/Events.java +++ b/src/main/java/com/sk89q/worldguard/internal/Events.java @@ -34,8 +34,20 @@ private Events() { } /** - * Fire the {@code eventToFire} if {@code original} has not been cancelled - * and cancel the original if the fired event is cancelled. + * Fire the {@code eventToFire} and return whether the event was cancelled. + * + * @param eventToFire the event to fire + * @param an event that can be fired and is cancellable + * @return true if the event was fired + */ + public static boolean fireAndTestCancel( T eventToFire) { + Bukkit.getServer().getPluginManager().callEvent(eventToFire); + return eventToFire.isCancelled(); + } + + /** + * Fire the {@code eventToFire} and cancel the original if the fired event + * is cancelled. * * @param original the original event to potentially cancel * @param eventToFire the event to fire to consider cancelling the original event @@ -43,20 +55,18 @@ private Events() { * @return true if the event was fired and it caused the original event to be cancelled */ public static boolean fireToCancel(Cancellable original, T eventToFire) { - if (!original.isCancelled()) { - Bukkit.getServer().getPluginManager().callEvent(eventToFire); - if (eventToFire.isCancelled()) { - original.setCancelled(true); - return true; - } + Bukkit.getServer().getPluginManager().callEvent(eventToFire); + if (eventToFire.isCancelled()) { + original.setCancelled(true); + return true; } return false; } /** - * Fire the {@code eventToFire} if {@code original} - * and cancel the original if the fired event is cancelled. + * Fire the {@code eventToFire} and cancel the original if the fired event + * is cancelled. * * @param original the original event to potentially cancel * @param eventToFire the event to fire to consider cancelling the original event diff --git a/src/main/java/com/sk89q/worldguard/internal/cause/Causes.java b/src/main/java/com/sk89q/worldguard/internal/cause/Causes.java index 45b09df2..e549319b 100644 --- a/src/main/java/com/sk89q/worldguard/internal/cause/Causes.java +++ b/src/main/java/com/sk89q/worldguard/internal/cause/Causes.java @@ -22,6 +22,7 @@ import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; import javax.annotation.Nullable; import java.util.ArrayList; @@ -88,6 +89,9 @@ public static List> create(Object ... cause) { causes.add(new PlayerCause((Player) o)); } else if (o instanceof Block) { causes.add(new BlockCause((Block) o)); + } else if (o instanceof Projectile) { + causes.addAll(create(o)); + causes.add(new EntityCause((Entity) o)); } else if (o instanceof Entity) { causes.add(new EntityCause((Entity) o)); } else { diff --git a/src/main/java/com/sk89q/worldguard/internal/event/AbstractInteractEvent.java b/src/main/java/com/sk89q/worldguard/internal/event/AbstractInteractEvent.java index f0142d4f..ed528d80 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/AbstractInteractEvent.java +++ b/src/main/java/com/sk89q/worldguard/internal/event/AbstractInteractEvent.java @@ -28,11 +28,10 @@ import static com.google.common.base.Preconditions.checkNotNull; -abstract class AbstractInteractEvent extends Event implements Cancellable { +public abstract class AbstractInteractEvent extends Event implements Cancellable { private final Event originalEvent; private final List> causes; - private final Interaction interaction; private boolean cancelled; /** @@ -40,15 +39,12 @@ abstract class AbstractInteractEvent extends Event implements Cancellable { * * @param originalEvent the original event * @param causes a list of causes, where the originating causes are at the beginning - * @param interaction the action that is being taken */ - protected AbstractInteractEvent(Event originalEvent, List> causes, Interaction interaction) { + protected AbstractInteractEvent(Event originalEvent, List> causes) { checkNotNull(originalEvent); checkNotNull(causes); - checkNotNull(interaction); this.originalEvent = originalEvent; this.causes = causes; - this.interaction = interaction; } /** @@ -70,15 +66,6 @@ public List> getCauses() { return Collections.unmodifiableList(causes); } - /** - * Get the action that is being taken. - * - * @return the action - */ - public Interaction getInteraction() { - return interaction; - } - @Override public boolean isCancelled() { return cancelled; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/block/AbstractBlockEvent.java b/src/main/java/com/sk89q/worldguard/internal/event/block/AbstractBlockEvent.java new file mode 100644 index 00000000..f856e27b --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/event/block/AbstractBlockEvent.java @@ -0,0 +1,85 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.event.block; + +import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.internal.event.AbstractInteractEvent; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.Event; + +import javax.annotation.Nullable; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +abstract class AbstractBlockEvent extends AbstractInteractEvent { + + private final Location target; + @Nullable + private final Block block; + private final Material effectiveMaterial; + + protected AbstractBlockEvent(Event originalEvent, List> causes, Block block) { + super(originalEvent, causes); + checkNotNull(block); + this.target = block.getLocation(); + this.block = block; + this.effectiveMaterial = block.getType(); + } + + protected AbstractBlockEvent(Event originalEvent, List> causes, Location target, Material effectiveMaterial) { + super(originalEvent, causes); + this.target = target; + this.block = null; + this.effectiveMaterial = effectiveMaterial; + } + + /** + * Get the target block being affected. + * + * @return a block + */ + public Location getTarget() { + return target; + } + + /** + * Get the block. + * + * @return the block + */ + @Nullable + public Block getBlock() { + return block; + } + + /** + * Get the effective material of the block, regardless of what the block + * currently is. + * + * @return the effective material + */ + public Material getEffectiveMaterial() { + return effectiveMaterial; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/event/block/BreakBlockEvent.java b/src/main/java/com/sk89q/worldguard/internal/event/block/BreakBlockEvent.java new file mode 100644 index 00000000..ddd0c667 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/event/block/BreakBlockEvent.java @@ -0,0 +1,52 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.event.block; + +import com.sk89q.worldguard.internal.cause.Cause; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import java.util.List; + +public class BreakBlockEvent extends AbstractBlockEvent { + + private static final HandlerList handlers = new HandlerList(); + + public BreakBlockEvent(Event originalEvent, List> causes, Block block) { + super(originalEvent, causes, block); + } + + public BreakBlockEvent(Event originalEvent, List> causes, Location target, Material effectiveMaterial) { + super(originalEvent, causes, target, effectiveMaterial); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/event/block/PlaceBlockEvent.java b/src/main/java/com/sk89q/worldguard/internal/event/block/PlaceBlockEvent.java new file mode 100644 index 00000000..69874e14 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/event/block/PlaceBlockEvent.java @@ -0,0 +1,52 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.event.block; + +import com.sk89q.worldguard.internal.cause.Cause; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import java.util.List; + +public class PlaceBlockEvent extends AbstractBlockEvent { + + private static final HandlerList handlers = new HandlerList(); + + public PlaceBlockEvent(Event originalEvent, List> causes, Block block) { + super(originalEvent, causes, block); + } + + public PlaceBlockEvent(Event originalEvent, List> causes, Location target, Material effectiveMaterial) { + super(originalEvent, causes, target, effectiveMaterial); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/event/block/UseBlockEvent.java b/src/main/java/com/sk89q/worldguard/internal/event/block/UseBlockEvent.java new file mode 100644 index 00000000..99705dc9 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/event/block/UseBlockEvent.java @@ -0,0 +1,55 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.event.block; + +import com.sk89q.worldguard.internal.cause.Cause; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import java.util.List; + +/** + * Fired when a block is interacted with. + */ +public class UseBlockEvent extends AbstractBlockEvent { + + private static final HandlerList handlers = new HandlerList(); + + public UseBlockEvent(Event originalEvent, List> causes, Block block) { + super(originalEvent, causes, block); + } + + public UseBlockEvent(Event originalEvent, List> causes, Location target, Material effectiveMaterial) { + super(originalEvent, causes, target, effectiveMaterial); + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/event/entity/AbstractEntityEvent.java b/src/main/java/com/sk89q/worldguard/internal/event/entity/AbstractEntityEvent.java new file mode 100644 index 00000000..3e33d8bb --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/event/entity/AbstractEntityEvent.java @@ -0,0 +1,87 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.event.entity; + +import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.internal.event.AbstractInteractEvent; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.event.Event; + +import javax.annotation.Nullable; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +abstract class AbstractEntityEvent extends AbstractInteractEvent { + + private final Location target; + @Nullable + private final Entity entity; + + /** + * Create a new instance + * + * @param originalEvent the original event + * @param causes a list of causes, where the originating causes are at the beginning + * @param entity the target + */ + protected AbstractEntityEvent(Event originalEvent, List> causes, Entity entity) { + super(originalEvent, causes); + checkNotNull(entity); + this.target = entity.getLocation(); + this.entity = entity; + } + + /** + * Create a new instance + * + * @param originalEvent the original event + * @param causes a list of causes, where the originating causes are at the beginning + * @param target the target + */ + protected AbstractEntityEvent(Event originalEvent, List> causes, Location target) { + super(originalEvent, causes); + checkNotNull(target); + this.target = target; + this.entity = null; + } + + /** + * Get the target location being affected. + * + * @return a location + */ + public Location getTarget() { + return target; + } + + + /** + * Get the target entity being affected. + * + * @return a entity + */ + @Nullable + public Entity getEntity() { + return entity; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/event/BlockInteractEvent.java b/src/main/java/com/sk89q/worldguard/internal/event/entity/DestroyEntityEvent.java similarity index 67% rename from src/main/java/com/sk89q/worldguard/internal/event/BlockInteractEvent.java rename to src/main/java/com/sk89q/worldguard/internal/event/entity/DestroyEntityEvent.java index 5f21d854..ed68ed8f 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/BlockInteractEvent.java +++ b/src/main/java/com/sk89q/worldguard/internal/event/entity/DestroyEntityEvent.java @@ -17,46 +17,37 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event; +package com.sk89q.worldguard.internal.event.entity; import com.sk89q.worldguard.internal.cause.Cause; -import org.bukkit.block.Block; +import org.bukkit.entity.Entity; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import javax.annotation.Nonnull; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; -/** - * Fired when a block is interacted with. - */ -public class BlockInteractEvent extends AbstractInteractEvent { +public class DestroyEntityEvent extends AbstractEntityEvent { private static final HandlerList handlers = new HandlerList(); - private final Block target; /** * Create a new instance. * * @param originalEvent the original event * @param causes a list of causes, where the originating causes are at the beginning - * @param interaction the action that is being taken - * @param target the target block being affected + * @param target the target entity being affected */ - public BlockInteractEvent(Event originalEvent, List> causes, Interaction interaction, Block target) { - super(originalEvent, causes, interaction); - checkNotNull(target); - this.target = target; + public DestroyEntityEvent(Event originalEvent, List> causes, Entity target) { + super(originalEvent, causes, checkNotNull(target)); } - /** - * Get the target block being affected. - * - * @return a block - */ - public Block getTarget() { - return target; + @Override + @Nonnull + public Entity getEntity() { + return super.getEntity(); } @Override diff --git a/src/main/java/com/sk89q/worldguard/internal/event/entity/SpawnEntityEvent.java b/src/main/java/com/sk89q/worldguard/internal/event/entity/SpawnEntityEvent.java new file mode 100644 index 00000000..d51bc364 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/internal/event/entity/SpawnEntityEvent.java @@ -0,0 +1,67 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.internal.event.entity; + +import com.sk89q.worldguard.internal.cause.Cause; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class SpawnEntityEvent extends AbstractEntityEvent { + + private static final HandlerList handlers = new HandlerList(); + private final EntityType effectiveType; + + public SpawnEntityEvent(Event originalEvent, List> causes, Entity target) { + super(originalEvent, causes, checkNotNull(target)); + this.effectiveType = target.getType(); + } + + public SpawnEntityEvent(Event originalEvent, List> causes, Location location, EntityType type) { + super(originalEvent, causes, location); + checkNotNull(type); + this.effectiveType = type; + } + + /** + * Get the effective entity type of the spawned entity. + * + * @return the effective type + */ + public EntityType getEffectiveType() { + return effectiveType; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/internal/event/EntityInteractEvent.java b/src/main/java/com/sk89q/worldguard/internal/event/entity/UseEntityEvent.java similarity index 73% rename from src/main/java/com/sk89q/worldguard/internal/event/EntityInteractEvent.java rename to src/main/java/com/sk89q/worldguard/internal/event/entity/UseEntityEvent.java index bc924ee0..e8002534 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/EntityInteractEvent.java +++ b/src/main/java/com/sk89q/worldguard/internal/event/entity/UseEntityEvent.java @@ -17,13 +17,14 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event; +package com.sk89q.worldguard.internal.event.entity; import com.sk89q.worldguard.internal.cause.Cause; import org.bukkit.entity.Entity; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import javax.annotation.Nonnull; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -31,32 +32,25 @@ /** * Fired when an entity is interacted with. */ -public class EntityInteractEvent extends AbstractInteractEvent { +public class UseEntityEvent extends AbstractEntityEvent { private static final HandlerList handlers = new HandlerList(); - private final Entity target; /** * Create a new instance. * * @param originalEvent the original event * @param causes a list of causes, where the originating causes are at the beginning - * @param interaction the action that is being taken * @param target the target entity being affected */ - public EntityInteractEvent(Event originalEvent, List> causes, Interaction interaction, Entity target) { - super(originalEvent, causes, interaction); - checkNotNull(target); - this.target = target; + public UseEntityEvent(Event originalEvent, List> causes, Entity target) { + super(originalEvent, causes, checkNotNull(target)); } - /** - * Get the target entity. - * - * @return the target entity - */ - public Entity getTarget() { - return target; + @Override + @Nonnull + public Entity getEntity() { + return super.getEntity(); } @Override @@ -67,4 +61,5 @@ public HandlerList getHandlers() { public static HandlerList getHandlerList() { return handlers; } + } diff --git a/src/main/java/com/sk89q/worldguard/internal/event/ItemInteractEvent.java b/src/main/java/com/sk89q/worldguard/internal/event/inventory/UseItemEvent.java similarity index 85% rename from src/main/java/com/sk89q/worldguard/internal/event/ItemInteractEvent.java rename to src/main/java/com/sk89q/worldguard/internal/event/inventory/UseItemEvent.java index 163cd53a..83766e9e 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/ItemInteractEvent.java +++ b/src/main/java/com/sk89q/worldguard/internal/event/inventory/UseItemEvent.java @@ -17,9 +17,10 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event; +package com.sk89q.worldguard.internal.event.inventory; import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.internal.event.AbstractInteractEvent; import org.bukkit.World; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -32,7 +33,7 @@ /** * Fired when an item is interacted with. */ -public class ItemInteractEvent extends AbstractInteractEvent { +public class UseItemEvent extends AbstractInteractEvent { private static final HandlerList handlers = new HandlerList(); private final World world; @@ -43,12 +44,11 @@ public class ItemInteractEvent extends AbstractInteractEvent { * * @param originalEvent the original event * @param causes a list of causes, where the originating causes are at the beginning - * @param interaction the action that is being taken * @param world the world * @param itemStack the item */ - public ItemInteractEvent(Event originalEvent, List> causes, Interaction interaction, World world, ItemStack itemStack) { - super(originalEvent, causes, interaction); + public UseItemEvent(Event originalEvent, List> causes, World world, ItemStack itemStack) { + super(originalEvent, causes); checkNotNull(world); checkNotNull(itemStack); this.world = world; diff --git a/src/main/java/com/sk89q/worldguard/internal/listener/BlacklistListener.java b/src/main/java/com/sk89q/worldguard/internal/listener/BlacklistListener.java index d750cfcd..18fcde6b 100644 --- a/src/main/java/com/sk89q/worldguard/internal/listener/BlacklistListener.java +++ b/src/main/java/com/sk89q/worldguard/internal/listener/BlacklistListener.java @@ -19,16 +19,37 @@ package com.sk89q.worldguard.internal.listener; +import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.blacklist.event.BlockBreakBlacklistEvent; +import com.sk89q.worldguard.blacklist.event.BlockDispenseBlacklistEvent; +import com.sk89q.worldguard.blacklist.event.BlockInteractBlacklistEvent; import com.sk89q.worldguard.blacklist.event.BlockPlaceBlacklistEvent; +import com.sk89q.worldguard.blacklist.event.ItemAcquireBlacklistEvent; import com.sk89q.worldguard.blacklist.event.ItemDestroyWithBlacklistEvent; +import com.sk89q.worldguard.blacklist.event.ItemDropBlacklistEvent; +import com.sk89q.worldguard.blacklist.event.ItemUseBlacklistEvent; +import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.util.Materials; import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.BlockInteractEvent; +import com.sk89q.worldguard.internal.event.block.BreakBlockEvent; +import com.sk89q.worldguard.internal.event.block.PlaceBlockEvent; +import com.sk89q.worldguard.internal.event.block.UseBlockEvent; +import com.sk89q.worldguard.internal.event.entity.DestroyEntityEvent; +import com.sk89q.worldguard.internal.event.entity.SpawnEntityEvent; +import com.sk89q.worldguard.internal.event.inventory.UseItemEvent; +import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockDispenseEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.inventory.ItemStack; import static com.sk89q.worldguard.bukkit.BukkitUtil.createTarget; import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; @@ -48,9 +69,15 @@ public BlacklistListener(WorldGuardPlugin plugin) { } @EventHandler(ignoreCancelled = true) - public void handleBlockInteract(BlockInteractEvent event) { + public void onBreakBlock(BreakBlockEvent event) { Player player = Causes.getInvolvedPlayer(event.getCauses()); - Block target = event.getTarget(); + + if (player == null) { + return; + } + + LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + Block target = event.getBlock(); WorldConfiguration wcfg = getWorldConfig(player); // Blacklist guard @@ -58,31 +85,173 @@ public void handleBlockInteract(BlockInteractEvent event) { return; } - if (player != null) { - switch (event.getInteraction()) { - case BREAK: - if (!wcfg.getBlacklist().check( - new BlockBreakBlacklistEvent(getPlugin().wrapPlayer(player), toVector(target), createTarget(target)), false, false)) { - event.setCancelled(true); - return; - } + if (!wcfg.getBlacklist().check( + new BlockBreakBlacklistEvent(localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiveMaterial())), false, false)) { + event.setCancelled(true); + } else if (!wcfg.getBlacklist().check( + new ItemDestroyWithBlacklistEvent(localPlayer, toVector(event.getTarget()), createTarget(player.getItemInHand())), false, false)) { + event.setCancelled(true); + } + } - if (!wcfg.getBlacklist().check( - new ItemDestroyWithBlacklistEvent(getPlugin().wrapPlayer(player), toVector(target), createTarget(player.getItemInHand())), false, false)) { - event.setCancelled(true); - return; - } + @EventHandler(ignoreCancelled = true) + public void onPlaceBlock(PlaceBlockEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); - break; + if (player == null) { + return; + } - case PLACE: - if (!wcfg.getBlacklist().check( - new BlockPlaceBlacklistEvent(getPlugin().wrapPlayer(player), toVector(target), createTarget(target)), false, false)) { - event.setCancelled(true); - return; - } + LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + Block target = event.getBlock(); + WorldConfiguration wcfg = getWorldConfig(player); - break; + // Blacklist guard + if (wcfg.getBlacklist() == null) { + return; + } + + if (!wcfg.getBlacklist().check(new BlockPlaceBlacklistEvent( + localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiveMaterial())), false, false)) { + event.setCancelled(true); + } + } + + @EventHandler(ignoreCancelled = true) + public void onUseBlock(UseBlockEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + + if (player == null) { + return; + } + + LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + Block target = event.getBlock(); + WorldConfiguration wcfg = getWorldConfig(player); + + // Blacklist guard + if (wcfg.getBlacklist() == null) { + return; + } + + if (!wcfg.getBlacklist().check(new BlockInteractBlacklistEvent( + localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiveMaterial())), false, false)) { + event.setCancelled(true); + } + } + + @EventHandler(ignoreCancelled = true) + public void onSpawnEntity(SpawnEntityEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + + if (player == null) { + return; + } + + LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + WorldConfiguration wcfg = getWorldConfig(player); + + // Blacklist guard + if (wcfg.getBlacklist() == null) { + return; + } + + Material material = Materials.getRelatedMaterial(event.getEffectiveType()); + if (material != null) { + if (!wcfg.getBlacklist().check(new ItemUseBlacklistEvent(localPlayer, toVector(event.getTarget()), createTarget(material)), false, false)) { + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onDestroyEntity(DestroyEntityEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + + if (player == null) { + return; + } + + LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + Entity target = event.getEntity(); + WorldConfiguration wcfg = getWorldConfig(player); + + // Blacklist guard + if (wcfg.getBlacklist() == null) { + return; + } + + Material material = Materials.getRelatedMaterial(target.getType()); + if (material != null) { + // Not really a block but we only have one on-break blacklist event + if (!wcfg.getBlacklist().check(new BlockBreakBlacklistEvent(localPlayer, toVector(event.getTarget()), createTarget(material)), false, false)) { + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onUseItem(UseItemEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + + if (player == null) { + return; + } + + LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + ItemStack target = event.getItemStack(); + WorldConfiguration wcfg = getWorldConfig(player); + + // Blacklist guard + if (wcfg.getBlacklist() == null) { + return; + } + + if (!wcfg.getBlacklist().check(new ItemUseBlacklistEvent(localPlayer, toVector(player.getLocation()), createTarget(target)), false, false)) { + event.setCancelled(true); + } + } + + @EventHandler(ignoreCancelled = true) + public void onPlayerDropItem(PlayerDropItemEvent event) { + ConfigurationManager cfg = getPlugin().getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getPlayer().getWorld()); + + if (wcfg.getBlacklist() != null) { + Item ci = event.getItemDrop(); + + if (!wcfg.getBlacklist().check( + new ItemDropBlacklistEvent(getPlugin().wrapPlayer(event.getPlayer()), + toVector(ci.getLocation()), createTarget(ci.getItemStack())), false, false)) { + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPlayerPickupItem(PlayerPickupItemEvent event) { + ConfigurationManager cfg = getPlugin().getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getPlayer().getWorld()); + + if (wcfg.getBlacklist() != null) { + Item ci = event.getItem(); + + if (!wcfg.getBlacklist().check( + new ItemAcquireBlacklistEvent(getPlugin().wrapPlayer(event.getPlayer()), + toVector(ci.getLocation()), createTarget(ci.getItemStack())), false, true)) { + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onBlockDispense(BlockDispenseEvent event) { + ConfigurationManager cfg = getPlugin().getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + + if (wcfg.getBlacklist() != null) { + if (!wcfg.getBlacklist().check(new BlockDispenseBlacklistEvent(null, toVector(event.getBlock()), createTarget(event.getItem())), false, false)) { + event.setCancelled(true); } } } diff --git a/src/main/java/com/sk89q/worldguard/internal/listener/BlockedPotionsListener.java b/src/main/java/com/sk89q/worldguard/internal/listener/BlockedPotionsListener.java index 93a9e01a..85f5c455 100644 --- a/src/main/java/com/sk89q/worldguard/internal/listener/BlockedPotionsListener.java +++ b/src/main/java/com/sk89q/worldguard/internal/listener/BlockedPotionsListener.java @@ -24,7 +24,7 @@ import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.ItemInteractEvent; +import com.sk89q.worldguard.internal.event.inventory.UseItemEvent; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -49,7 +49,7 @@ public BlockedPotionsListener(WorldGuardPlugin plugin) { } @EventHandler - public void onItemInteract(ItemInteractEvent event) { + public void onItemInteract(UseItemEvent event) { // We only care about player caused events if (!Causes.mayInvolvePlayer(event.getCauses())) { return; diff --git a/src/main/java/com/sk89q/worldguard/internal/listener/ChestProtectionListener.java b/src/main/java/com/sk89q/worldguard/internal/listener/ChestProtectionListener.java index c2e3c2d2..4b7daffd 100644 --- a/src/main/java/com/sk89q/worldguard/internal/listener/ChestProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/internal/listener/ChestProtectionListener.java @@ -19,15 +19,18 @@ package com.sk89q.worldguard.internal.listener; +import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.Interaction; -import com.sk89q.worldguard.internal.event.BlockInteractEvent; +import com.sk89q.worldguard.internal.event.block.BreakBlockEvent; +import com.sk89q.worldguard.internal.event.block.PlaceBlockEvent; +import com.sk89q.worldguard.internal.event.block.UseBlockEvent; import org.bukkit.ChatColor; -import org.bukkit.block.Block; +import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.block.SignChangeEvent; /** * Handle events that need to be processed by the chest protection. @@ -44,31 +47,126 @@ public ChestProtectionListener(WorldGuardPlugin plugin) { } @EventHandler(ignoreCancelled = true) - public void handleBlockInteract(BlockInteractEvent event) { + public void onPlaceBlock(PlaceBlockEvent event) { Player player = Causes.getInvolvedPlayer(event.getCauses()); - Block target = event.getTarget(); - WorldConfiguration wcfg = getWorldConfig(player); - - // Early guard - if (!wcfg.signChestProtection) { - return; - } + Location target = event.getTarget(); if (player != null) { - if (wcfg.isChestProtected(target, player)) { - player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); - event.setCancelled(true); + WorldConfiguration wcfg = getWorldConfig(player); + + // Early guard + if (!wcfg.signChestProtection) { return; } - if (event.getInteraction() == Interaction.PLACE) { - if (wcfg.getChestProtection().isChest(target.getTypeId())) { - if (wcfg.isAdjacentChestProtected(target, player)) { - player.sendMessage(ChatColor.DARK_RED + "This spot is for a chest that you don't have permission for."); - event.setCancelled(true); - return; - } + if (wcfg.getChestProtection().isChest(event.getEffectiveMaterial().getId()) && wcfg.isChestProtected(target.getBlock(), player)) { + player.sendMessage(ChatColor.DARK_RED + "This spot is for a chest that you don't have permission for."); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onBreakBlock(BreakBlockEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + Location target = event.getTarget(); + + if (player != null) { + WorldConfiguration wcfg = getWorldConfig(player); + + // Early guard + if (!wcfg.signChestProtection) { + return; + } + + if (wcfg.isChestProtected(target.getBlock(), player)) { + player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); + event.setCancelled(true); + } + } else { + // No player? Deny anyway + event.setCancelled(true); + } + } + + @EventHandler(ignoreCancelled = true) + public void onUseBlock(UseBlockEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + Location target = event.getTarget(); + + if (player != null) { + WorldConfiguration wcfg = getWorldConfig(player); + + // Early guard + if (!wcfg.signChestProtection) { + return; + } + + if (wcfg.isChestProtected(target.getBlock(), player)) { + player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); + event.setCancelled(true); + } + } else { + // No player? Deny anyway + event.setCancelled(true); + } + } + + @EventHandler(ignoreCancelled = true) + public void onSignChange(SignChangeEvent event) { + Player player = event.getPlayer(); + WorldConfiguration wcfg = getWorldConfig(player); + + if (wcfg.signChestProtection) { + if (event.getLine(0).equalsIgnoreCase("[Lock]")) { + if (wcfg.isChestProtectedPlacement(event.getBlock(), player)) { + player.sendMessage(ChatColor.DARK_RED + "You do not own the adjacent chest."); + event.getBlock().breakNaturally(); + event.setCancelled(true); + return; } + + if (event.getBlock().getTypeId() != BlockID.SIGN_POST) { + player.sendMessage(ChatColor.RED + + "The [Lock] sign must be a sign post, not a wall sign."); + + event.getBlock().breakNaturally(); + event.setCancelled(true); + return; + } + + if (!event.getLine(1).equalsIgnoreCase(player.getName())) { + player.sendMessage(ChatColor.RED + + "The first owner line must be your name."); + + event.getBlock().breakNaturally(); + event.setCancelled(true); + return; + } + + int below = event.getBlock().getRelative(0, -1, 0).getTypeId(); + + if (below == BlockID.TNT || below == BlockID.SAND + || below == BlockID.GRAVEL || below == BlockID.SIGN_POST) { + player.sendMessage(ChatColor.RED + + "That is not a safe block that you're putting this sign on."); + + event.getBlock().breakNaturally(); + event.setCancelled(true); + return; + } + + event.setLine(0, "[Lock]"); + player.sendMessage(ChatColor.YELLOW + + "A chest or double chest above is now protected."); + } + } else if (!wcfg.disableSignChestProtectionCheck) { + if (event.getLine(0).equalsIgnoreCase("[Lock]")) { + player.sendMessage(ChatColor.RED + + "WorldGuard's sign chest protection is disabled."); + + event.getBlock().breakNaturally(); + event.setCancelled(true); } } } diff --git a/src/main/java/com/sk89q/worldguard/internal/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/internal/listener/RegionProtectionListener.java index bf9a526b..3214d23a 100644 --- a/src/main/java/com/sk89q/worldguard/internal/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/internal/listener/RegionProtectionListener.java @@ -20,12 +20,22 @@ package com.sk89q.worldguard.internal.listener; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.util.Entities; +import com.sk89q.worldguard.bukkit.util.Materials; +import com.sk89q.worldguard.bukkit.util.RegionQuery; import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.Interaction; -import com.sk89q.worldguard.internal.event.BlockInteractEvent; +import com.sk89q.worldguard.internal.event.block.BreakBlockEvent; +import com.sk89q.worldguard.internal.event.block.PlaceBlockEvent; +import com.sk89q.worldguard.internal.event.block.UseBlockEvent; +import com.sk89q.worldguard.internal.event.entity.DestroyEntityEvent; +import com.sk89q.worldguard.internal.event.entity.SpawnEntityEvent; +import com.sk89q.worldguard.internal.event.entity.UseEntityEvent; +import com.sk89q.worldguard.protection.flags.DefaultFlag; import org.bukkit.ChatColor; -import org.bukkit.block.Block; +import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -48,23 +58,144 @@ private void tellErrorMessage(CommandSender sender, Object subject) { } @EventHandler(ignoreCancelled = true) - public void handleBlockInteract(BlockInteractEvent event) { + public void onPlaceBlock(PlaceBlockEvent event) { Player player = Causes.getInvolvedPlayer(event.getCauses()); - Block target = event.getTarget(); + Location target = event.getTarget(); + Material type = event.getEffectiveMaterial(); + + if (player != null) { + RegionQuery query = new RegionQuery(getPlugin(), player); + boolean canPlace; + + // Flint and steel, fire charge + if (type == Material.FIRE) { + canPlace = query.allows(DefaultFlag.LIGHTER, target) || (query.canBuild(target) && query.canConstruct(target)); + + } else { + canPlace = query.canBuild(target) && query.canConstruct(target); + } + + if (!canPlace) { + tellErrorMessage(player, target); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onBreakBlock(BreakBlockEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + Location target = event.getTarget(); if (player != null) { if (!getPlugin().getGlobalRegionManager().canBuild(player, target)) { tellErrorMessage(player, target); event.setCancelled(true); - return; + } else if (!getPlugin().getGlobalRegionManager().canConstruct(player, target)) { + tellErrorMessage(player, target); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onUseBlock(UseBlockEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + Location target = event.getTarget(); + Material type = event.getEffectiveMaterial(); + + if (player != null) { + RegionQuery query = new RegionQuery(getPlugin(), player); + boolean canUse; + + // Inventory blocks (CHEST_ACCESS) + if (Materials.isInventoryBlock(type)) { + canUse = query.canBuild( target) + || query.allows(DefaultFlag.CHEST_ACCESS, target) + || query.allows(DefaultFlag.USE, target); + + // Beds (SLEEP) + } else if (type == Material.BED) { + canUse = query.canBuild(target) + || query.allows(DefaultFlag.SLEEP, target) + || query.allows(DefaultFlag.USE, target); + + // TNT (TNT) + } else if (type == Material.TNT) { + canUse = query.canBuild(target) + || query.allows(DefaultFlag.TNT, target); + + // Everything else + } else { + canUse = query.canBuild(target) + || query.allows(DefaultFlag.USE, target); } - if (event.getInteraction() != Interaction.INTERACT) { - if (!getPlugin().getGlobalRegionManager().canConstruct(player, target)) { - tellErrorMessage(player, target); - event.setCancelled(true); - return; - } + if (!canUse) { + tellErrorMessage(player, target); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onSpawnEntity(SpawnEntityEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + Location target = event.getTarget(); + EntityType type = event.getEffectiveType(); + + if (player != null) { + RegionQuery query = new RegionQuery(getPlugin(), player); + boolean canSpawn; + + if (Entities.isVehicle(type)) { + canSpawn = query.canBuild(target) || query.allows(DefaultFlag.PLACE_VEHICLE, target); + } else { + canSpawn = query.canBuild(target); + } + + if (!canSpawn) { + tellErrorMessage(player, target); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onDestroyEntity(DestroyEntityEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + Location target = event.getTarget(); + EntityType type = event.getEntity().getType(); + + if (player != null) { + RegionQuery query = new RegionQuery(getPlugin(), player); + boolean canDestroy; + + if (Entities.isVehicle(type)) { + canDestroy = query.canBuild(target) || query.allows(DefaultFlag.DESTROY_VEHICLE, target); + } else { + canDestroy = query.canBuild(target); + } + + if (!canDestroy) { + tellErrorMessage(player, target); + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onUseEntity(UseEntityEvent event) { + Player player = Causes.getInvolvedPlayer(event.getCauses()); + Location target = event.getTarget(); + + if (player != null) { + RegionQuery query = new RegionQuery(getPlugin(), player); + boolean canUse = query.canBuild(target) || query.allows(DefaultFlag.USE, target); + + if (!canUse) { + tellErrorMessage(player, target); + event.setCancelled(true); } } } From 05369b69b6c560e72ac207b86eb10df08f72ad97 Mon Sep 17 00:00:00 2001 From: sk89q Date: Mon, 11 Aug 2014 17:51:00 -0700 Subject: [PATCH 022/133] Reorganize classes into different packages. --- .../worldguard/bukkit/WorldGuardPlugin.java | 17 +- .../event/AbstractInteractEvent.java | 4 +- .../event/block/AbstractBlockEvent.java | 6 +- .../event/block/BreakBlockEvent.java | 4 +- .../event/block/PlaceBlockEvent.java | 4 +- .../event/block/UseBlockEvent.java | 4 +- .../event/entity/AbstractEntityEvent.java | 6 +- .../event/entity/DestroyEntityEvent.java | 4 +- .../event/entity/SpawnEntityEvent.java | 4 +- .../event/entity/UseEntityEvent.java | 4 +- .../event/inventory/UseItemEvent.java | 6 +- .../listener/AbstractListener.java | 2 +- .../listener/BlacklistListener.java | 16 +- .../listener/BlockedPotionsListener.java | 6 +- .../listener/ChestProtectionListener.java | 10 +- ...ner.java => EventAbstractionListener.java} | 26 +- .../listener/RegionProtectionListener.java | 16 +- .../WorldGuardBlockListener.java | 1460 ++++++------ .../WorldGuardCommandBookListener.java | 3 +- .../WorldGuardEntityListener.java | 2013 +++++++++-------- .../WorldGuardHangingListener.java | 241 +- .../WorldGuardPlayerListener.java | 1462 ++++++------ .../WorldGuardServerListener.java | 3 +- .../WorldGuardVehicleListener.java | 147 +- .../WorldGuardWeatherListener.java | 225 +- .../WorldGuardWorldListener.java | 198 +- .../{internal => bukkit/util}/Events.java | 2 +- .../com/sk89q/worldguard/internal/Blocks.java | 55 - .../{internal => util}/cause/BlockCause.java | 2 +- .../{internal => util}/cause/Cause.java | 2 +- .../{internal => util}/cause/Causes.java | 2 +- .../{internal => util}/cause/EntityCause.java | 2 +- .../{internal => util}/cause/PlayerCause.java | 2 +- .../cause/UnknownCause.java | 2 +- 34 files changed, 2971 insertions(+), 2989 deletions(-) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/event/AbstractInteractEvent.java (95%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/event/block/AbstractBlockEvent.java (93%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/event/block/BreakBlockEvent.java (94%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/event/block/PlaceBlockEvent.java (94%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/event/block/UseBlockEvent.java (94%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/event/entity/AbstractEntityEvent.java (93%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/event/entity/DestroyEntityEvent.java (94%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/event/entity/SpawnEntityEvent.java (95%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/event/entity/UseEntityEvent.java (94%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/event/inventory/UseItemEvent.java (93%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/listener/AbstractListener.java (98%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/listener/BlacklistListener.java (94%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/listener/BlockedPotionsListener.java (96%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/listener/ChestProtectionListener.java (95%) rename src/main/java/com/sk89q/worldguard/bukkit/listener/{CauseListener.java => EventAbstractionListener.java} (96%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit}/listener/RegionProtectionListener.java (93%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/WorldGuardBlockListener.java (97%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/WorldGuardCommandBookListener.java (96%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/WorldGuardEntityListener.java (97%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/WorldGuardHangingListener.java (95%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/WorldGuardPlayerListener.java (97%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/WorldGuardServerListener.java (95%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/WorldGuardVehicleListener.java (92%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/WorldGuardWeatherListener.java (94%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/WorldGuardWorldListener.java (92%) rename src/main/java/com/sk89q/worldguard/{internal => bukkit/util}/Events.java (98%) delete mode 100644 src/main/java/com/sk89q/worldguard/internal/Blocks.java rename src/main/java/com/sk89q/worldguard/{internal => util}/cause/BlockCause.java (96%) rename src/main/java/com/sk89q/worldguard/{internal => util}/cause/Cause.java (96%) rename src/main/java/com/sk89q/worldguard/{internal => util}/cause/Causes.java (98%) rename src/main/java/com/sk89q/worldguard/{internal => util}/cause/EntityCause.java (96%) rename src/main/java/com/sk89q/worldguard/{internal => util}/cause/PlayerCause.java (96%) rename src/main/java/com/sk89q/worldguard/{internal => util}/cause/UnknownCause.java (96%) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index e1112f71..0b205618 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -33,10 +33,19 @@ import com.sk89q.worldguard.bukkit.commands.GeneralCommands; import com.sk89q.worldguard.bukkit.commands.ProtectionCommands; import com.sk89q.worldguard.bukkit.commands.ToggleCommands; -import com.sk89q.worldguard.internal.listener.BlacklistListener; -import com.sk89q.worldguard.internal.listener.BlockedPotionsListener; -import com.sk89q.worldguard.internal.listener.ChestProtectionListener; -import com.sk89q.worldguard.internal.listener.RegionProtectionListener; +import com.sk89q.worldguard.bukkit.listener.WorldGuardBlockListener; +import com.sk89q.worldguard.bukkit.listener.WorldGuardCommandBookListener; +import com.sk89q.worldguard.bukkit.listener.WorldGuardEntityListener; +import com.sk89q.worldguard.bukkit.listener.WorldGuardHangingListener; +import com.sk89q.worldguard.bukkit.listener.WorldGuardPlayerListener; +import com.sk89q.worldguard.bukkit.listener.WorldGuardServerListener; +import com.sk89q.worldguard.bukkit.listener.WorldGuardVehicleListener; +import com.sk89q.worldguard.bukkit.listener.WorldGuardWeatherListener; +import com.sk89q.worldguard.bukkit.listener.WorldGuardWorldListener; +import com.sk89q.worldguard.bukkit.listener.BlacklistListener; +import com.sk89q.worldguard.bukkit.listener.BlockedPotionsListener; +import com.sk89q.worldguard.bukkit.listener.ChestProtectionListener; +import com.sk89q.worldguard.bukkit.listener.RegionProtectionListener; import com.sk89q.worldguard.protection.GlobalRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/AbstractInteractEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/AbstractInteractEvent.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/internal/event/AbstractInteractEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/AbstractInteractEvent.java index ed528d80..a2a8d22e 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/AbstractInteractEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/AbstractInteractEvent.java @@ -17,9 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event; +package com.sk89q.worldguard.bukkit.event; -import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.util.cause.Cause; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/block/AbstractBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java similarity index 93% rename from src/main/java/com/sk89q/worldguard/internal/event/block/AbstractBlockEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java index f856e27b..a9f423ef 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/block/AbstractBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java @@ -17,10 +17,10 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event.block; +package com.sk89q.worldguard.bukkit.event.block; -import com.sk89q.worldguard.internal.cause.Cause; -import com.sk89q.worldguard.internal.event.AbstractInteractEvent; +import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.event.AbstractInteractEvent; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/block/BreakBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java similarity index 94% rename from src/main/java/com/sk89q/worldguard/internal/event/block/BreakBlockEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java index ddd0c667..fb929316 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/block/BreakBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java @@ -17,9 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event.block; +package com.sk89q.worldguard.bukkit.event.block; -import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.util.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/block/PlaceBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java similarity index 94% rename from src/main/java/com/sk89q/worldguard/internal/event/block/PlaceBlockEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java index 69874e14..68d0d7c9 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/block/PlaceBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java @@ -17,9 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event.block; +package com.sk89q.worldguard.bukkit.event.block; -import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.util.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/block/UseBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java similarity index 94% rename from src/main/java/com/sk89q/worldguard/internal/event/block/UseBlockEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java index 99705dc9..21ba32de 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/block/UseBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java @@ -17,9 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event.block; +package com.sk89q.worldguard.bukkit.event.block; -import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.util.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/entity/AbstractEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java similarity index 93% rename from src/main/java/com/sk89q/worldguard/internal/event/entity/AbstractEntityEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java index 3e33d8bb..bb723f4b 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/entity/AbstractEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java @@ -17,10 +17,10 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event.entity; +package com.sk89q.worldguard.bukkit.event.entity; -import com.sk89q.worldguard.internal.cause.Cause; -import com.sk89q.worldguard.internal.event.AbstractInteractEvent; +import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.event.AbstractInteractEvent; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.event.Event; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/entity/DestroyEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java similarity index 94% rename from src/main/java/com/sk89q/worldguard/internal/event/entity/DestroyEntityEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java index ed68ed8f..3b74ee09 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/entity/DestroyEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java @@ -17,9 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event.entity; +package com.sk89q.worldguard.bukkit.event.entity; -import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.util.cause.Cause; import org.bukkit.entity.Entity; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/entity/SpawnEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/internal/event/entity/SpawnEntityEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java index d51bc364..bfd41a9f 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/entity/SpawnEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java @@ -17,9 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event.entity; +package com.sk89q.worldguard.bukkit.event.entity; -import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.util.cause.Cause; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/entity/UseEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java similarity index 94% rename from src/main/java/com/sk89q/worldguard/internal/event/entity/UseEntityEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java index e8002534..08933202 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/entity/UseEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java @@ -17,9 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event.entity; +package com.sk89q.worldguard.bukkit.event.entity; -import com.sk89q.worldguard.internal.cause.Cause; +import com.sk89q.worldguard.util.cause.Cause; import org.bukkit.entity.Entity; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; diff --git a/src/main/java/com/sk89q/worldguard/internal/event/inventory/UseItemEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java similarity index 93% rename from src/main/java/com/sk89q/worldguard/internal/event/inventory/UseItemEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java index 83766e9e..2d35d804 100644 --- a/src/main/java/com/sk89q/worldguard/internal/event/inventory/UseItemEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java @@ -17,10 +17,10 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.event.inventory; +package com.sk89q.worldguard.bukkit.event.inventory; -import com.sk89q.worldguard.internal.cause.Cause; -import com.sk89q.worldguard.internal.event.AbstractInteractEvent; +import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.event.AbstractInteractEvent; import org.bukkit.World; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; diff --git a/src/main/java/com/sk89q/worldguard/internal/listener/AbstractListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/AbstractListener.java similarity index 98% rename from src/main/java/com/sk89q/worldguard/internal/listener/AbstractListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/AbstractListener.java index f8845c5b..bce1e6ac 100644 --- a/src/main/java/com/sk89q/worldguard/internal/listener/AbstractListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/AbstractListener.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.listener; +package com.sk89q.worldguard.bukkit.listener; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; diff --git a/src/main/java/com/sk89q/worldguard/internal/listener/BlacklistListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java similarity index 94% rename from src/main/java/com/sk89q/worldguard/internal/listener/BlacklistListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java index 18fcde6b..7399d3ea 100644 --- a/src/main/java/com/sk89q/worldguard/internal/listener/BlacklistListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.listener; +package com.sk89q.worldguard.bukkit.listener; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.blacklist.event.BlockBreakBlacklistEvent; @@ -32,13 +32,13 @@ import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.bukkit.util.Materials; -import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.block.BreakBlockEvent; -import com.sk89q.worldguard.internal.event.block.PlaceBlockEvent; -import com.sk89q.worldguard.internal.event.block.UseBlockEvent; -import com.sk89q.worldguard.internal.event.entity.DestroyEntityEvent; -import com.sk89q.worldguard.internal.event.entity.SpawnEntityEvent; -import com.sk89q.worldguard.internal.event.inventory.UseItemEvent; +import com.sk89q.worldguard.util.cause.Causes; +import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; +import com.sk89q.worldguard.bukkit.event.entity.DestroyEntityEvent; +import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; +import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Entity; diff --git a/src/main/java/com/sk89q/worldguard/internal/listener/BlockedPotionsListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlockedPotionsListener.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/internal/listener/BlockedPotionsListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/BlockedPotionsListener.java index 85f5c455..a02a979c 100644 --- a/src/main/java/com/sk89q/worldguard/internal/listener/BlockedPotionsListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlockedPotionsListener.java @@ -17,14 +17,14 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.listener; +package com.sk89q.worldguard.bukkit.listener; import com.sk89q.worldguard.bukkit.BukkitUtil; import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.inventory.UseItemEvent; +import com.sk89q.worldguard.util.cause.Causes; +import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.entity.Player; diff --git a/src/main/java/com/sk89q/worldguard/internal/listener/ChestProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/internal/listener/ChestProtectionListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java index 4b7daffd..3757ad98 100644 --- a/src/main/java/com/sk89q/worldguard/internal/listener/ChestProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java @@ -17,15 +17,15 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.listener; +package com.sk89q.worldguard.bukkit.listener; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.block.BreakBlockEvent; -import com.sk89q.worldguard.internal.event.block.PlaceBlockEvent; -import com.sk89q.worldguard.internal.event.block.UseBlockEvent; +import com.sk89q.worldguard.util.cause.Causes; +import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.entity.Player; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/CauseListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/bukkit/listener/CauseListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java index 485b0b31..7e25d31e 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/CauseListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java @@ -22,16 +22,16 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.bukkit.util.Blocks; import com.sk89q.worldguard.bukkit.util.Materials; -import com.sk89q.worldguard.internal.Events; -import com.sk89q.worldguard.internal.cause.Cause; -import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.block.BreakBlockEvent; -import com.sk89q.worldguard.internal.event.block.PlaceBlockEvent; -import com.sk89q.worldguard.internal.event.block.UseBlockEvent; -import com.sk89q.worldguard.internal.event.entity.DestroyEntityEvent; -import com.sk89q.worldguard.internal.event.entity.SpawnEntityEvent; -import com.sk89q.worldguard.internal.event.entity.UseEntityEvent; -import com.sk89q.worldguard.internal.event.inventory.UseItemEvent; +import com.sk89q.worldguard.bukkit.util.Events; +import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.util.cause.Causes; +import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; +import com.sk89q.worldguard.bukkit.event.entity.DestroyEntityEvent; +import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; +import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; +import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; import org.bukkit.DyeColor; import org.bukkit.Material; import org.bukkit.World; @@ -87,13 +87,13 @@ import static com.sk89q.worldguard.bukkit.util.Materials.isBlockModifiedOnClick; import static com.sk89q.worldguard.bukkit.util.Materials.isItemAppliedToBlock; -import static com.sk89q.worldguard.internal.cause.Causes.create; +import static com.sk89q.worldguard.util.cause.Causes.create; -public class CauseListener implements Listener { +public class EventAbstractionListener implements Listener { private final WorldGuardPlugin plugin; - public CauseListener(WorldGuardPlugin plugin) { + public EventAbstractionListener(WorldGuardPlugin plugin) { this.plugin = plugin; } diff --git a/src/main/java/com/sk89q/worldguard/internal/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java similarity index 93% rename from src/main/java/com/sk89q/worldguard/internal/listener/RegionProtectionListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java index 3214d23a..4c9c1a4c 100644 --- a/src/main/java/com/sk89q/worldguard/internal/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java @@ -17,19 +17,19 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.listener; +package com.sk89q.worldguard.bukkit.listener; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.bukkit.util.Entities; import com.sk89q.worldguard.bukkit.util.Materials; import com.sk89q.worldguard.bukkit.util.RegionQuery; -import com.sk89q.worldguard.internal.cause.Causes; -import com.sk89q.worldguard.internal.event.block.BreakBlockEvent; -import com.sk89q.worldguard.internal.event.block.PlaceBlockEvent; -import com.sk89q.worldguard.internal.event.block.UseBlockEvent; -import com.sk89q.worldguard.internal.event.entity.DestroyEntityEvent; -import com.sk89q.worldguard.internal.event.entity.SpawnEntityEvent; -import com.sk89q.worldguard.internal.event.entity.UseEntityEvent; +import com.sk89q.worldguard.util.cause.Causes; +import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; +import com.sk89q.worldguard.bukkit.event.entity.DestroyEntityEvent; +import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; +import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; import com.sk89q.worldguard.protection.flags.DefaultFlag; import org.bukkit.ChatColor; import org.bukkit.Location; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardBlockListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java similarity index 97% rename from src/main/java/com/sk89q/worldguard/bukkit/WorldGuardBlockListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java index 4cab5fab..ef0c69cf 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardBlockListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java @@ -1,728 +1,732 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.blocks.BlockID; -import com.sk89q.worldedit.blocks.BlockType; -import com.sk89q.worldedit.blocks.ItemType; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.entity.Snowman; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockBurnEvent; -import org.bukkit.event.block.BlockExpEvent; -import org.bukkit.event.block.BlockFadeEvent; -import org.bukkit.event.block.BlockFormEvent; -import org.bukkit.event.block.BlockFromToEvent; -import org.bukkit.event.block.BlockIgniteEvent; -import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; -import org.bukkit.event.block.BlockPhysicsEvent; -import org.bukkit.event.block.BlockPistonExtendEvent; -import org.bukkit.event.block.BlockPistonRetractEvent; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.block.BlockRedstoneEvent; -import org.bukkit.event.block.BlockSpreadEvent; -import org.bukkit.event.block.EntityBlockFormEvent; -import org.bukkit.event.block.LeavesDecayEvent; -import org.bukkit.inventory.ItemStack; - -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - -/** - * The listener for block events. - * - * @author sk89q - */ -public class WorldGuardBlockListener implements Listener { - - private WorldGuardPlugin plugin; - - /** - * Construct the object. - * - * @param plugin The plugin instance - */ - public WorldGuardBlockListener(WorldGuardPlugin plugin) { - this.plugin = plugin; - } - - /** - * Register events. - */ - public void registerEvents() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - /** - * Get the world configuration given a world. - * - * @param world The world to get the configuration for. - * @return The configuration for {@code world} - */ - protected WorldConfiguration getWorldConfig(World world) { - return plugin.getGlobalStateManager().get(world); - } - - /** - * Get the world configuration given a player. - * - * @param player The player to get the wold from - * @return The {@link WorldConfiguration} for the player's world - */ - protected WorldConfiguration getWorldConfig(Player player) { - return getWorldConfig(player.getWorld()); - } - - /* - * Called when a block is broken. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockBreak(BlockBreakEvent event) { - Player player = event.getPlayer(); - Block target = event.getBlock(); - WorldConfiguration wcfg = getWorldConfig(player); - - if (!wcfg.itemDurability) { - ItemStack held = player.getItemInHand(); - if (held.getType() != Material.AIR && !(ItemType.usesDamageValue(held.getTypeId())|| BlockType.usesData(held.getTypeId()))) { - held.setDurability((short) 0); - player.setItemInHand(held); - } - } - } - - /* - * Called when fluids flow. - */ - @EventHandler(ignoreCancelled = true) - public void onBlockFromTo(BlockFromToEvent event) { - World world = event.getBlock().getWorld(); - Block blockFrom = event.getBlock(); - Block blockTo = event.getToBlock(); - - boolean isWater = blockFrom.getTypeId() == 8 || blockFrom.getTypeId() == 9; - boolean isLava = blockFrom.getTypeId() == 10 || blockFrom.getTypeId() == 11; - boolean isAir = blockFrom.getTypeId() == 0; - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - - if (cfg.activityHaltToggle) { - event.setCancelled(true); - return; - } - - if (wcfg.simulateSponge && isWater) { - int ox = blockTo.getX(); - int oy = blockTo.getY(); - int oz = blockTo.getZ(); - - for (int cx = -wcfg.spongeRadius; cx <= wcfg.spongeRadius; cx++) { - for (int cy = -wcfg.spongeRadius; cy <= wcfg.spongeRadius; cy++) { - for (int cz = -wcfg.spongeRadius; cz <= wcfg.spongeRadius; cz++) { - Block sponge = world.getBlockAt(ox + cx, oy + cy, oz + cz); - if (sponge.getTypeId() == 19 - && (!wcfg.redstoneSponges || !sponge.isBlockIndirectlyPowered())) { - event.setCancelled(true); - return; - } - } - } - } - } - - /*if (plugin.classicWater && isWater) { - int blockBelow = blockFrom.getRelative(0, -1, 0).getTypeId(); - if (blockBelow != 0 && blockBelow != 8 && blockBelow != 9) { - blockFrom.setTypeId(9); - if (blockTo.getTypeId() == 0) { - blockTo.setTypeId(9); - } - return; - } - }*/ - - // Check the fluid block (from) whether it is air. - // If so and the target block is protected, cancel the event - if (wcfg.preventWaterDamage.size() > 0) { - int targetId = blockTo.getTypeId(); - - if ((isAir || isWater) && - wcfg.preventWaterDamage.contains(targetId)) { - event.setCancelled(true); - return; - } - } - - if (wcfg.allowedLavaSpreadOver.size() > 0 && isLava) { - int targetId = blockTo.getRelative(0, -1, 0).getTypeId(); - - if (!wcfg.allowedLavaSpreadOver.contains(targetId)) { - event.setCancelled(true); - return; - } - } - - if (wcfg.highFreqFlags && isWater - && !plugin.getGlobalRegionManager().allows(DefaultFlag.WATER_FLOW, - blockFrom.getLocation())) { - event.setCancelled(true); - return; - } - - if (wcfg.highFreqFlags && isLava - && !plugin.getGlobalRegionManager().allows(DefaultFlag.LAVA_FLOW, - blockFrom.getLocation())) { - event.setCancelled(true); - return; - } - - if (wcfg.disableObsidianGenerators && (isAir || isLava) - && (blockTo.getTypeId() == BlockID.REDSTONE_WIRE - || blockTo.getTypeId() == BlockID.TRIPWIRE)) { - blockTo.setTypeId(BlockID.AIR); - return; - } - } - - /* - * Called when a block gets ignited. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockIgnite(BlockIgniteEvent event) { - IgniteCause cause = event.getCause(); - Block block = event.getBlock(); - World world = block.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (cfg.activityHaltToggle) { - event.setCancelled(true); - return; - } - boolean isFireSpread = cause == IgniteCause.SPREAD; - - if (wcfg.preventLightningFire && cause == IgniteCause.LIGHTNING) { - event.setCancelled(true); - return; - } - - if (wcfg.preventLavaFire && cause == IgniteCause.LAVA) { - event.setCancelled(true); - return; - } - - if (wcfg.disableFireSpread && isFireSpread) { - event.setCancelled(true); - return; - } - - if (wcfg.blockLighter && (cause == IgniteCause.FLINT_AND_STEEL || cause == IgniteCause.FIREBALL) - && event.getPlayer() != null - && !plugin.hasPermission(event.getPlayer(), "worldguard.override.lighter")) { - event.setCancelled(true); - return; - } - - if (wcfg.fireSpreadDisableToggle && isFireSpread) { - event.setCancelled(true); - return; - } - - if (wcfg.disableFireSpreadBlocks.size() > 0 && isFireSpread) { - int x = block.getX(); - int y = block.getY(); - int z = block.getZ(); - - if (wcfg.disableFireSpreadBlocks.contains(world.getBlockTypeIdAt(x, y - 1, z)) - || wcfg.disableFireSpreadBlocks.contains(world.getBlockTypeIdAt(x + 1, y, z)) - || wcfg.disableFireSpreadBlocks.contains(world.getBlockTypeIdAt(x - 1, y, z)) - || wcfg.disableFireSpreadBlocks.contains(world.getBlockTypeIdAt(x, y, z - 1)) - || wcfg.disableFireSpreadBlocks.contains(world.getBlockTypeIdAt(x, y, z + 1))) { - event.setCancelled(true); - return; - } - } - - if (wcfg.useRegions) { - Vector pt = toVector(block); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - if (wcfg.highFreqFlags && isFireSpread - && !set.allows(DefaultFlag.FIRE_SPREAD)) { - event.setCancelled(true); - return; - } - - if (wcfg.highFreqFlags && cause == IgniteCause.LAVA - && !set.allows(DefaultFlag.LAVA_FIRE)) { - event.setCancelled(true); - return; - } - - if (cause == IgniteCause.FIREBALL && event.getPlayer() == null) { - // wtf bukkit, FIREBALL is supposed to be reserved to players - if (!set.allows(DefaultFlag.GHAST_FIREBALL)) { - event.setCancelled(true); - return; - } - } - - if (cause == IgniteCause.LIGHTNING && !set.allows(DefaultFlag.LIGHTNING)) { - event.setCancelled(true); - return; - } - } - } - - /* - * Called when a block is destroyed from burning. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockBurn(BlockBurnEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - - if (cfg.activityHaltToggle) { - event.setCancelled(true); - return; - } - - if (wcfg.disableFireSpread) { - event.setCancelled(true); - return; - } - - if (wcfg.fireSpreadDisableToggle) { - Block block = event.getBlock(); - event.setCancelled(true); - checkAndDestroyAround(block.getWorld(), block.getX(), block.getY(), block.getZ(), BlockID.FIRE); - return; - } - - if (wcfg.disableFireSpreadBlocks.size() > 0) { - Block block = event.getBlock(); - - if (wcfg.disableFireSpreadBlocks.contains(block.getTypeId())) { - event.setCancelled(true); - checkAndDestroyAround(block.getWorld(), block.getX(), block.getY(), block.getZ(), BlockID.FIRE); - return; - } - } - - if (wcfg.isChestProtected(event.getBlock())) { - event.setCancelled(true); - return; - } - - if (wcfg.useRegions) { - Block block = event.getBlock(); - int x = block.getX(); - int y = block.getY(); - int z = block.getZ(); - Vector pt = toVector(block); - RegionManager mgr = plugin.getGlobalRegionManager().get(block.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - if (!set.allows(DefaultFlag.FIRE_SPREAD)) { - checkAndDestroyAround(block.getWorld(), x, y, z, BlockID.FIRE); - event.setCancelled(true); - return; - } - - } - } - - private void checkAndDestroyAround(World world, int x, int y, int z, int required) { - checkAndDestroy(world, x, y, z + 1, required); - checkAndDestroy(world, x, y, z - 1, required); - checkAndDestroy(world, x, y + 1, z, required); - checkAndDestroy(world, x, y - 1, z, required); - checkAndDestroy(world, x + 1, y, z, required); - checkAndDestroy(world, x - 1, y, z, required); - } - - private void checkAndDestroy(World world, int x, int y, int z, int required) { - if (world.getBlockTypeIdAt(x, y, z) == required) { - world.getBlockAt(x, y, z).setTypeId(BlockID.AIR); - } - } - - /* - * Called when block physics occurs. - */ - @EventHandler(ignoreCancelled = true) - public void onBlockPhysics(BlockPhysicsEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - - if (cfg.activityHaltToggle) { - event.setCancelled(true); - return; - } - - int id = event.getChangedTypeId(); - - if (id == 13 && wcfg.noPhysicsGravel) { - event.setCancelled(true); - return; - } - - if (id == 12 && wcfg.noPhysicsSand) { - event.setCancelled(true); - return; - } - - if (id == 90 && wcfg.allowPortalAnywhere) { - event.setCancelled(true); - return; - } - - if (wcfg.ropeLadders && event.getBlock().getType() == Material.LADDER) { - if (event.getBlock().getRelative(0, 1, 0).getType() == Material.LADDER) { - event.setCancelled(true); - return; - } - } - } - - /* - * Called when a player places a block. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockPlace(BlockPlaceEvent event) { - Block target = event.getBlock(); - World world = target.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.simulateSponge && target.getType() == Material.SPONGE) { - if (wcfg.redstoneSponges && target.isBlockIndirectlyPowered()) { - return; - } - - int ox = target.getX(); - int oy = target.getY(); - int oz = target.getZ(); - - SpongeUtil.clearSpongeWater(plugin, world, ox, oy, oz); - } - } - - /* - * Called when redstone changes. - */ - @EventHandler(priority = EventPriority.HIGH) - public void onBlockRedstoneChange(BlockRedstoneEvent event) { - Block blockTo = event.getBlock(); - World world = blockTo.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.simulateSponge && wcfg.redstoneSponges) { - int ox = blockTo.getX(); - int oy = blockTo.getY(); - int oz = blockTo.getZ(); - - for (int cx = -1; cx <= 1; cx++) { - for (int cy = -1; cy <= 1; cy++) { - for (int cz = -1; cz <= 1; cz++) { - Block sponge = world.getBlockAt(ox + cx, oy + cy, oz + cz); - if (sponge.getTypeId() == 19 - && sponge.isBlockIndirectlyPowered()) { - SpongeUtil.clearSpongeWater(plugin, world, ox + cx, oy + cy, oz + cz); - } else if (sponge.getTypeId() == 19 - && !sponge.isBlockIndirectlyPowered()) { - SpongeUtil.addSpongeWater(plugin, world, ox + cx, oy + cy, oz + cz); - } - } - } - } - - return; - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onLeavesDecay(LeavesDecayEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - - if (cfg.activityHaltToggle) { - event.setCancelled(true); - return; - } - - if (wcfg.disableLeafDecay) { - event.setCancelled(true); - return; - } - - if (wcfg.useRegions) { - if (!plugin.getGlobalRegionManager().allows(DefaultFlag.LEAF_DECAY, - event.getBlock().getLocation())) { - event.setCancelled(true); - } - } - } - - /* - * Called when a block is formed based on world conditions. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockForm(BlockFormEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - - if (cfg.activityHaltToggle) { - event.setCancelled(true); - return; - } - - int type = event.getNewState().getTypeId(); - - if (event instanceof EntityBlockFormEvent) { - if (((EntityBlockFormEvent) event).getEntity() instanceof Snowman) { - if (wcfg.disableSnowmanTrails) { - event.setCancelled(true); - return; - } - } - return; - } - - if (type == BlockID.ICE) { - if (wcfg.disableIceFormation) { - event.setCancelled(true); - return; - } - if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( - DefaultFlag.ICE_FORM, event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - } - - if (type == BlockID.SNOW) { - if (wcfg.disableSnowFormation) { - event.setCancelled(true); - return; - } - if (wcfg.allowedSnowFallOver.size() > 0) { - int targetId = event.getBlock().getRelative(0, -1, 0).getTypeId(); - - if (!wcfg.allowedSnowFallOver.contains(targetId)) { - event.setCancelled(true); - return; - } - } - if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( - DefaultFlag.SNOW_FALL, event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - } - } - - /* - * Called when a block spreads based on world conditions. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockSpread(BlockSpreadEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - - if (cfg.activityHaltToggle) { - event.setCancelled(true); - return; - } - - int fromType = event.getSource().getTypeId(); - - if (fromType == BlockID.RED_MUSHROOM || fromType == BlockID.BROWN_MUSHROOM) { - if (wcfg.disableMushroomSpread) { - event.setCancelled(true); - return; - } - if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( - DefaultFlag.MUSHROOMS, event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - } - - if (fromType == BlockID.GRASS) { - if (wcfg.disableGrassGrowth) { - event.setCancelled(true); - return; - } - if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( - DefaultFlag.GRASS_SPREAD, event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - } - - if (fromType == BlockID.MYCELIUM) { - if (wcfg.disableMyceliumSpread) { - event.setCancelled(true); - return; - } - - if (wcfg.useRegions - && !plugin.getGlobalRegionManager().allows( - DefaultFlag.MYCELIUM_SPREAD, event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - } - - if (fromType == BlockID.VINE) { - if (wcfg.disableVineGrowth) { - event.setCancelled(true); - return; - } - - if (wcfg.useRegions - && !plugin.getGlobalRegionManager().allows( - DefaultFlag.VINE_GROWTH, event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - } - } - - /* - * Called when a block fades. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockFade(BlockFadeEvent event) { - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - - switch (event.getBlock().getTypeId()) { - case BlockID.ICE: - if (wcfg.disableIceMelting) { - event.setCancelled(true); - return; - } - - if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( - DefaultFlag.ICE_MELT, event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - break; - - case BlockID.SNOW: - if (wcfg.disableSnowMelting) { - event.setCancelled(true); - return; - } - - if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( - DefaultFlag.SNOW_MELT, event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - break; - - case BlockID.SOIL: - if (wcfg.disableSoilDehydration) { - event.setCancelled(true); - return; - } - if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( - DefaultFlag.SOIL_DRY, event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - break; - } - - } - - /* - * Called when a piston extends - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockPistonExtend(BlockPistonExtendEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - - if (wcfg.useRegions) { - if (!plugin.getGlobalRegionManager().allows(DefaultFlag.PISTONS, event.getBlock().getLocation())) { - event.setCancelled(true); - return; - } - for (Block block : event.getBlocks()) { - if (!plugin.getGlobalRegionManager().allows(DefaultFlag.PISTONS, block.getLocation())) { - event.setCancelled(true); - return; - } - } - } - } - - /* - * Called when a piston retracts - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onBlockPistonRetract(BlockPistonRetractEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - - if (wcfg.useRegions && event.isSticky()) { - if (!(plugin.getGlobalRegionManager().allows(DefaultFlag.PISTONS, event.getRetractLocation())) - || !(plugin.getGlobalRegionManager().allows(DefaultFlag.PISTONS, event.getBlock().getLocation()))) { - event.setCancelled(true); - return; - } - } - } - - /* - * Called when a block yields exp - */ - @EventHandler(priority = EventPriority.HIGH) - public void onBlockExp(BlockExpEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); - if (wcfg.disableExpDrops || !plugin.getGlobalRegionManager().allows(DefaultFlag.EXP_DROPS, - event.getBlock().getLocation())) { - event.setExpToDrop(0); - } - } - -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldedit.blocks.BlockType; +import com.sk89q.worldedit.blocks.ItemType; +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.SpongeUtil; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.entity.Snowman; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockExpEvent; +import org.bukkit.event.block.BlockFadeEvent; +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.block.BlockFromToEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.block.BlockRedstoneEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.block.EntityBlockFormEvent; +import org.bukkit.event.block.LeavesDecayEvent; +import org.bukkit.inventory.ItemStack; + +import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; + +/** + * The listener for block events. + * + * @author sk89q + */ +public class WorldGuardBlockListener implements Listener { + + private WorldGuardPlugin plugin; + + /** + * Construct the object. + * + * @param plugin The plugin instance + */ + public WorldGuardBlockListener(WorldGuardPlugin plugin) { + this.plugin = plugin; + } + + /** + * Register events. + */ + public void registerEvents() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + /** + * Get the world configuration given a world. + * + * @param world The world to get the configuration for. + * @return The configuration for {@code world} + */ + protected WorldConfiguration getWorldConfig(World world) { + return plugin.getGlobalStateManager().get(world); + } + + /** + * Get the world configuration given a player. + * + * @param player The player to get the wold from + * @return The {@link WorldConfiguration} for the player's world + */ + protected WorldConfiguration getWorldConfig(Player player) { + return getWorldConfig(player.getWorld()); + } + + /* + * Called when a block is broken. + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + Block target = event.getBlock(); + WorldConfiguration wcfg = getWorldConfig(player); + + if (!wcfg.itemDurability) { + ItemStack held = player.getItemInHand(); + if (held.getType() != Material.AIR && !(ItemType.usesDamageValue(held.getTypeId())|| BlockType.usesData(held.getTypeId()))) { + held.setDurability((short) 0); + player.setItemInHand(held); + } + } + } + + /* + * Called when fluids flow. + */ + @EventHandler(ignoreCancelled = true) + public void onBlockFromTo(BlockFromToEvent event) { + World world = event.getBlock().getWorld(); + Block blockFrom = event.getBlock(); + Block blockTo = event.getToBlock(); + + boolean isWater = blockFrom.getTypeId() == 8 || blockFrom.getTypeId() == 9; + boolean isLava = blockFrom.getTypeId() == 10 || blockFrom.getTypeId() == 11; + boolean isAir = blockFrom.getTypeId() == 0; + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + + if (cfg.activityHaltToggle) { + event.setCancelled(true); + return; + } + + if (wcfg.simulateSponge && isWater) { + int ox = blockTo.getX(); + int oy = blockTo.getY(); + int oz = blockTo.getZ(); + + for (int cx = -wcfg.spongeRadius; cx <= wcfg.spongeRadius; cx++) { + for (int cy = -wcfg.spongeRadius; cy <= wcfg.spongeRadius; cy++) { + for (int cz = -wcfg.spongeRadius; cz <= wcfg.spongeRadius; cz++) { + Block sponge = world.getBlockAt(ox + cx, oy + cy, oz + cz); + if (sponge.getTypeId() == 19 + && (!wcfg.redstoneSponges || !sponge.isBlockIndirectlyPowered())) { + event.setCancelled(true); + return; + } + } + } + } + } + + /*if (plugin.classicWater && isWater) { + int blockBelow = blockFrom.getRelative(0, -1, 0).getTypeId(); + if (blockBelow != 0 && blockBelow != 8 && blockBelow != 9) { + blockFrom.setTypeId(9); + if (blockTo.getTypeId() == 0) { + blockTo.setTypeId(9); + } + return; + } + }*/ + + // Check the fluid block (from) whether it is air. + // If so and the target block is protected, cancel the event + if (wcfg.preventWaterDamage.size() > 0) { + int targetId = blockTo.getTypeId(); + + if ((isAir || isWater) && + wcfg.preventWaterDamage.contains(targetId)) { + event.setCancelled(true); + return; + } + } + + if (wcfg.allowedLavaSpreadOver.size() > 0 && isLava) { + int targetId = blockTo.getRelative(0, -1, 0).getTypeId(); + + if (!wcfg.allowedLavaSpreadOver.contains(targetId)) { + event.setCancelled(true); + return; + } + } + + if (wcfg.highFreqFlags && isWater + && !plugin.getGlobalRegionManager().allows(DefaultFlag.WATER_FLOW, + blockFrom.getLocation())) { + event.setCancelled(true); + return; + } + + if (wcfg.highFreqFlags && isLava + && !plugin.getGlobalRegionManager().allows(DefaultFlag.LAVA_FLOW, + blockFrom.getLocation())) { + event.setCancelled(true); + return; + } + + if (wcfg.disableObsidianGenerators && (isAir || isLava) + && (blockTo.getTypeId() == BlockID.REDSTONE_WIRE + || blockTo.getTypeId() == BlockID.TRIPWIRE)) { + blockTo.setTypeId(BlockID.AIR); + return; + } + } + + /* + * Called when a block gets ignited. + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockIgnite(BlockIgniteEvent event) { + IgniteCause cause = event.getCause(); + Block block = event.getBlock(); + World world = block.getWorld(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (cfg.activityHaltToggle) { + event.setCancelled(true); + return; + } + boolean isFireSpread = cause == IgniteCause.SPREAD; + + if (wcfg.preventLightningFire && cause == IgniteCause.LIGHTNING) { + event.setCancelled(true); + return; + } + + if (wcfg.preventLavaFire && cause == IgniteCause.LAVA) { + event.setCancelled(true); + return; + } + + if (wcfg.disableFireSpread && isFireSpread) { + event.setCancelled(true); + return; + } + + if (wcfg.blockLighter && (cause == IgniteCause.FLINT_AND_STEEL || cause == IgniteCause.FIREBALL) + && event.getPlayer() != null + && !plugin.hasPermission(event.getPlayer(), "worldguard.override.lighter")) { + event.setCancelled(true); + return; + } + + if (wcfg.fireSpreadDisableToggle && isFireSpread) { + event.setCancelled(true); + return; + } + + if (wcfg.disableFireSpreadBlocks.size() > 0 && isFireSpread) { + int x = block.getX(); + int y = block.getY(); + int z = block.getZ(); + + if (wcfg.disableFireSpreadBlocks.contains(world.getBlockTypeIdAt(x, y - 1, z)) + || wcfg.disableFireSpreadBlocks.contains(world.getBlockTypeIdAt(x + 1, y, z)) + || wcfg.disableFireSpreadBlocks.contains(world.getBlockTypeIdAt(x - 1, y, z)) + || wcfg.disableFireSpreadBlocks.contains(world.getBlockTypeIdAt(x, y, z - 1)) + || wcfg.disableFireSpreadBlocks.contains(world.getBlockTypeIdAt(x, y, z + 1))) { + event.setCancelled(true); + return; + } + } + + if (wcfg.useRegions) { + Vector pt = toVector(block); + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + if (wcfg.highFreqFlags && isFireSpread + && !set.allows(DefaultFlag.FIRE_SPREAD)) { + event.setCancelled(true); + return; + } + + if (wcfg.highFreqFlags && cause == IgniteCause.LAVA + && !set.allows(DefaultFlag.LAVA_FIRE)) { + event.setCancelled(true); + return; + } + + if (cause == IgniteCause.FIREBALL && event.getPlayer() == null) { + // wtf bukkit, FIREBALL is supposed to be reserved to players + if (!set.allows(DefaultFlag.GHAST_FIREBALL)) { + event.setCancelled(true); + return; + } + } + + if (cause == IgniteCause.LIGHTNING && !set.allows(DefaultFlag.LIGHTNING)) { + event.setCancelled(true); + return; + } + } + } + + /* + * Called when a block is destroyed from burning. + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockBurn(BlockBurnEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + + if (cfg.activityHaltToggle) { + event.setCancelled(true); + return; + } + + if (wcfg.disableFireSpread) { + event.setCancelled(true); + return; + } + + if (wcfg.fireSpreadDisableToggle) { + Block block = event.getBlock(); + event.setCancelled(true); + checkAndDestroyAround(block.getWorld(), block.getX(), block.getY(), block.getZ(), BlockID.FIRE); + return; + } + + if (wcfg.disableFireSpreadBlocks.size() > 0) { + Block block = event.getBlock(); + + if (wcfg.disableFireSpreadBlocks.contains(block.getTypeId())) { + event.setCancelled(true); + checkAndDestroyAround(block.getWorld(), block.getX(), block.getY(), block.getZ(), BlockID.FIRE); + return; + } + } + + if (wcfg.isChestProtected(event.getBlock())) { + event.setCancelled(true); + return; + } + + if (wcfg.useRegions) { + Block block = event.getBlock(); + int x = block.getX(); + int y = block.getY(); + int z = block.getZ(); + Vector pt = toVector(block); + RegionManager mgr = plugin.getGlobalRegionManager().get(block.getWorld()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + if (!set.allows(DefaultFlag.FIRE_SPREAD)) { + checkAndDestroyAround(block.getWorld(), x, y, z, BlockID.FIRE); + event.setCancelled(true); + return; + } + + } + } + + private void checkAndDestroyAround(World world, int x, int y, int z, int required) { + checkAndDestroy(world, x, y, z + 1, required); + checkAndDestroy(world, x, y, z - 1, required); + checkAndDestroy(world, x, y + 1, z, required); + checkAndDestroy(world, x, y - 1, z, required); + checkAndDestroy(world, x + 1, y, z, required); + checkAndDestroy(world, x - 1, y, z, required); + } + + private void checkAndDestroy(World world, int x, int y, int z, int required) { + if (world.getBlockTypeIdAt(x, y, z) == required) { + world.getBlockAt(x, y, z).setTypeId(BlockID.AIR); + } + } + + /* + * Called when block physics occurs. + */ + @EventHandler(ignoreCancelled = true) + public void onBlockPhysics(BlockPhysicsEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + + if (cfg.activityHaltToggle) { + event.setCancelled(true); + return; + } + + int id = event.getChangedTypeId(); + + if (id == 13 && wcfg.noPhysicsGravel) { + event.setCancelled(true); + return; + } + + if (id == 12 && wcfg.noPhysicsSand) { + event.setCancelled(true); + return; + } + + if (id == 90 && wcfg.allowPortalAnywhere) { + event.setCancelled(true); + return; + } + + if (wcfg.ropeLadders && event.getBlock().getType() == Material.LADDER) { + if (event.getBlock().getRelative(0, 1, 0).getType() == Material.LADDER) { + event.setCancelled(true); + return; + } + } + } + + /* + * Called when a player places a block. + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockPlace(BlockPlaceEvent event) { + Block target = event.getBlock(); + World world = target.getWorld(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (wcfg.simulateSponge && target.getType() == Material.SPONGE) { + if (wcfg.redstoneSponges && target.isBlockIndirectlyPowered()) { + return; + } + + int ox = target.getX(); + int oy = target.getY(); + int oz = target.getZ(); + + SpongeUtil.clearSpongeWater(plugin, world, ox, oy, oz); + } + } + + /* + * Called when redstone changes. + */ + @EventHandler(priority = EventPriority.HIGH) + public void onBlockRedstoneChange(BlockRedstoneEvent event) { + Block blockTo = event.getBlock(); + World world = blockTo.getWorld(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (wcfg.simulateSponge && wcfg.redstoneSponges) { + int ox = blockTo.getX(); + int oy = blockTo.getY(); + int oz = blockTo.getZ(); + + for (int cx = -1; cx <= 1; cx++) { + for (int cy = -1; cy <= 1; cy++) { + for (int cz = -1; cz <= 1; cz++) { + Block sponge = world.getBlockAt(ox + cx, oy + cy, oz + cz); + if (sponge.getTypeId() == 19 + && sponge.isBlockIndirectlyPowered()) { + SpongeUtil.clearSpongeWater(plugin, world, ox + cx, oy + cy, oz + cz); + } else if (sponge.getTypeId() == 19 + && !sponge.isBlockIndirectlyPowered()) { + SpongeUtil.addSpongeWater(plugin, world, ox + cx, oy + cy, oz + cz); + } + } + } + } + + return; + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onLeavesDecay(LeavesDecayEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + + if (cfg.activityHaltToggle) { + event.setCancelled(true); + return; + } + + if (wcfg.disableLeafDecay) { + event.setCancelled(true); + return; + } + + if (wcfg.useRegions) { + if (!plugin.getGlobalRegionManager().allows(DefaultFlag.LEAF_DECAY, + event.getBlock().getLocation())) { + event.setCancelled(true); + } + } + } + + /* + * Called when a block is formed based on world conditions. + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockForm(BlockFormEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + + if (cfg.activityHaltToggle) { + event.setCancelled(true); + return; + } + + int type = event.getNewState().getTypeId(); + + if (event instanceof EntityBlockFormEvent) { + if (((EntityBlockFormEvent) event).getEntity() instanceof Snowman) { + if (wcfg.disableSnowmanTrails) { + event.setCancelled(true); + return; + } + } + return; + } + + if (type == BlockID.ICE) { + if (wcfg.disableIceFormation) { + event.setCancelled(true); + return; + } + if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( + DefaultFlag.ICE_FORM, event.getBlock().getLocation())) { + event.setCancelled(true); + return; + } + } + + if (type == BlockID.SNOW) { + if (wcfg.disableSnowFormation) { + event.setCancelled(true); + return; + } + if (wcfg.allowedSnowFallOver.size() > 0) { + int targetId = event.getBlock().getRelative(0, -1, 0).getTypeId(); + + if (!wcfg.allowedSnowFallOver.contains(targetId)) { + event.setCancelled(true); + return; + } + } + if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( + DefaultFlag.SNOW_FALL, event.getBlock().getLocation())) { + event.setCancelled(true); + return; + } + } + } + + /* + * Called when a block spreads based on world conditions. + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockSpread(BlockSpreadEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + + if (cfg.activityHaltToggle) { + event.setCancelled(true); + return; + } + + int fromType = event.getSource().getTypeId(); + + if (fromType == BlockID.RED_MUSHROOM || fromType == BlockID.BROWN_MUSHROOM) { + if (wcfg.disableMushroomSpread) { + event.setCancelled(true); + return; + } + if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( + DefaultFlag.MUSHROOMS, event.getBlock().getLocation())) { + event.setCancelled(true); + return; + } + } + + if (fromType == BlockID.GRASS) { + if (wcfg.disableGrassGrowth) { + event.setCancelled(true); + return; + } + if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( + DefaultFlag.GRASS_SPREAD, event.getBlock().getLocation())) { + event.setCancelled(true); + return; + } + } + + if (fromType == BlockID.MYCELIUM) { + if (wcfg.disableMyceliumSpread) { + event.setCancelled(true); + return; + } + + if (wcfg.useRegions + && !plugin.getGlobalRegionManager().allows( + DefaultFlag.MYCELIUM_SPREAD, event.getBlock().getLocation())) { + event.setCancelled(true); + return; + } + } + + if (fromType == BlockID.VINE) { + if (wcfg.disableVineGrowth) { + event.setCancelled(true); + return; + } + + if (wcfg.useRegions + && !plugin.getGlobalRegionManager().allows( + DefaultFlag.VINE_GROWTH, event.getBlock().getLocation())) { + event.setCancelled(true); + return; + } + } + } + + /* + * Called when a block fades. + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockFade(BlockFadeEvent event) { + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + + switch (event.getBlock().getTypeId()) { + case BlockID.ICE: + if (wcfg.disableIceMelting) { + event.setCancelled(true); + return; + } + + if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( + DefaultFlag.ICE_MELT, event.getBlock().getLocation())) { + event.setCancelled(true); + return; + } + break; + + case BlockID.SNOW: + if (wcfg.disableSnowMelting) { + event.setCancelled(true); + return; + } + + if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( + DefaultFlag.SNOW_MELT, event.getBlock().getLocation())) { + event.setCancelled(true); + return; + } + break; + + case BlockID.SOIL: + if (wcfg.disableSoilDehydration) { + event.setCancelled(true); + return; + } + if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows( + DefaultFlag.SOIL_DRY, event.getBlock().getLocation())) { + event.setCancelled(true); + return; + } + break; + } + + } + + /* + * Called when a piston extends + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockPistonExtend(BlockPistonExtendEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + + if (wcfg.useRegions) { + if (!plugin.getGlobalRegionManager().allows(DefaultFlag.PISTONS, event.getBlock().getLocation())) { + event.setCancelled(true); + return; + } + for (Block block : event.getBlocks()) { + if (!plugin.getGlobalRegionManager().allows(DefaultFlag.PISTONS, block.getLocation())) { + event.setCancelled(true); + return; + } + } + } + } + + /* + * Called when a piston retracts + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onBlockPistonRetract(BlockPistonRetractEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + + if (wcfg.useRegions && event.isSticky()) { + if (!(plugin.getGlobalRegionManager().allows(DefaultFlag.PISTONS, event.getRetractLocation())) + || !(plugin.getGlobalRegionManager().allows(DefaultFlag.PISTONS, event.getBlock().getLocation()))) { + event.setCancelled(true); + return; + } + } + } + + /* + * Called when a block yields exp + */ + @EventHandler(priority = EventPriority.HIGH) + public void onBlockExp(BlockExpEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getBlock().getWorld()); + if (wcfg.disableExpDrops || !plugin.getGlobalRegionManager().allows(DefaultFlag.EXP_DROPS, + event.getBlock().getLocation())) { + event.setExpToDrop(0); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardCommandBookListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardCommandBookListener.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/bukkit/WorldGuardCommandBookListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardCommandBookListener.java index 7727ba6d..40fa77eb 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardCommandBookListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardCommandBookListener.java @@ -17,10 +17,11 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.bukkit; +package com.sk89q.worldguard.bukkit.listener; import com.sk89q.commandbook.InfoComponent; import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import org.bukkit.entity.Player; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardEntityListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java similarity index 97% rename from src/main/java/com/sk89q/worldguard/bukkit/WorldGuardEntityListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java index ac5fb2ec..b052f72f 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardEntityListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java @@ -1,1004 +1,1009 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.blocks.BlockID; -import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.GlobalRegionManager; -import com.sk89q.worldguard.protection.events.DisallowedPVPEvent; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.entity.Creeper; -import org.bukkit.entity.EnderDragon; -import org.bukkit.entity.EnderPearl; -import org.bukkit.entity.Enderman; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Fireball; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.entity.TNTPrimed; -import org.bukkit.entity.Tameable; -import org.bukkit.entity.ThrownPotion; -import org.bukkit.entity.Wither; -import org.bukkit.entity.WitherSkull; -import org.bukkit.entity.Wolf; -import org.bukkit.entity.minecart.ExplosiveMinecart; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; -import org.bukkit.event.entity.CreeperPowerEvent; -import org.bukkit.event.entity.EntityBreakDoorEvent; -import org.bukkit.event.entity.EntityChangeBlockEvent; -import org.bukkit.event.entity.EntityCombustEvent; -import org.bukkit.event.entity.EntityCreatePortalEvent; -import org.bukkit.event.entity.EntityDamageByBlockEvent; -import org.bukkit.event.entity.EntityDamageByEntityEvent; -import org.bukkit.event.entity.EntityDamageEvent; -import org.bukkit.event.entity.EntityDamageEvent.DamageCause; -import org.bukkit.event.entity.EntityDeathEvent; -import org.bukkit.event.entity.EntityExplodeEvent; -import org.bukkit.event.entity.EntityInteractEvent; -import org.bukkit.event.entity.EntityRegainHealthEvent; -import org.bukkit.event.entity.ExpBottleEvent; -import org.bukkit.event.entity.ExplosionPrimeEvent; -import org.bukkit.event.entity.FoodLevelChangeEvent; -import org.bukkit.event.entity.PigZapEvent; -import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.entity.PotionSplashEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.projectiles.ProjectileSource; - -import java.util.Set; - -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - -/** - * Listener for entity related events. - * - * @author sk89q - */ -public class WorldGuardEntityListener implements Listener { - - private WorldGuardPlugin plugin; - - /** - * Construct the object; - * - * @param plugin The plugin instance - */ - public WorldGuardEntityListener(WorldGuardPlugin plugin) { - this.plugin = plugin; - } - - /** - * Register events. - */ - public void registerEvents() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onEntityInteract(EntityInteractEvent event) { - Entity entity = event.getEntity(); - Block block = event.getBlock(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(entity.getWorld()); - - if (block.getTypeId() == BlockID.SOIL) { - if (/* entity instanceof Creature && // catch for any entity (not thrown for players) */ - wcfg.disableCreatureCropTrampling) { - event.setCancelled(true); - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onExpBottle(ExpBottleEvent event) { - WorldConfiguration wcfg = plugin.getGlobalStateManager().get(event.getEntity().getWorld()); - - if (wcfg.disableExpDrops || !plugin.getGlobalRegionManager().allows(DefaultFlag.EXP_DROPS, - event.getEntity().getLocation())) { - event.setExperience(0); - // event.setShowEffect(false); // don't want to cancel the bottle entirely I suppose, just the exp - } - } - - @EventHandler(priority = EventPriority.HIGH) - public void onEntityDeath(EntityDeathEvent event) { - WorldConfiguration wcfg = plugin.getGlobalStateManager().get(event.getEntity().getWorld()); - - if (wcfg.disableExpDrops || !plugin.getGlobalRegionManager().allows(DefaultFlag.EXP_DROPS, - event.getEntity().getLocation())) { - event.setDroppedExp(0); - } - - if (event instanceof PlayerDeathEvent && wcfg.disableDeathMessages) { - ((PlayerDeathEvent) event).setDeathMessage(""); - } - } - - private void onEntityDamageByBlock(EntityDamageByBlockEvent event) { - Entity defender = event.getEntity(); - DamageCause type = event.getCause(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(defender.getWorld()); - - if (defender instanceof Wolf && ((Wolf) defender).isTamed()) { - if (wcfg.antiWolfDumbness && !(type == DamageCause.VOID)) { - event.setCancelled(true); - return; - } - } else if (defender instanceof Player) { - Player player = (Player) defender; - - if (isInvincible(player)) { - event.setCancelled(true); - return; - } - - if (wcfg.disableLavaDamage && type == DamageCause.LAVA) { - event.setCancelled(true); - player.setFireTicks(0); - return; - } - - if (wcfg.disableContactDamage && type == DamageCause.CONTACT) { - event.setCancelled(true); - return; - } - - if (wcfg.teleportOnVoid && type == DamageCause.VOID) { - BukkitUtil.findFreePosition(player); - event.setCancelled(true); - return; - } - - if (wcfg.disableVoidDamage && type == DamageCause.VOID) { - event.setCancelled(true); - return; - } - - if (type == DamageCause.BLOCK_EXPLOSION - && (wcfg.disableExplosionDamage || wcfg.blockOtherExplosions - || (wcfg.explosionFlagCancellation - && !plugin.getGlobalRegionManager().allows(DefaultFlag.OTHER_EXPLOSION, player.getLocation())))) { - event.setCancelled(true); - return; - } - } else { - - // for whatever reason, plugin-caused explosions with a null entity count as block explosions and aren't - // handled anywhere else - if (type == DamageCause.BLOCK_EXPLOSION - && (wcfg.blockOtherExplosions - || (wcfg.explosionFlagCancellation - && !plugin.getGlobalRegionManager().allows(DefaultFlag.OTHER_EXPLOSION, defender.getLocation())))) { - event.setCancelled(true); - return; - } - } - } - - private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { - - if (event.getDamager() instanceof Projectile) { - onEntityDamageByProjectile(event); - return; - } - - Entity attacker = event.getDamager(); - Entity defender = event.getEntity(); - - if (defender instanceof ItemFrame) { - if (checkItemFrameProtection(attacker, (ItemFrame) defender)) { - event.setCancelled(true); - return; - } - } - - if (defender instanceof Player) { - Player player = (Player) defender; - LocalPlayer localPlayer = plugin.wrapPlayer(player); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(player.getWorld()); - - if (isInvincible(player)) { - if (wcfg.regionInvinciblityRemovesMobs - && attacker instanceof LivingEntity && !(attacker instanceof Player) - && !(attacker instanceof Tameable && ((Tameable) attacker).isTamed())) { - attacker.remove(); - } - - event.setCancelled(true); - return; - } - - if (wcfg.disableLightningDamage && event.getCause() == DamageCause.LIGHTNING) { - event.setCancelled(true); - return; - } - - if (wcfg.disableExplosionDamage) { - switch (event.getCause()) { - case BLOCK_EXPLOSION: - case ENTITY_EXPLOSION: - event.setCancelled(true); - return; - } - } - - if (attacker != null) { - if (attacker instanceof Player) { - if (wcfg.useRegions) { - Vector pt = toVector(defender.getLocation()); - Vector pt2 = toVector(attacker.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - - if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer((Player) attacker))) { - tryCancelPVPEvent((Player) attacker, player, event, true); - } else if (!mgr.getApplicableRegions(pt).allows(DefaultFlag.PVP ,localPlayer)) { - tryCancelPVPEvent((Player) attacker, player, event, false); - } - } - } - - if (attacker instanceof TNTPrimed || attacker instanceof ExplosiveMinecart) { - - // The check for explosion damage should be handled already... But... What ever... - if (wcfg.blockTNTExplosions) { - event.setCancelled(true); - return; - } - if (wcfg.useRegions && wcfg.explosionFlagCancellation) { - Vector pt = toVector(defender.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - if (!set.allows(DefaultFlag.TNT, localPlayer)) { - event.setCancelled(true); - return; - } - } - } - - if (attacker instanceof Fireball) { - if (attacker instanceof WitherSkull) { - if (wcfg.blockWitherSkullExplosions) { - event.setCancelled(true); - return; - } - } else { - if (wcfg.blockFireballExplosions) { - event.setCancelled(true); - return; - } - } - if (wcfg.useRegions) { - Fireball fireball = (Fireball) attacker; - Vector pt = toVector(defender.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - if (fireball.getShooter() instanceof Player) { - Vector pt2 = toVector(((Player) fireball.getShooter()).getLocation()); - if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer((Player) fireball.getShooter()))) { - tryCancelPVPEvent((Player) fireball.getShooter(), player, event, true); - } else if (!set.allows(DefaultFlag.PVP, localPlayer)) { - tryCancelPVPEvent((Player) fireball.getShooter(), player, event, false); - } - } else { - if (!set.allows(DefaultFlag.GHAST_FIREBALL, localPlayer) && wcfg.explosionFlagCancellation) { - event.setCancelled(true); - return; - } - } - - } - } - - if (attacker instanceof LivingEntity && !(attacker instanceof Player)) { - if (attacker instanceof Creeper && wcfg.blockCreeperExplosions) { - event.setCancelled(true); - return; - } - - if (wcfg.disableMobDamage) { - event.setCancelled(true); - return; - } - - if (wcfg.useRegions) { - Vector pt = toVector(defender.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - if (!set.allows(DefaultFlag.MOB_DAMAGE, localPlayer) && !(attacker instanceof Tameable)) { - event.setCancelled(true); - return; - } - - if (attacker instanceof Creeper) { - if (!set.allows(DefaultFlag.CREEPER_EXPLOSION, localPlayer) && wcfg.explosionFlagCancellation) { - event.setCancelled(true); - return; - } - } - if (attacker instanceof Tameable) { - if (((Tameable) attacker).getOwner() == null) { - if (!set.allows(DefaultFlag.MOB_DAMAGE, localPlayer)) { - event.setCancelled(true); - return; - } - } - if (!(((Tameable) attacker).getOwner() instanceof Player)) { - return; - } - Player beastMaster = (Player) ((Tameable) attacker).getOwner(); - Vector pt2 = toVector(attacker.getLocation()); - if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer(beastMaster))) { - tryCancelPVPEvent(beastMaster, player, event, true); - } else if (!set.allows(DefaultFlag.PVP, localPlayer)) { - tryCancelPVPEvent(beastMaster, player, event, false); - } - } - } - } - } - } - } - - private void onEntityDamageByProjectile(EntityDamageByEntityEvent event) { - Entity defender = event.getEntity(); - Entity attacker; - ProjectileSource source = ((Projectile) event.getDamager()).getShooter(); - if (source instanceof LivingEntity) { - attacker = (LivingEntity) source; - } else { - return; - } - - if (defender instanceof Player) { - Player player = (Player) defender; - LocalPlayer localPlayer = plugin.wrapPlayer(player); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(player.getWorld()); - - // Check Invincible - if (isInvincible(player)) { - event.setCancelled(true); - return; - } - - // Check Mob - if (attacker != null && !(attacker instanceof Player)) { - if (wcfg.disableMobDamage) { - event.setCancelled(true); - return; - } - if (wcfg.useRegions) { - Vector pt = toVector(defender.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - - if (!mgr.getApplicableRegions(pt).allows(DefaultFlag.MOB_DAMAGE, localPlayer)) { - event.setCancelled(true); - return; - } - } - } - - // Check Player - // if (event.getDamager() instanceof EnderPearl || event.getDamager() instanceof Snowball) return; - if (attacker != null && attacker instanceof Player) { - if (event.getDamager() instanceof EnderPearl && attacker == player) return; - if (wcfg.useRegions) { - Vector pt = toVector(defender.getLocation()); - Vector pt2 = toVector(attacker.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - - if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer((Player) attacker))) { - tryCancelPVPEvent((Player) attacker, player, event, true); - } else if (!mgr.getApplicableRegions(pt).allows(DefaultFlag.PVP, localPlayer)) { - tryCancelPVPEvent((Player) attacker, player, event, false); - } - } - } - } else if (defender instanceof ItemFrame) { - if (checkItemFrameProtection(attacker, (ItemFrame) defender)) { - event.setCancelled(true); - return; - } - } - - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onEntityDamage(EntityDamageEvent event) { - - if (event instanceof EntityDamageByEntityEvent) { - this.onEntityDamageByEntity((EntityDamageByEntityEvent) event); - return; - } else if (event instanceof EntityDamageByBlockEvent) { - this.onEntityDamageByBlock((EntityDamageByBlockEvent) event); - return; - } - - Entity defender = event.getEntity(); - DamageCause type = event.getCause(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(defender.getWorld()); - - if (defender instanceof Wolf && ((Wolf) defender).isTamed()) { - if (wcfg.antiWolfDumbness) { - event.setCancelled(true); - return; - } - } else if (defender instanceof Player) { - Player player = (Player) defender; - - if (isInvincible(player)) { - event.setCancelled(true); - player.setFireTicks(0); - return; - } - - if (type == DamageCause.WITHER) { - // wither boss DoT tick - if (wcfg.disableMobDamage) { - event.setCancelled(true); - return; - } - - if (wcfg.useRegions) { - Vector pt = toVector(defender.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - if (!set.allows(DefaultFlag.MOB_DAMAGE, plugin.wrapPlayer(player))) { - event.setCancelled(true); - return; - } - } - } - - if (type == DamageCause.DROWNING && cfg.hasAmphibiousMode(player)) { - player.setRemainingAir(player.getMaximumAir()); - event.setCancelled(true); - return; - } - - ItemStack helmet = player.getInventory().getHelmet(); - - if (type == DamageCause.DROWNING && wcfg.pumpkinScuba - && helmet != null - && (helmet.getTypeId() == BlockID.PUMPKIN - || helmet.getTypeId() == BlockID.JACKOLANTERN)) { - player.setRemainingAir(player.getMaximumAir()); - event.setCancelled(true); - return; - } - - if (wcfg.disableFallDamage && type == DamageCause.FALL) { - event.setCancelled(true); - return; - } - - if (wcfg.disableFireDamage && (type == DamageCause.FIRE - || type == DamageCause.FIRE_TICK)) { - event.setCancelled(true); - return; - } - - if (wcfg.disableDrowningDamage && type == DamageCause.DROWNING) { - player.setRemainingAir(player.getMaximumAir()); - event.setCancelled(true); - return; - } - - if (wcfg.teleportOnSuffocation && type == DamageCause.SUFFOCATION) { - BukkitUtil.findFreePosition(player); - event.setCancelled(true); - return; - } - - if (wcfg.disableSuffocationDamage && type == DamageCause.SUFFOCATION) { - event.setCancelled(true); - return; - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onEntityCombust(EntityCombustEvent event) { - Entity entity = event.getEntity(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(entity.getWorld()); - - if (entity instanceof Player) { - Player player = (Player) entity; - - if (cfg.hasGodMode(player) || (wcfg.useRegions && RegionQueryUtil.isInvincible(plugin, player))) { - event.setCancelled(true); - return; - } - } - } - - /* - * Called on entity explode. - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onEntityExplode(EntityExplodeEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - Location l = event.getLocation(); - World world = l.getWorld(); - WorldConfiguration wcfg = cfg.get(world); - Entity ent = event.getEntity(); - - if (cfg.activityHaltToggle) { - if (ent != null) { - ent.remove(); - } - event.setCancelled(true); - return; - } - - if (ent instanceof Creeper) { - if (wcfg.blockCreeperExplosions) { - event.setCancelled(true); - return; - } - if (wcfg.blockCreeperBlockDamage) { - event.blockList().clear(); - return; - } - - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - - for (Block block : event.blockList()) { - if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.CREEPER_EXPLOSION)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } - } else if (ent instanceof EnderDragon) { - if (wcfg.blockEnderDragonBlockDamage) { - event.blockList().clear(); - return; - } - - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - - for (Block block : event.blockList()) { - if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.ENDERDRAGON_BLOCK_DAMAGE)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } - } else if (ent instanceof TNTPrimed || ent instanceof ExplosiveMinecart) { - if (wcfg.blockTNTExplosions) { - event.setCancelled(true); - return; - } - if (wcfg.blockTNTBlockDamage) { - event.blockList().clear(); - return; - } - - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - - for (Block block : event.blockList()) { - if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.TNT)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } - } else if (ent instanceof Fireball) { - if (ent instanceof WitherSkull) { - if (wcfg.blockWitherSkullExplosions) { - event.setCancelled(true); - return; - } - if (wcfg.blockWitherSkullBlockDamage) { - event.blockList().clear(); - return; - } - } else { - if (wcfg.blockFireballExplosions) { - event.setCancelled(true); - return; - } - if (wcfg.blockFireballBlockDamage) { - event.blockList().clear(); - return; - } - } - // allow wither skull blocking since there is no dedicated flag atm - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - - for (Block block : event.blockList()) { - if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.GHAST_FIREBALL)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } - } else if (ent instanceof Wither) { - if (wcfg.blockWitherExplosions) { - event.setCancelled(true); - return; - } - if (wcfg.blockWitherBlockDamage) { - event.blockList().clear(); - return; - } - } else { - // unhandled entity - if (wcfg.blockOtherExplosions) { - event.setCancelled(true); - return; - } - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - for (Block block : event.blockList()) { - if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.OTHER_EXPLOSION)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } - } - - - if (wcfg.signChestProtection) { - for (Block block : event.blockList()) { - if (wcfg.isChestProtected(block)) { - event.blockList().clear(); - return; - } - } - } - - } - - /* - * Called on explosion prime - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onExplosionPrime(ExplosionPrimeEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getEntity().getWorld()); - Entity ent = event.getEntity(); - - if (cfg.activityHaltToggle) { - ent.remove(); - event.setCancelled(true); - return; - } - - if (event.getEntityType() == EntityType.WITHER) { - if (wcfg.blockWitherExplosions) { - event.setCancelled(true); - return; - } - } else if (event.getEntityType() == EntityType.WITHER_SKULL) { - if (wcfg.blockWitherSkullExplosions) { - event.setCancelled(true); - return; - } - } else if (event.getEntityType() == EntityType.FIREBALL) { - if (wcfg.blockFireballExplosions) { - event.setCancelled(true); - return; - } - } else if (event.getEntityType() == EntityType.CREEPER) { - if (wcfg.blockCreeperExplosions) { - event.setCancelled(true); - return; - } - } else if (event.getEntityType() == EntityType.PRIMED_TNT - || event.getEntityType() == EntityType.MINECART_TNT) { - if (wcfg.blockTNTExplosions) { - event.setCancelled(true); - return; - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onCreatureSpawn(CreatureSpawnEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - - if (cfg.activityHaltToggle) { - event.setCancelled(true); - return; - } - - WorldConfiguration wcfg = cfg.get(event.getEntity().getWorld()); - - // allow spawning of creatures from plugins - if (!wcfg.blockPluginSpawning && event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.CUSTOM) { - return; - } - - if (wcfg.allowTamedSpawns - && event.getEntity() instanceof Tameable // nullsafe check - && ((Tameable) event.getEntity()).isTamed()) { - return; - } - - EntityType entityType = event.getEntityType(); - - if (wcfg.blockCreatureSpawn.contains(entityType)) { - event.setCancelled(true); - return; - } - - Location eventLoc = event.getLocation(); - - if (wcfg.useRegions && cfg.useRegionsCreatureSpawnEvent) { - Vector pt = toVector(eventLoc); - RegionManager mgr = plugin.getGlobalRegionManager().get(eventLoc.getWorld()); - // @TODO get victims' stacktraces and find out why it's null anyway - if (mgr == null) return; - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - if (!set.allows(DefaultFlag.MOB_SPAWNING)) { - event.setCancelled(true); - return; - } - - Set entityTypes = set.getFlag(DefaultFlag.DENY_SPAWN); - if (entityTypes != null && entityTypes.contains(entityType)) { - event.setCancelled(true); - return; - } - } - - if (wcfg.blockGroundSlimes && entityType == EntityType.SLIME - && eventLoc.getY() >= 60 - && event.getSpawnReason() == SpawnReason.NATURAL) { - event.setCancelled(true); - return; - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onCreatePortal(EntityCreatePortalEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getEntity().getWorld()); - - switch (event.getEntityType()) { - case ENDER_DRAGON: - if (wcfg.blockEnderDragonPortalCreation) event.setCancelled(true); - break; - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPigZap(PigZapEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getEntity().getWorld()); - - if (wcfg.disablePigZap) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onCreeperPower(CreeperPowerEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getEntity().getWorld()); - - if (wcfg.disableCreeperPower) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onEntityRegainHealth(EntityRegainHealthEvent event) { - - Entity ent = event.getEntity(); - World world = ent.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.disableHealthRegain) { - event.setCancelled(true); - return; - } - } - - /** - * Called when an entity changes a block somehow - * - * @param event Relevant event details - */ - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onEntityChangeBlock(EntityChangeBlockEvent event) { - Entity ent = event.getEntity(); - Block block = event.getBlock(); - Location location = block.getLocation(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(ent.getWorld()); - if (ent instanceof Enderman) { - if (wcfg.disableEndermanGriefing) { - event.setCancelled(true); - return; - } - - if (wcfg.useRegions) { - if (!plugin.getGlobalRegionManager().allows(DefaultFlag.ENDER_BUILD, location)) { - event.setCancelled(true); - return; - } - } - } else if (ent.getType() == EntityType.WITHER) { - if (wcfg.blockWitherBlockDamage || wcfg.blockWitherExplosions) { - event.setCancelled(true); - return; - } - } else if (/*ent instanceof Zombie && */event instanceof EntityBreakDoorEvent) { - if (wcfg.blockZombieDoorDestruction) { - event.setCancelled(true); - return; - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onFoodLevelChange(FoodLevelChangeEvent event) { - if (event.getEntity() instanceof Player) { - Player player = (Player) event.getEntity(); - if (event.getFoodLevel() < player.getFoodLevel() && isInvincible(player)) { - event.setCancelled(true); - } - } - } - - @EventHandler(ignoreCancelled = true) - public void onPotionSplash(PotionSplashEvent event) { - Entity entity = event.getEntity(); - ThrownPotion potion = event.getPotion(); - World world = entity.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - - GlobalRegionManager regionMan = plugin.getGlobalRegionManager(); - - int blockedEntities = 0; - for (LivingEntity e : event.getAffectedEntities()) { - if (!regionMan.allows(DefaultFlag.POTION_SPLASH, e.getLocation(), - e instanceof Player ? plugin.wrapPlayer((Player) e) : null)) { - event.setIntensity(e, 0); - ++blockedEntities; - } - } - - if (blockedEntities == event.getAffectedEntities().size()) { - event.setCancelled(true); - } - } - - /** - * Check if a player is invincible, via either god mode or region flag. If - * the region denies invincibility, the player must have an extra permission - * to override it. (worldguard.god.override-regions) - * - * @param player The player to check - * @return Whether {@code player} is invincible - */ - private boolean isInvincible(Player player) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(player.getWorld()); - - boolean god = cfg.hasGodMode(player); - if (wcfg.useRegions) { - Boolean flag = RegionQueryUtil.isAllowedInvinciblity(plugin, player); - boolean allowed = flag == null || flag; - boolean invincible = RegionQueryUtil.isInvincible(plugin, player); - - if (allowed) { - return god || invincible; - } else { - return (god && plugin.hasPermission(player, "worldguard.god.override-regions")) - || invincible; - } - } else { - return god; - } - } - - /** - * Using a DisallowedPVPEvent, notifies other plugins that WorldGuard - * wants to cancel a PvP damage event.
- * If this event is not cancelled, the attacking player is notified that - * PvP is disabled and WorldGuard cancels the damage event. - * - * @param attackingPlayer The attacker - * @param defendingPlayer The defender - * @param event The event that caused WorldGuard to act - */ - public void tryCancelPVPEvent(final Player attackingPlayer, final Player defendingPlayer, EntityDamageByEntityEvent event, boolean aggressorTriggered) { - final DisallowedPVPEvent disallowedPVPEvent = new DisallowedPVPEvent(attackingPlayer, defendingPlayer, event); - plugin.getServer().getPluginManager().callEvent(disallowedPVPEvent); - if (!disallowedPVPEvent.isCancelled()) { - if (aggressorTriggered) attackingPlayer.sendMessage(ChatColor.DARK_RED + "You are in a no-PvP area."); - else attackingPlayer.sendMessage(ChatColor.DARK_RED + "That player is in a no-PvP area."); - event.setCancelled(true); - } - } - - /** - * Checks regions and config settings to protect items from being knocked - * out of item frames. - * @param attacker attacking entity - * @param defender item frame being damaged - * @return true if the event should be cancelled - */ - private boolean checkItemFrameProtection(Entity attacker, ItemFrame defender) { - World world = attacker.getWorld(); - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - if (wcfg.useRegions) { - // bukkit throws this event when a player attempts to remove an item from a frame - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - if (!(attacker instanceof Player)) { - if (!plugin.getGlobalRegionManager().allows( - DefaultFlag.ENTITY_ITEM_FRAME_DESTROY, defender.getLocation())) { - return true; - } - } - } - if (wcfg.blockEntityItemFrameDestroy && !(attacker instanceof Player)) { - return true; - } - return false; - } - -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.bukkit.BukkitUtil; +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.RegionQueryUtil; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.GlobalRegionManager; +import com.sk89q.worldguard.protection.events.DisallowedPVPEvent; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.EnderDragon; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.Enderman; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.entity.Tameable; +import org.bukkit.entity.ThrownPotion; +import org.bukkit.entity.Wither; +import org.bukkit.entity.WitherSkull; +import org.bukkit.entity.Wolf; +import org.bukkit.entity.minecart.ExplosiveMinecart; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.entity.CreeperPowerEvent; +import org.bukkit.event.entity.EntityBreakDoorEvent; +import org.bukkit.event.entity.EntityChangeBlockEvent; +import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityCreatePortalEvent; +import org.bukkit.event.entity.EntityDamageByBlockEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityInteractEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.event.entity.ExpBottleEvent; +import org.bukkit.event.entity.ExplosionPrimeEvent; +import org.bukkit.event.entity.FoodLevelChangeEvent; +import org.bukkit.event.entity.PigZapEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.entity.PotionSplashEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.projectiles.ProjectileSource; + +import java.util.Set; + +import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; + +/** + * Listener for entity related events. + * + * @author sk89q + */ +public class WorldGuardEntityListener implements Listener { + + private WorldGuardPlugin plugin; + + /** + * Construct the object; + * + * @param plugin The plugin instance + */ + public WorldGuardEntityListener(WorldGuardPlugin plugin) { + this.plugin = plugin; + } + + /** + * Register events. + */ + public void registerEvents() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onEntityInteract(EntityInteractEvent event) { + Entity entity = event.getEntity(); + Block block = event.getBlock(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(entity.getWorld()); + + if (block.getTypeId() == BlockID.SOIL) { + if (/* entity instanceof Creature && // catch for any entity (not thrown for players) */ + wcfg.disableCreatureCropTrampling) { + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onExpBottle(ExpBottleEvent event) { + WorldConfiguration wcfg = plugin.getGlobalStateManager().get(event.getEntity().getWorld()); + + if (wcfg.disableExpDrops || !plugin.getGlobalRegionManager().allows(DefaultFlag.EXP_DROPS, + event.getEntity().getLocation())) { + event.setExperience(0); + // event.setShowEffect(false); // don't want to cancel the bottle entirely I suppose, just the exp + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onEntityDeath(EntityDeathEvent event) { + WorldConfiguration wcfg = plugin.getGlobalStateManager().get(event.getEntity().getWorld()); + + if (wcfg.disableExpDrops || !plugin.getGlobalRegionManager().allows(DefaultFlag.EXP_DROPS, + event.getEntity().getLocation())) { + event.setDroppedExp(0); + } + + if (event instanceof PlayerDeathEvent && wcfg.disableDeathMessages) { + ((PlayerDeathEvent) event).setDeathMessage(""); + } + } + + private void onEntityDamageByBlock(EntityDamageByBlockEvent event) { + Entity defender = event.getEntity(); + DamageCause type = event.getCause(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(defender.getWorld()); + + if (defender instanceof Wolf && ((Wolf) defender).isTamed()) { + if (wcfg.antiWolfDumbness && !(type == DamageCause.VOID)) { + event.setCancelled(true); + return; + } + } else if (defender instanceof Player) { + Player player = (Player) defender; + + if (isInvincible(player)) { + event.setCancelled(true); + return; + } + + if (wcfg.disableLavaDamage && type == DamageCause.LAVA) { + event.setCancelled(true); + player.setFireTicks(0); + return; + } + + if (wcfg.disableContactDamage && type == DamageCause.CONTACT) { + event.setCancelled(true); + return; + } + + if (wcfg.teleportOnVoid && type == DamageCause.VOID) { + BukkitUtil.findFreePosition(player); + event.setCancelled(true); + return; + } + + if (wcfg.disableVoidDamage && type == DamageCause.VOID) { + event.setCancelled(true); + return; + } + + if (type == DamageCause.BLOCK_EXPLOSION + && (wcfg.disableExplosionDamage || wcfg.blockOtherExplosions + || (wcfg.explosionFlagCancellation + && !plugin.getGlobalRegionManager().allows(DefaultFlag.OTHER_EXPLOSION, player.getLocation())))) { + event.setCancelled(true); + return; + } + } else { + + // for whatever reason, plugin-caused explosions with a null entity count as block explosions and aren't + // handled anywhere else + if (type == DamageCause.BLOCK_EXPLOSION + && (wcfg.blockOtherExplosions + || (wcfg.explosionFlagCancellation + && !plugin.getGlobalRegionManager().allows(DefaultFlag.OTHER_EXPLOSION, defender.getLocation())))) { + event.setCancelled(true); + return; + } + } + } + + private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + + if (event.getDamager() instanceof Projectile) { + onEntityDamageByProjectile(event); + return; + } + + Entity attacker = event.getDamager(); + Entity defender = event.getEntity(); + + if (defender instanceof ItemFrame) { + if (checkItemFrameProtection(attacker, (ItemFrame) defender)) { + event.setCancelled(true); + return; + } + } + + if (defender instanceof Player) { + Player player = (Player) defender; + LocalPlayer localPlayer = plugin.wrapPlayer(player); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(player.getWorld()); + + if (isInvincible(player)) { + if (wcfg.regionInvinciblityRemovesMobs + && attacker instanceof LivingEntity && !(attacker instanceof Player) + && !(attacker instanceof Tameable && ((Tameable) attacker).isTamed())) { + attacker.remove(); + } + + event.setCancelled(true); + return; + } + + if (wcfg.disableLightningDamage && event.getCause() == DamageCause.LIGHTNING) { + event.setCancelled(true); + return; + } + + if (wcfg.disableExplosionDamage) { + switch (event.getCause()) { + case BLOCK_EXPLOSION: + case ENTITY_EXPLOSION: + event.setCancelled(true); + return; + } + } + + if (attacker != null) { + if (attacker instanceof Player) { + if (wcfg.useRegions) { + Vector pt = toVector(defender.getLocation()); + Vector pt2 = toVector(attacker.getLocation()); + RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); + + if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer((Player) attacker))) { + tryCancelPVPEvent((Player) attacker, player, event, true); + } else if (!mgr.getApplicableRegions(pt).allows(DefaultFlag.PVP ,localPlayer)) { + tryCancelPVPEvent((Player) attacker, player, event, false); + } + } + } + + if (attacker instanceof TNTPrimed || attacker instanceof ExplosiveMinecart) { + + // The check for explosion damage should be handled already... But... What ever... + if (wcfg.blockTNTExplosions) { + event.setCancelled(true); + return; + } + if (wcfg.useRegions && wcfg.explosionFlagCancellation) { + Vector pt = toVector(defender.getLocation()); + RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + if (!set.allows(DefaultFlag.TNT, localPlayer)) { + event.setCancelled(true); + return; + } + } + } + + if (attacker instanceof Fireball) { + if (attacker instanceof WitherSkull) { + if (wcfg.blockWitherSkullExplosions) { + event.setCancelled(true); + return; + } + } else { + if (wcfg.blockFireballExplosions) { + event.setCancelled(true); + return; + } + } + if (wcfg.useRegions) { + Fireball fireball = (Fireball) attacker; + Vector pt = toVector(defender.getLocation()); + RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + if (fireball.getShooter() instanceof Player) { + Vector pt2 = toVector(((Player) fireball.getShooter()).getLocation()); + if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer((Player) fireball.getShooter()))) { + tryCancelPVPEvent((Player) fireball.getShooter(), player, event, true); + } else if (!set.allows(DefaultFlag.PVP, localPlayer)) { + tryCancelPVPEvent((Player) fireball.getShooter(), player, event, false); + } + } else { + if (!set.allows(DefaultFlag.GHAST_FIREBALL, localPlayer) && wcfg.explosionFlagCancellation) { + event.setCancelled(true); + return; + } + } + + } + } + + if (attacker instanceof LivingEntity && !(attacker instanceof Player)) { + if (attacker instanceof Creeper && wcfg.blockCreeperExplosions) { + event.setCancelled(true); + return; + } + + if (wcfg.disableMobDamage) { + event.setCancelled(true); + return; + } + + if (wcfg.useRegions) { + Vector pt = toVector(defender.getLocation()); + RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + if (!set.allows(DefaultFlag.MOB_DAMAGE, localPlayer) && !(attacker instanceof Tameable)) { + event.setCancelled(true); + return; + } + + if (attacker instanceof Creeper) { + if (!set.allows(DefaultFlag.CREEPER_EXPLOSION, localPlayer) && wcfg.explosionFlagCancellation) { + event.setCancelled(true); + return; + } + } + if (attacker instanceof Tameable) { + if (((Tameable) attacker).getOwner() == null) { + if (!set.allows(DefaultFlag.MOB_DAMAGE, localPlayer)) { + event.setCancelled(true); + return; + } + } + if (!(((Tameable) attacker).getOwner() instanceof Player)) { + return; + } + Player beastMaster = (Player) ((Tameable) attacker).getOwner(); + Vector pt2 = toVector(attacker.getLocation()); + if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer(beastMaster))) { + tryCancelPVPEvent(beastMaster, player, event, true); + } else if (!set.allows(DefaultFlag.PVP, localPlayer)) { + tryCancelPVPEvent(beastMaster, player, event, false); + } + } + } + } + } + } + } + + private void onEntityDamageByProjectile(EntityDamageByEntityEvent event) { + Entity defender = event.getEntity(); + Entity attacker; + ProjectileSource source = ((Projectile) event.getDamager()).getShooter(); + if (source instanceof LivingEntity) { + attacker = (LivingEntity) source; + } else { + return; + } + + if (defender instanceof Player) { + Player player = (Player) defender; + LocalPlayer localPlayer = plugin.wrapPlayer(player); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(player.getWorld()); + + // Check Invincible + if (isInvincible(player)) { + event.setCancelled(true); + return; + } + + // Check Mob + if (attacker != null && !(attacker instanceof Player)) { + if (wcfg.disableMobDamage) { + event.setCancelled(true); + return; + } + if (wcfg.useRegions) { + Vector pt = toVector(defender.getLocation()); + RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); + + if (!mgr.getApplicableRegions(pt).allows(DefaultFlag.MOB_DAMAGE, localPlayer)) { + event.setCancelled(true); + return; + } + } + } + + // Check Player + // if (event.getDamager() instanceof EnderPearl || event.getDamager() instanceof Snowball) return; + if (attacker != null && attacker instanceof Player) { + if (event.getDamager() instanceof EnderPearl && attacker == player) return; + if (wcfg.useRegions) { + Vector pt = toVector(defender.getLocation()); + Vector pt2 = toVector(attacker.getLocation()); + RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); + + if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer((Player) attacker))) { + tryCancelPVPEvent((Player) attacker, player, event, true); + } else if (!mgr.getApplicableRegions(pt).allows(DefaultFlag.PVP, localPlayer)) { + tryCancelPVPEvent((Player) attacker, player, event, false); + } + } + } + } else if (defender instanceof ItemFrame) { + if (checkItemFrameProtection(attacker, (ItemFrame) defender)) { + event.setCancelled(true); + return; + } + } + + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onEntityDamage(EntityDamageEvent event) { + + if (event instanceof EntityDamageByEntityEvent) { + this.onEntityDamageByEntity((EntityDamageByEntityEvent) event); + return; + } else if (event instanceof EntityDamageByBlockEvent) { + this.onEntityDamageByBlock((EntityDamageByBlockEvent) event); + return; + } + + Entity defender = event.getEntity(); + DamageCause type = event.getCause(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(defender.getWorld()); + + if (defender instanceof Wolf && ((Wolf) defender).isTamed()) { + if (wcfg.antiWolfDumbness) { + event.setCancelled(true); + return; + } + } else if (defender instanceof Player) { + Player player = (Player) defender; + + if (isInvincible(player)) { + event.setCancelled(true); + player.setFireTicks(0); + return; + } + + if (type == DamageCause.WITHER) { + // wither boss DoT tick + if (wcfg.disableMobDamage) { + event.setCancelled(true); + return; + } + + if (wcfg.useRegions) { + Vector pt = toVector(defender.getLocation()); + RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + if (!set.allows(DefaultFlag.MOB_DAMAGE, plugin.wrapPlayer(player))) { + event.setCancelled(true); + return; + } + } + } + + if (type == DamageCause.DROWNING && cfg.hasAmphibiousMode(player)) { + player.setRemainingAir(player.getMaximumAir()); + event.setCancelled(true); + return; + } + + ItemStack helmet = player.getInventory().getHelmet(); + + if (type == DamageCause.DROWNING && wcfg.pumpkinScuba + && helmet != null + && (helmet.getTypeId() == BlockID.PUMPKIN + || helmet.getTypeId() == BlockID.JACKOLANTERN)) { + player.setRemainingAir(player.getMaximumAir()); + event.setCancelled(true); + return; + } + + if (wcfg.disableFallDamage && type == DamageCause.FALL) { + event.setCancelled(true); + return; + } + + if (wcfg.disableFireDamage && (type == DamageCause.FIRE + || type == DamageCause.FIRE_TICK)) { + event.setCancelled(true); + return; + } + + if (wcfg.disableDrowningDamage && type == DamageCause.DROWNING) { + player.setRemainingAir(player.getMaximumAir()); + event.setCancelled(true); + return; + } + + if (wcfg.teleportOnSuffocation && type == DamageCause.SUFFOCATION) { + BukkitUtil.findFreePosition(player); + event.setCancelled(true); + return; + } + + if (wcfg.disableSuffocationDamage && type == DamageCause.SUFFOCATION) { + event.setCancelled(true); + return; + } + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onEntityCombust(EntityCombustEvent event) { + Entity entity = event.getEntity(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(entity.getWorld()); + + if (entity instanceof Player) { + Player player = (Player) entity; + + if (cfg.hasGodMode(player) || (wcfg.useRegions && RegionQueryUtil.isInvincible(plugin, player))) { + event.setCancelled(true); + return; + } + } + } + + /* + * Called on entity explode. + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onEntityExplode(EntityExplodeEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + Location l = event.getLocation(); + World world = l.getWorld(); + WorldConfiguration wcfg = cfg.get(world); + Entity ent = event.getEntity(); + + if (cfg.activityHaltToggle) { + if (ent != null) { + ent.remove(); + } + event.setCancelled(true); + return; + } + + if (ent instanceof Creeper) { + if (wcfg.blockCreeperExplosions) { + event.setCancelled(true); + return; + } + if (wcfg.blockCreeperBlockDamage) { + event.blockList().clear(); + return; + } + + if (wcfg.useRegions) { + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + + for (Block block : event.blockList()) { + if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.CREEPER_EXPLOSION)) { + event.blockList().clear(); + if (wcfg.explosionFlagCancellation) event.setCancelled(true); + return; + } + } + } + } else if (ent instanceof EnderDragon) { + if (wcfg.blockEnderDragonBlockDamage) { + event.blockList().clear(); + return; + } + + if (wcfg.useRegions) { + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + + for (Block block : event.blockList()) { + if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.ENDERDRAGON_BLOCK_DAMAGE)) { + event.blockList().clear(); + if (wcfg.explosionFlagCancellation) event.setCancelled(true); + return; + } + } + } + } else if (ent instanceof TNTPrimed || ent instanceof ExplosiveMinecart) { + if (wcfg.blockTNTExplosions) { + event.setCancelled(true); + return; + } + if (wcfg.blockTNTBlockDamage) { + event.blockList().clear(); + return; + } + + if (wcfg.useRegions) { + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + + for (Block block : event.blockList()) { + if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.TNT)) { + event.blockList().clear(); + if (wcfg.explosionFlagCancellation) event.setCancelled(true); + return; + } + } + } + } else if (ent instanceof Fireball) { + if (ent instanceof WitherSkull) { + if (wcfg.blockWitherSkullExplosions) { + event.setCancelled(true); + return; + } + if (wcfg.blockWitherSkullBlockDamage) { + event.blockList().clear(); + return; + } + } else { + if (wcfg.blockFireballExplosions) { + event.setCancelled(true); + return; + } + if (wcfg.blockFireballBlockDamage) { + event.blockList().clear(); + return; + } + } + // allow wither skull blocking since there is no dedicated flag atm + if (wcfg.useRegions) { + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + + for (Block block : event.blockList()) { + if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.GHAST_FIREBALL)) { + event.blockList().clear(); + if (wcfg.explosionFlagCancellation) event.setCancelled(true); + return; + } + } + } + } else if (ent instanceof Wither) { + if (wcfg.blockWitherExplosions) { + event.setCancelled(true); + return; + } + if (wcfg.blockWitherBlockDamage) { + event.blockList().clear(); + return; + } + } else { + // unhandled entity + if (wcfg.blockOtherExplosions) { + event.setCancelled(true); + return; + } + if (wcfg.useRegions) { + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + for (Block block : event.blockList()) { + if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.OTHER_EXPLOSION)) { + event.blockList().clear(); + if (wcfg.explosionFlagCancellation) event.setCancelled(true); + return; + } + } + } + } + + + if (wcfg.signChestProtection) { + for (Block block : event.blockList()) { + if (wcfg.isChestProtected(block)) { + event.blockList().clear(); + return; + } + } + } + + } + + /* + * Called on explosion prime + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onExplosionPrime(ExplosionPrimeEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getEntity().getWorld()); + Entity ent = event.getEntity(); + + if (cfg.activityHaltToggle) { + ent.remove(); + event.setCancelled(true); + return; + } + + if (event.getEntityType() == EntityType.WITHER) { + if (wcfg.blockWitherExplosions) { + event.setCancelled(true); + return; + } + } else if (event.getEntityType() == EntityType.WITHER_SKULL) { + if (wcfg.blockWitherSkullExplosions) { + event.setCancelled(true); + return; + } + } else if (event.getEntityType() == EntityType.FIREBALL) { + if (wcfg.blockFireballExplosions) { + event.setCancelled(true); + return; + } + } else if (event.getEntityType() == EntityType.CREEPER) { + if (wcfg.blockCreeperExplosions) { + event.setCancelled(true); + return; + } + } else if (event.getEntityType() == EntityType.PRIMED_TNT + || event.getEntityType() == EntityType.MINECART_TNT) { + if (wcfg.blockTNTExplosions) { + event.setCancelled(true); + return; + } + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onCreatureSpawn(CreatureSpawnEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + + if (cfg.activityHaltToggle) { + event.setCancelled(true); + return; + } + + WorldConfiguration wcfg = cfg.get(event.getEntity().getWorld()); + + // allow spawning of creatures from plugins + if (!wcfg.blockPluginSpawning && event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.CUSTOM) { + return; + } + + if (wcfg.allowTamedSpawns + && event.getEntity() instanceof Tameable // nullsafe check + && ((Tameable) event.getEntity()).isTamed()) { + return; + } + + EntityType entityType = event.getEntityType(); + + if (wcfg.blockCreatureSpawn.contains(entityType)) { + event.setCancelled(true); + return; + } + + Location eventLoc = event.getLocation(); + + if (wcfg.useRegions && cfg.useRegionsCreatureSpawnEvent) { + Vector pt = toVector(eventLoc); + RegionManager mgr = plugin.getGlobalRegionManager().get(eventLoc.getWorld()); + // @TODO get victims' stacktraces and find out why it's null anyway + if (mgr == null) return; + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + if (!set.allows(DefaultFlag.MOB_SPAWNING)) { + event.setCancelled(true); + return; + } + + Set entityTypes = set.getFlag(DefaultFlag.DENY_SPAWN); + if (entityTypes != null && entityTypes.contains(entityType)) { + event.setCancelled(true); + return; + } + } + + if (wcfg.blockGroundSlimes && entityType == EntityType.SLIME + && eventLoc.getY() >= 60 + && event.getSpawnReason() == SpawnReason.NATURAL) { + event.setCancelled(true); + return; + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onCreatePortal(EntityCreatePortalEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getEntity().getWorld()); + + switch (event.getEntityType()) { + case ENDER_DRAGON: + if (wcfg.blockEnderDragonPortalCreation) event.setCancelled(true); + break; + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPigZap(PigZapEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getEntity().getWorld()); + + if (wcfg.disablePigZap) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onCreeperPower(CreeperPowerEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getEntity().getWorld()); + + if (wcfg.disableCreeperPower) { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onEntityRegainHealth(EntityRegainHealthEvent event) { + + Entity ent = event.getEntity(); + World world = ent.getWorld(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (wcfg.disableHealthRegain) { + event.setCancelled(true); + return; + } + } + + /** + * Called when an entity changes a block somehow + * + * @param event Relevant event details + */ + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onEntityChangeBlock(EntityChangeBlockEvent event) { + Entity ent = event.getEntity(); + Block block = event.getBlock(); + Location location = block.getLocation(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(ent.getWorld()); + if (ent instanceof Enderman) { + if (wcfg.disableEndermanGriefing) { + event.setCancelled(true); + return; + } + + if (wcfg.useRegions) { + if (!plugin.getGlobalRegionManager().allows(DefaultFlag.ENDER_BUILD, location)) { + event.setCancelled(true); + return; + } + } + } else if (ent.getType() == EntityType.WITHER) { + if (wcfg.blockWitherBlockDamage || wcfg.blockWitherExplosions) { + event.setCancelled(true); + return; + } + } else if (/*ent instanceof Zombie && */event instanceof EntityBreakDoorEvent) { + if (wcfg.blockZombieDoorDestruction) { + event.setCancelled(true); + return; + } + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onFoodLevelChange(FoodLevelChangeEvent event) { + if (event.getEntity() instanceof Player) { + Player player = (Player) event.getEntity(); + if (event.getFoodLevel() < player.getFoodLevel() && isInvincible(player)) { + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onPotionSplash(PotionSplashEvent event) { + Entity entity = event.getEntity(); + ThrownPotion potion = event.getPotion(); + World world = entity.getWorld(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + + GlobalRegionManager regionMan = plugin.getGlobalRegionManager(); + + int blockedEntities = 0; + for (LivingEntity e : event.getAffectedEntities()) { + if (!regionMan.allows(DefaultFlag.POTION_SPLASH, e.getLocation(), + e instanceof Player ? plugin.wrapPlayer((Player) e) : null)) { + event.setIntensity(e, 0); + ++blockedEntities; + } + } + + if (blockedEntities == event.getAffectedEntities().size()) { + event.setCancelled(true); + } + } + + /** + * Check if a player is invincible, via either god mode or region flag. If + * the region denies invincibility, the player must have an extra permission + * to override it. (worldguard.god.override-regions) + * + * @param player The player to check + * @return Whether {@code player} is invincible + */ + private boolean isInvincible(Player player) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(player.getWorld()); + + boolean god = cfg.hasGodMode(player); + if (wcfg.useRegions) { + Boolean flag = RegionQueryUtil.isAllowedInvinciblity(plugin, player); + boolean allowed = flag == null || flag; + boolean invincible = RegionQueryUtil.isInvincible(plugin, player); + + if (allowed) { + return god || invincible; + } else { + return (god && plugin.hasPermission(player, "worldguard.god.override-regions")) + || invincible; + } + } else { + return god; + } + } + + /** + * Using a DisallowedPVPEvent, notifies other plugins that WorldGuard + * wants to cancel a PvP damage event.
+ * If this event is not cancelled, the attacking player is notified that + * PvP is disabled and WorldGuard cancels the damage event. + * + * @param attackingPlayer The attacker + * @param defendingPlayer The defender + * @param event The event that caused WorldGuard to act + */ + public void tryCancelPVPEvent(final Player attackingPlayer, final Player defendingPlayer, EntityDamageByEntityEvent event, boolean aggressorTriggered) { + final DisallowedPVPEvent disallowedPVPEvent = new DisallowedPVPEvent(attackingPlayer, defendingPlayer, event); + plugin.getServer().getPluginManager().callEvent(disallowedPVPEvent); + if (!disallowedPVPEvent.isCancelled()) { + if (aggressorTriggered) attackingPlayer.sendMessage(ChatColor.DARK_RED + "You are in a no-PvP area."); + else attackingPlayer.sendMessage(ChatColor.DARK_RED + "That player is in a no-PvP area."); + event.setCancelled(true); + } + } + + /** + * Checks regions and config settings to protect items from being knocked + * out of item frames. + * @param attacker attacking entity + * @param defender item frame being damaged + * @return true if the event should be cancelled + */ + private boolean checkItemFrameProtection(Entity attacker, ItemFrame defender) { + World world = attacker.getWorld(); + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + if (wcfg.useRegions) { + // bukkit throws this event when a player attempts to remove an item from a frame + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + if (!(attacker instanceof Player)) { + if (!plugin.getGlobalRegionManager().allows( + DefaultFlag.ENTITY_ITEM_FRAME_DESTROY, defender.getLocation())) { + return true; + } + } + } + if (wcfg.blockEntityItemFrameDestroy && !(attacker instanceof Player)) { + return true; + } + return false; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardHangingListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardHangingListener.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/bukkit/WorldGuardHangingListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardHangingListener.java index 53a7f141..8a767f09 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardHangingListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardHangingListener.java @@ -1,119 +1,122 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import org.bukkit.World; -import org.bukkit.entity.Creeper; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Hanging; -import org.bukkit.entity.ItemFrame; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Painting; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.hanging.HangingBreakByEntityEvent; -import org.bukkit.event.hanging.HangingBreakEvent; -import org.bukkit.event.hanging.HangingBreakEvent.RemoveCause; -import org.bukkit.projectiles.ProjectileSource; - -/** - * Listener for painting related events. - * - * @author BangL - */ -public class WorldGuardHangingListener implements Listener { - - private WorldGuardPlugin plugin; - - /** - * Construct the object; - * - * @param plugin The plugin instance - */ - public WorldGuardHangingListener(WorldGuardPlugin plugin) { - this.plugin = plugin; - } - - /** - * Register events. - */ - public void registerEvents() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onHangingBreak(HangingBreakEvent event) { - Hanging hanging = event.getEntity(); - World world = hanging.getWorld(); - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (event instanceof HangingBreakByEntityEvent) { - HangingBreakByEntityEvent entityEvent = (HangingBreakByEntityEvent) event; - Entity removerEntity = entityEvent.getRemover(); - if (removerEntity instanceof Projectile) { - Projectile projectile = (Projectile) removerEntity; - ProjectileSource remover = projectile.getShooter(); - removerEntity = (remover instanceof LivingEntity ? (LivingEntity) remover : null); - } - - if (!(removerEntity instanceof Player)) { - if (removerEntity instanceof Creeper) { - if (wcfg.blockCreeperBlockDamage || wcfg.blockCreeperExplosions) { - event.setCancelled(true); - return; - } - if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows(DefaultFlag.CREEPER_EXPLOSION, hanging.getLocation())) { - event.setCancelled(true); - return; - } - } - - // this now covers dispensers as well, if removerEntity is null above, - // due to a non-LivingEntity ProjectileSource - if (hanging instanceof Painting - && (wcfg.blockEntityPaintingDestroy - || (wcfg.useRegions - && !plugin.getGlobalRegionManager().allows(DefaultFlag.ENTITY_PAINTING_DESTROY, hanging.getLocation())))) { - event.setCancelled(true); - } else if (hanging instanceof ItemFrame - && (wcfg.blockEntityItemFrameDestroy - || (wcfg.useRegions - && !plugin.getGlobalRegionManager().allows(DefaultFlag.ENTITY_ITEM_FRAME_DESTROY, hanging.getLocation())))) { - event.setCancelled(true); - } - } - } else { - // Explosions from mobs are not covered by HangingBreakByEntity - if (hanging instanceof Painting && wcfg.blockEntityPaintingDestroy - && event.getCause() == RemoveCause.EXPLOSION) { - event.setCancelled(true); - } else if (hanging instanceof ItemFrame && wcfg.blockEntityItemFrameDestroy - && event.getCause() == RemoveCause.EXPLOSION) { - event.setCancelled(true); - } - } - } - -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import org.bukkit.World; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Hanging; +import org.bukkit.entity.ItemFrame; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Painting; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; +import org.bukkit.event.hanging.HangingBreakEvent; +import org.bukkit.event.hanging.HangingBreakEvent.RemoveCause; +import org.bukkit.projectiles.ProjectileSource; + +/** + * Listener for painting related events. + * + * @author BangL + */ +public class WorldGuardHangingListener implements Listener { + + private WorldGuardPlugin plugin; + + /** + * Construct the object; + * + * @param plugin The plugin instance + */ + public WorldGuardHangingListener(WorldGuardPlugin plugin) { + this.plugin = plugin; + } + + /** + * Register events. + */ + public void registerEvents() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onHangingBreak(HangingBreakEvent event) { + Hanging hanging = event.getEntity(); + World world = hanging.getWorld(); + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (event instanceof HangingBreakByEntityEvent) { + HangingBreakByEntityEvent entityEvent = (HangingBreakByEntityEvent) event; + Entity removerEntity = entityEvent.getRemover(); + if (removerEntity instanceof Projectile) { + Projectile projectile = (Projectile) removerEntity; + ProjectileSource remover = projectile.getShooter(); + removerEntity = (remover instanceof LivingEntity ? (LivingEntity) remover : null); + } + + if (!(removerEntity instanceof Player)) { + if (removerEntity instanceof Creeper) { + if (wcfg.blockCreeperBlockDamage || wcfg.blockCreeperExplosions) { + event.setCancelled(true); + return; + } + if (wcfg.useRegions && !plugin.getGlobalRegionManager().allows(DefaultFlag.CREEPER_EXPLOSION, hanging.getLocation())) { + event.setCancelled(true); + return; + } + } + + // this now covers dispensers as well, if removerEntity is null above, + // due to a non-LivingEntity ProjectileSource + if (hanging instanceof Painting + && (wcfg.blockEntityPaintingDestroy + || (wcfg.useRegions + && !plugin.getGlobalRegionManager().allows(DefaultFlag.ENTITY_PAINTING_DESTROY, hanging.getLocation())))) { + event.setCancelled(true); + } else if (hanging instanceof ItemFrame + && (wcfg.blockEntityItemFrameDestroy + || (wcfg.useRegions + && !plugin.getGlobalRegionManager().allows(DefaultFlag.ENTITY_ITEM_FRAME_DESTROY, hanging.getLocation())))) { + event.setCancelled(true); + } + } + } else { + // Explosions from mobs are not covered by HangingBreakByEntity + if (hanging instanceof Painting && wcfg.blockEntityPaintingDestroy + && event.getCause() == RemoveCause.EXPLOSION) { + event.setCancelled(true); + } else if (hanging instanceof ItemFrame && wcfg.blockEntityItemFrameDestroy + && event.getCause() == RemoveCause.EXPLOSION) { + event.setCancelled(true); + } + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlayerListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java similarity index 97% rename from src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlayerListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java index df660abc..6b4ce0eb 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlayerListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java @@ -1,729 +1,733 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.blocks.BlockID; -import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.blacklist.event.ItemUseBlacklistEvent; -import com.sk89q.worldguard.bukkit.FlagStateManager.PlayerFlagState; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.util.command.CommandFilter; -import org.bukkit.ChatColor; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.event.player.PlayerBedEnterEvent; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; -import org.bukkit.event.player.PlayerDropItemEvent; -import org.bukkit.event.player.PlayerFishEvent; -import org.bukkit.event.player.PlayerGameModeChangeEvent; -import org.bukkit.event.player.PlayerInteractEntityEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.event.player.PlayerItemHeldEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerLoginEvent; -import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.player.PlayerRespawnEvent; -import org.bukkit.event.player.PlayerTeleportEvent; -import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.PluginManager; - -import java.util.Iterator; -import java.util.Set; -import java.util.logging.Level; -import java.util.regex.Pattern; - -import static com.sk89q.worldguard.bukkit.BukkitUtil.createTarget; -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - -/** - * Handles all events thrown in relation to a player. - */ -public class WorldGuardPlayerListener implements Listener { - - private Pattern opPattern = Pattern.compile("^/op(?:\\s.*)?$", Pattern.CASE_INSENSITIVE); - private WorldGuardPlugin plugin; - - /** - * Construct the object; - * - * @param plugin - */ - public WorldGuardPlayerListener(WorldGuardPlugin plugin) { - this.plugin = plugin; - } - - /** - * Register events. - */ - public void registerEvents() { - final PluginManager pm = plugin.getServer().getPluginManager(); - pm.registerEvents(this, plugin); - - if (plugin.getGlobalStateManager().usePlayerMove) { - pm.registerEvents(new PlayerMoveHandler(), plugin); - } - } - - // unsure if anyone actually started using this yet, but just in case... - @Deprecated - public static boolean checkMove(WorldGuardPlugin plugin, Player player, World world, Location from, Location to) { - return checkMove(plugin, player, from, to); // drop world since it used to be mishandled - } - - /** - * Handles movement related events, including changing gamemode, sending - * greeting/farewell messages, etc. - * A reference to WorldGuardPlugin is required to keep this method static - * although WGBukkit.getPlugin() may be used. - * @return true if the movement should not be allowed - */ - public static boolean checkMove(WorldGuardPlugin plugin, Player player, Location from, Location to) { - PlayerFlagState state = plugin.getFlagStateManager().getState(player); - - //Flush states in multiworld scenario - if (state.lastWorld != null && !state.lastWorld.equals(to.getWorld())) { - plugin.getFlagStateManager().forget(player); - state = plugin.getFlagStateManager().getState(player); - } - - World world = from.getWorld(); - World toWorld = to.getWorld(); - - LocalPlayer localPlayer = plugin.wrapPlayer(player); - boolean hasBypass = plugin.getGlobalRegionManager().hasBypass(player, world); - boolean hasRemoteBypass; - if (world.equals(toWorld)) { - hasRemoteBypass = hasBypass; - } else { - hasRemoteBypass = plugin.getGlobalRegionManager().hasBypass(player, toWorld); - } - - RegionManager mgr = plugin.getGlobalRegionManager().get(toWorld); - Vector pt = new Vector(to.getBlockX(), to.getBlockY(), to.getBlockZ()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - /* - // check if region is full - // get the lowest number of allowed members in any region - boolean regionFull = false; - String maxPlayerMessage = null; - if (!hasBypass) { - for (ProtectedRegion region : set) { - if (region instanceof GlobalProtectedRegion) { - continue; // global region can't have a max - } - // get the max for just this region - Integer maxPlayers = region.getFlag(DefaultFlag.MAX_PLAYERS); - if (maxPlayers == null) { - continue; - } - int occupantCount = 0; - for(Player occupant : world.getPlayers()) { - // each player in this region counts as one toward the max of just this region - // A person with bypass doesn't count as an occupant of the region - if (!occupant.equals(player) && !plugin.getGlobalRegionManager().hasBypass(occupant, world)) { - if (region.contains(BukkitUtil.toVector(occupant.getLocation()))) { - if (++occupantCount >= maxPlayers) { - regionFull = true; - maxPlayerMessage = region.getFlag(DefaultFlag.MAX_PLAYERS_MESSAGE); - // At least one region in the set is full, we are going to use this message because it - // was the first one we detected as full. In reality we should check them all and then - // resolve the message from full regions, but that is probably a lot laggier (and this - // is already pretty laggy. In practice, we can't really control which one we get first - // right here. - break; - } - } - } - } - } - } - */ - - boolean entryAllowed = set.allows(DefaultFlag.ENTRY, localPlayer); - if (!hasRemoteBypass && (!entryAllowed /*|| regionFull*/)) { - String message = /*maxPlayerMessage != null ? maxPlayerMessage :*/ "You are not permitted to enter this area."; - - player.sendMessage(ChatColor.DARK_RED + message); - return true; - } - - // Have to set this state - if (state.lastExitAllowed == null) { - state.lastExitAllowed = plugin.getGlobalRegionManager().get(world) - .getApplicableRegions(toVector(from)) - .allows(DefaultFlag.EXIT, localPlayer); - } - - boolean exitAllowed = set.allows(DefaultFlag.EXIT, localPlayer); - if (!hasBypass && exitAllowed && !state.lastExitAllowed) { - player.sendMessage(ChatColor.DARK_RED + "You are not permitted to leave this area."); - return true; - } - -// WorldGuardRegionMoveEvent event = new WorldGuardRegionMoveEvent(plugin, player, state, set, from, to); -// Bukkit.getPluginManager().callEvent(event); - - String greeting = set.getFlag(DefaultFlag.GREET_MESSAGE);//, localPlayer); - String farewell = set.getFlag(DefaultFlag.FAREWELL_MESSAGE);//, localPlayer); - Boolean notifyEnter = set.getFlag(DefaultFlag.NOTIFY_ENTER);//, localPlayer); - Boolean notifyLeave = set.getFlag(DefaultFlag.NOTIFY_LEAVE);//, localPlayer); - GameMode gameMode = set.getFlag(DefaultFlag.GAME_MODE); - - if (state.lastFarewell != null && (farewell == null - || !state.lastFarewell.equals(farewell))) { - String replacedFarewell = plugin.replaceMacros( - player, BukkitUtil.replaceColorMacros(state.lastFarewell)); - player.sendMessage(replacedFarewell.replaceAll("\\\\n", "\n").split("\\n")); - } - - if (greeting != null && (state.lastGreeting == null - || !state.lastGreeting.equals(greeting))) { - String replacedGreeting = plugin.replaceMacros( - player, BukkitUtil.replaceColorMacros(greeting)); - player.sendMessage(replacedGreeting.replaceAll("\\\\n", "\n").split("\\n")); - } - - if ((notifyLeave == null || !notifyLeave) - && state.notifiedForLeave != null && state.notifiedForLeave) { - plugin.broadcastNotification(ChatColor.GRAY + "WG: " - + ChatColor.LIGHT_PURPLE + player.getName() - + ChatColor.GOLD + " left NOTIFY region"); - } - - if (notifyEnter != null && notifyEnter && (state.notifiedForEnter == null - || !state.notifiedForEnter)) { - StringBuilder regionList = new StringBuilder(); - - for (ProtectedRegion region : set) { - if (regionList.length() != 0) { - regionList.append(", "); - } - regionList.append(region.getId()); - } - - plugin.broadcastNotification(ChatColor.GRAY + "WG: " - + ChatColor.LIGHT_PURPLE + player.getName() - + ChatColor.GOLD + " entered NOTIFY region: " - + ChatColor.WHITE - + regionList); - } - - if (!hasBypass && gameMode != null) { - if (player.getGameMode() != gameMode) { - state.lastGameMode = player.getGameMode(); - player.setGameMode(gameMode); - } else if (state.lastGameMode == null) { - state.lastGameMode = player.getServer().getDefaultGameMode(); - } - } else { - if (state.lastGameMode != null) { - GameMode mode = state.lastGameMode; - state.lastGameMode = null; - player.setGameMode(mode); - } - } - - state.lastGreeting = greeting; - state.lastFarewell = farewell; - state.notifiedForEnter = notifyEnter; - state.notifiedForLeave = notifyLeave; - state.lastExitAllowed = exitAllowed; - state.lastWorld = to.getWorld(); - state.lastBlockX = to.getBlockX(); - state.lastBlockY = to.getBlockY(); - state.lastBlockZ = to.getBlockZ(); - return false; - } - - class PlayerMoveHandler implements Listener { - @EventHandler(priority = EventPriority.HIGH) - public void onPlayerMove(PlayerMoveEvent event) { - Player player = event.getPlayer(); - World world = player.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (player.getVehicle() != null) { - return; // handled in vehicle listener - } - if (wcfg.useRegions) { - // Did we move a block? - if (event.getFrom().getBlockX() != event.getTo().getBlockX() - || event.getFrom().getBlockY() != event.getTo().getBlockY() - || event.getFrom().getBlockZ() != event.getTo().getBlockZ()) { - boolean result = checkMove(plugin, player, event.getFrom(), event.getTo()); - if (result) { - Location newLoc = event.getFrom(); - newLoc.setX(newLoc.getBlockX() + 0.5); - newLoc.setY(newLoc.getBlockY()); - newLoc.setZ(newLoc.getBlockZ() + 0.5); - event.setTo(newLoc); - } - } - } - } - } - - @EventHandler - public void onPlayerGameModeChange(PlayerGameModeChangeEvent event) { - Player player = event.getPlayer(); - WorldConfiguration wcfg = plugin.getGlobalStateManager().get(player.getWorld()); - if (wcfg.useRegions && !plugin.getGlobalRegionManager().hasBypass(player, player.getWorld())) { - GameMode gameMode = plugin.getGlobalRegionManager().get(player.getWorld()) - .getApplicableRegions(player.getLocation()).getFlag(DefaultFlag.GAME_MODE); - if (plugin.getFlagStateManager().getState(player).lastGameMode != null - && gameMode != null && event.getNewGameMode() != gameMode) { - event.setCancelled(true); - } - } - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - Player player = event.getPlayer(); - World world = player.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (cfg.activityHaltToggle) { - player.sendMessage(ChatColor.YELLOW - + "Intensive server activity has been HALTED."); - - int removed = 0; - - for (Entity entity : world.getEntities()) { - if (BukkitUtil.isIntensiveEntity(entity)) { - entity.remove(); - removed++; - } - } - - if (removed > 10) { - plugin.getLogger().info("Halt-Act: " + removed + " entities (>10) auto-removed from " - + player.getWorld().toString()); - } - } - - if (wcfg.fireSpreadDisableToggle) { - player.sendMessage(ChatColor.YELLOW - + "Fire spread is currently globally disabled for this world."); - } - - if (!cfg.hasCommandBookGodMode() && cfg.autoGodMode && (plugin.inGroup(player, "wg-invincible") - || plugin.hasPermission(player, "worldguard.auto-invincible"))) { - plugin.getLogger().log(Level.INFO, "Enabled auto-god mode for " + player.getName()); - cfg.enableGodMode(player); - } - - if (plugin.inGroup(player, "wg-amphibious")) { - plugin.getLogger().log(Level.INFO, "Enabled no-drowning mode for " + player.getName() + " (player is in group 'wg-amphibious')"); - cfg.enableAmphibiousMode(player); - } - - if (wcfg.useRegions) { - PlayerFlagState state = plugin.getFlagStateManager().getState(player); - Location loc = player.getLocation(); - state.lastWorld = loc.getWorld(); - state.lastBlockX = loc.getBlockX(); - state.lastBlockY = loc.getBlockY(); - state.lastBlockZ = loc.getBlockZ(); - } - } - - @EventHandler(ignoreCancelled = true) - public void onPlayerChat(AsyncPlayerChatEvent event) { - Player player = event.getPlayer(); - WorldConfiguration wcfg = plugin.getGlobalStateManager().get(player.getWorld()); - if (wcfg.useRegions) { - if (!plugin.getGlobalRegionManager().allows(DefaultFlag.SEND_CHAT, player.getLocation())) { - player.sendMessage(ChatColor.RED + "You don't have permission to chat in this region!"); - event.setCancelled(true); - return; - } - - for (Iterator i = event.getRecipients().iterator(); i.hasNext();) { - if (!plugin.getGlobalRegionManager().allows(DefaultFlag.RECEIVE_CHAT, i.next().getLocation())) { - i.remove(); - } - } - if (event.getRecipients().size() == 0) { - event.setCancelled(true); - } - } - } - - @EventHandler(ignoreCancelled = true) - public void onPlayerLogin(PlayerLoginEvent event) { - Player player = event.getPlayer(); - ConfigurationManager cfg = plugin.getGlobalStateManager(); - - String hostKey = cfg.hostKeys.get(player.getName().toLowerCase()); - if (hostKey != null) { - String hostname = event.getHostname(); - int colonIndex = hostname.indexOf(':'); - if (colonIndex != -1) { - hostname = hostname.substring(0, colonIndex); - } - - if (!hostname.equals(hostKey)) { - event.disallow(PlayerLoginEvent.Result.KICK_OTHER, - "You did not join with the valid host key!"); - plugin.getLogger().warning("WorldGuard host key check: " + - player.getName() + " joined with '" + hostname + - "' but '" + hostKey + "' was expected. Kicked!"); - return; - } - } - - if (cfg.deopOnJoin) { - player.setOp(false); - } - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - Player player = event.getPlayer(); - World world = player.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - // This is to make the enter/exit flags accurate -- move events are not - // sent constantly, so it is possible to move just a little enough to - // not trigger the event and then rejoin so that you are then considered - // outside the border. This should work around that. - if (wcfg.useRegions) { - boolean hasBypass = plugin.getGlobalRegionManager().hasBypass(player, world); - PlayerFlagState state = plugin.getFlagStateManager().getState(player); - - if (state.lastWorld != null && !hasBypass) { - LocalPlayer localPlayer = plugin.wrapPlayer(player); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - Location loc = player.getLocation(); - Vector pt = new Vector(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - if (state.lastExitAllowed == null) { - state.lastExitAllowed = set.allows(DefaultFlag.EXIT, localPlayer); - } - - if (!state.lastExitAllowed || !set.allows(DefaultFlag.ENTRY, localPlayer)) { - // Only if we have the last location cached - if (state.lastWorld.equals(world)) { - Location newLoc = new Location(world, state.lastBlockX + 0.5, - state.lastBlockY, state.lastBlockZ + 0.5); - player.teleport(newLoc); - } - } - } - } - - cfg.forgetPlayer(plugin.wrapPlayer(player)); - plugin.forgetPlayer(player); - } - - @EventHandler(priority = EventPriority.HIGH) - public void onPlayerInteract(PlayerInteractEvent event) { - Player player = event.getPlayer(); - World world = player.getWorld(); - - if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { - handleBlockRightClick(event); - } else if (event.getAction() == Action.PHYSICAL) { - handlePhysicalInteract(event); - } - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.removeInfiniteStacks - && !plugin.hasPermission(player, "worldguard.override.infinite-stack")) { - int slot = player.getInventory().getHeldItemSlot(); - ItemStack heldItem = player.getInventory().getItem(slot); - if (heldItem != null && heldItem.getAmount() < 0) { - player.getInventory().setItem(slot, null); - player.sendMessage(ChatColor.RED + "Infinite stack removed."); - } - } - } - - /** - * Called when a player right clicks a block. - * - * @param event Thrown event - */ - private void handleBlockRightClick(PlayerInteractEvent event) { - if (event.isCancelled()) { - return; - } - - Block block = event.getClickedBlock(); - World world = block.getWorld(); - int type = block.getTypeId(); - Player player = event.getPlayer(); - ItemStack item = player.getItemInHand(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - // Infinite stack removal - if ((type == BlockID.CHEST - || type == BlockID.JUKEBOX - || type == BlockID.DISPENSER - || type == BlockID.FURNACE - || type == BlockID.BURNING_FURNACE - || type == BlockID.BREWING_STAND - || type == BlockID.ENCHANTMENT_TABLE) - && wcfg.removeInfiniteStacks - && !plugin.hasPermission(player, "worldguard.override.infinite-stack")) { - for (int slot = 0; slot < 40; slot++) { - ItemStack heldItem = player.getInventory().getItem(slot); - if (heldItem != null && heldItem.getAmount() < 0) { - player.getInventory().setItem(slot, null); - player.sendMessage(ChatColor.RED + "Infinite stack in slot #" + slot + " removed."); - } - } - } - - if (wcfg.useRegions) { - Vector pt = toVector(block); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - Block placedIn = block.getRelative(event.getBlockFace()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - ApplicableRegionSet placedInSet = mgr.getApplicableRegions(placedIn.getLocation()); - LocalPlayer localPlayer = plugin.wrapPlayer(player); - - if (item.getTypeId() == wcfg.regionWand && plugin.hasPermission(player, "worldguard.region.wand")) { - if (set.size() > 0) { - player.sendMessage(ChatColor.YELLOW + "Can you build? " - + (set.canBuild(localPlayer) ? "Yes" : "No")); - - StringBuilder str = new StringBuilder(); - for (Iterator it = set.iterator(); it.hasNext();) { - str.append(it.next().getId()); - if (it.hasNext()) { - str.append(", "); - } - } - - player.sendMessage(ChatColor.YELLOW + "Applicable regions: " + str.toString()); - } else { - player.sendMessage(ChatColor.YELLOW + "WorldGuard: No defined regions here!"); - } - - event.setCancelled(true); - } - } - } - - /** - * Called when a player steps on a pressure plate or tramples crops. - * - * @param event Thrown event - */ - private void handlePhysicalInteract(PlayerInteractEvent event) { - if (event.isCancelled()) return; - - Player player = event.getPlayer(); - Block block = event.getClickedBlock(); //not actually clicked but whatever - int type = block.getTypeId(); - World world = player.getWorld(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (block.getTypeId() == BlockID.SOIL && wcfg.disablePlayerCropTrampling) { - event.setCancelled(true); - return; - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPlayerDropItem(PlayerDropItemEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getPlayer().getWorld()); - Player player = event.getPlayer(); - - if (wcfg.useRegions) { - if (!plugin.getGlobalRegionManager().hasBypass(player, player.getWorld()) - && !plugin.getGlobalRegionManager().allows(DefaultFlag.ITEM_DROP, player.getLocation())) { - event.setCancelled(true); - player.sendMessage(ChatColor.RED + "You don't have permission to do that in this area."); - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPlayerFish(PlayerFishEvent event) { - WorldConfiguration wcfg = plugin.getGlobalStateManager().get(event.getPlayer().getWorld()); - - if (wcfg.disableExpDrops || !plugin.getGlobalRegionManager().allows(DefaultFlag.EXP_DROPS, - event.getPlayer().getLocation())) { - event.setExpToDrop(0); - } - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerRespawn(PlayerRespawnEvent event) { - Player player = event.getPlayer(); - Location location = player.getLocation(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(player.getWorld()); - - if (wcfg.useRegions) { - Vector pt = toVector(location); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - LocalPlayer localPlayer = plugin.wrapPlayer(player); - com.sk89q.worldedit.Location spawn = set.getFlag(DefaultFlag.SPAWN_LOC, localPlayer); - - if (spawn != null) { - event.setRespawnLocation(com.sk89q.worldedit.bukkit.BukkitUtil.toLocation(spawn)); - } - } - } - - @EventHandler(priority = EventPriority.HIGH) - public void onItemHeldChange(PlayerItemHeldEvent event) { - Player player = event.getPlayer(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(player.getWorld()); - - if (wcfg.removeInfiniteStacks - && !plugin.hasPermission(player, "worldguard.override.infinite-stack")) { - int newSlot = event.getNewSlot(); - ItemStack heldItem = player.getInventory().getItem(newSlot); - if (heldItem != null && heldItem.getAmount() < 0) { - player.getInventory().setItem(newSlot, null); - player.sendMessage(ChatColor.RED + "Infinite stack removed."); - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPlayerBedEnter(PlayerBedEnterEvent event) { - Player player = event.getPlayer(); - Location location = player.getLocation(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(player.getWorld()); - - if (wcfg.useRegions) { - Vector pt = toVector(location); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - if (!plugin.getGlobalRegionManager().hasBypass(player, player.getWorld()) - && !set.allows(DefaultFlag.SLEEP, plugin.wrapPlayer(player))) { - event.setCancelled(true); - player.sendMessage("This bed doesn't belong to you!"); - return; - } - } - } - - @EventHandler(priority= EventPriority.LOW, ignoreCancelled = true) - public void onPlayerTeleport(PlayerTeleportEvent event) { - World world = event.getFrom().getWorld(); - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(event.getFrom().getWorld()); - Vector pt = new Vector(event.getTo().getBlockX(), event.getTo().getBlockY(), event.getTo().getBlockZ()); - Vector ptFrom = new Vector(event.getFrom().getBlockX(), event.getFrom().getBlockY(), event.getFrom().getBlockZ()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - ApplicableRegionSet setFrom = mgr.getApplicableRegions(ptFrom); - LocalPlayer localPlayer = plugin.wrapPlayer(event.getPlayer()); - - if (cfg.usePlayerTeleports) { - boolean result = checkMove(plugin, event.getPlayer(), event.getFrom(), event.getTo()); - if (result) { - event.setCancelled(true); - return; - } - } - - if (event.getCause() == TeleportCause.ENDER_PEARL) { - if (!plugin.getGlobalRegionManager().hasBypass(localPlayer, world) - && !(set.allows(DefaultFlag.ENDERPEARL, localPlayer) - && setFrom.allows(DefaultFlag.ENDERPEARL, localPlayer))) { - event.getPlayer().sendMessage(ChatColor.DARK_RED + "You're not allowed to go there."); - event.setCancelled(true); - return; - } - } - } - } - - @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { - Player player = event.getPlayer(); - LocalPlayer localPlayer = plugin.wrapPlayer(player); - World world = player.getWorld(); - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.useRegions && !plugin.getGlobalRegionManager().hasBypass(player, world)) { - Vector pt = toVector(player.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - Set allowedCommands = set.getFlag(DefaultFlag.ALLOWED_CMDS, localPlayer); - Set blockedCommands = set.getFlag(DefaultFlag.BLOCKED_CMDS, localPlayer); - CommandFilter test = new CommandFilter(allowedCommands, blockedCommands); - - if (!test.apply(event.getMessage())) { - player.sendMessage(ChatColor.RED + event.getMessage() + " is not allowed in this area."); - event.setCancelled(true); - return; - } - } - - if (cfg.blockInGameOp) { - if (opPattern.matcher(event.getMessage()).matches()) { - player.sendMessage(ChatColor.RED + "/op can only be used in console (as set by a WG setting)."); - event.setCancelled(true); - return; - } - } - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BlockID; +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.blacklist.event.ItemUseBlacklistEvent; +import com.sk89q.worldguard.bukkit.BukkitUtil; +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.FlagStateManager.PlayerFlagState; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.command.CommandFilter; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerBedEnterEvent; +import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.player.PlayerGameModeChangeEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.PluginManager; + +import java.util.Iterator; +import java.util.Set; +import java.util.logging.Level; +import java.util.regex.Pattern; + +import static com.sk89q.worldguard.bukkit.BukkitUtil.createTarget; +import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; + +/** + * Handles all events thrown in relation to a player. + */ +public class WorldGuardPlayerListener implements Listener { + + private Pattern opPattern = Pattern.compile("^/op(?:\\s.*)?$", Pattern.CASE_INSENSITIVE); + private WorldGuardPlugin plugin; + + /** + * Construct the object; + * + * @param plugin + */ + public WorldGuardPlayerListener(WorldGuardPlugin plugin) { + this.plugin = plugin; + } + + /** + * Register events. + */ + public void registerEvents() { + final PluginManager pm = plugin.getServer().getPluginManager(); + pm.registerEvents(this, plugin); + + if (plugin.getGlobalStateManager().usePlayerMove) { + pm.registerEvents(new PlayerMoveHandler(), plugin); + } + } + + // unsure if anyone actually started using this yet, but just in case... + @Deprecated + public static boolean checkMove(WorldGuardPlugin plugin, Player player, World world, Location from, Location to) { + return checkMove(plugin, player, from, to); // drop world since it used to be mishandled + } + + /** + * Handles movement related events, including changing gamemode, sending + * greeting/farewell messages, etc. + * A reference to WorldGuardPlugin is required to keep this method static + * although WGBukkit.getPlugin() may be used. + * @return true if the movement should not be allowed + */ + public static boolean checkMove(WorldGuardPlugin plugin, Player player, Location from, Location to) { + PlayerFlagState state = plugin.getFlagStateManager().getState(player); + + //Flush states in multiworld scenario + if (state.lastWorld != null && !state.lastWorld.equals(to.getWorld())) { + plugin.getFlagStateManager().forget(player); + state = plugin.getFlagStateManager().getState(player); + } + + World world = from.getWorld(); + World toWorld = to.getWorld(); + + LocalPlayer localPlayer = plugin.wrapPlayer(player); + boolean hasBypass = plugin.getGlobalRegionManager().hasBypass(player, world); + boolean hasRemoteBypass; + if (world.equals(toWorld)) { + hasRemoteBypass = hasBypass; + } else { + hasRemoteBypass = plugin.getGlobalRegionManager().hasBypass(player, toWorld); + } + + RegionManager mgr = plugin.getGlobalRegionManager().get(toWorld); + Vector pt = new Vector(to.getBlockX(), to.getBlockY(), to.getBlockZ()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + /* + // check if region is full + // get the lowest number of allowed members in any region + boolean regionFull = false; + String maxPlayerMessage = null; + if (!hasBypass) { + for (ProtectedRegion region : set) { + if (region instanceof GlobalProtectedRegion) { + continue; // global region can't have a max + } + // get the max for just this region + Integer maxPlayers = region.getFlag(DefaultFlag.MAX_PLAYERS); + if (maxPlayers == null) { + continue; + } + int occupantCount = 0; + for(Player occupant : world.getPlayers()) { + // each player in this region counts as one toward the max of just this region + // A person with bypass doesn't count as an occupant of the region + if (!occupant.equals(player) && !plugin.getGlobalRegionManager().hasBypass(occupant, world)) { + if (region.contains(BukkitUtil.toVector(occupant.getLocation()))) { + if (++occupantCount >= maxPlayers) { + regionFull = true; + maxPlayerMessage = region.getFlag(DefaultFlag.MAX_PLAYERS_MESSAGE); + // At least one region in the set is full, we are going to use this message because it + // was the first one we detected as full. In reality we should check them all and then + // resolve the message from full regions, but that is probably a lot laggier (and this + // is already pretty laggy. In practice, we can't really control which one we get first + // right here. + break; + } + } + } + } + } + } + */ + + boolean entryAllowed = set.allows(DefaultFlag.ENTRY, localPlayer); + if (!hasRemoteBypass && (!entryAllowed /*|| regionFull*/)) { + String message = /*maxPlayerMessage != null ? maxPlayerMessage :*/ "You are not permitted to enter this area."; + + player.sendMessage(ChatColor.DARK_RED + message); + return true; + } + + // Have to set this state + if (state.lastExitAllowed == null) { + state.lastExitAllowed = plugin.getGlobalRegionManager().get(world) + .getApplicableRegions(toVector(from)) + .allows(DefaultFlag.EXIT, localPlayer); + } + + boolean exitAllowed = set.allows(DefaultFlag.EXIT, localPlayer); + if (!hasBypass && exitAllowed && !state.lastExitAllowed) { + player.sendMessage(ChatColor.DARK_RED + "You are not permitted to leave this area."); + return true; + } + +// WorldGuardRegionMoveEvent event = new WorldGuardRegionMoveEvent(plugin, player, state, set, from, to); +// Bukkit.getPluginManager().callEvent(event); + + String greeting = set.getFlag(DefaultFlag.GREET_MESSAGE);//, localPlayer); + String farewell = set.getFlag(DefaultFlag.FAREWELL_MESSAGE);//, localPlayer); + Boolean notifyEnter = set.getFlag(DefaultFlag.NOTIFY_ENTER);//, localPlayer); + Boolean notifyLeave = set.getFlag(DefaultFlag.NOTIFY_LEAVE);//, localPlayer); + GameMode gameMode = set.getFlag(DefaultFlag.GAME_MODE); + + if (state.lastFarewell != null && (farewell == null + || !state.lastFarewell.equals(farewell))) { + String replacedFarewell = plugin.replaceMacros( + player, BukkitUtil.replaceColorMacros(state.lastFarewell)); + player.sendMessage(replacedFarewell.replaceAll("\\\\n", "\n").split("\\n")); + } + + if (greeting != null && (state.lastGreeting == null + || !state.lastGreeting.equals(greeting))) { + String replacedGreeting = plugin.replaceMacros( + player, BukkitUtil.replaceColorMacros(greeting)); + player.sendMessage(replacedGreeting.replaceAll("\\\\n", "\n").split("\\n")); + } + + if ((notifyLeave == null || !notifyLeave) + && state.notifiedForLeave != null && state.notifiedForLeave) { + plugin.broadcastNotification(ChatColor.GRAY + "WG: " + + ChatColor.LIGHT_PURPLE + player.getName() + + ChatColor.GOLD + " left NOTIFY region"); + } + + if (notifyEnter != null && notifyEnter && (state.notifiedForEnter == null + || !state.notifiedForEnter)) { + StringBuilder regionList = new StringBuilder(); + + for (ProtectedRegion region : set) { + if (regionList.length() != 0) { + regionList.append(", "); + } + regionList.append(region.getId()); + } + + plugin.broadcastNotification(ChatColor.GRAY + "WG: " + + ChatColor.LIGHT_PURPLE + player.getName() + + ChatColor.GOLD + " entered NOTIFY region: " + + ChatColor.WHITE + + regionList); + } + + if (!hasBypass && gameMode != null) { + if (player.getGameMode() != gameMode) { + state.lastGameMode = player.getGameMode(); + player.setGameMode(gameMode); + } else if (state.lastGameMode == null) { + state.lastGameMode = player.getServer().getDefaultGameMode(); + } + } else { + if (state.lastGameMode != null) { + GameMode mode = state.lastGameMode; + state.lastGameMode = null; + player.setGameMode(mode); + } + } + + state.lastGreeting = greeting; + state.lastFarewell = farewell; + state.notifiedForEnter = notifyEnter; + state.notifiedForLeave = notifyLeave; + state.lastExitAllowed = exitAllowed; + state.lastWorld = to.getWorld(); + state.lastBlockX = to.getBlockX(); + state.lastBlockY = to.getBlockY(); + state.lastBlockZ = to.getBlockZ(); + return false; + } + + class PlayerMoveHandler implements Listener { + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerMove(PlayerMoveEvent event) { + Player player = event.getPlayer(); + World world = player.getWorld(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (player.getVehicle() != null) { + return; // handled in vehicle listener + } + if (wcfg.useRegions) { + // Did we move a block? + if (event.getFrom().getBlockX() != event.getTo().getBlockX() + || event.getFrom().getBlockY() != event.getTo().getBlockY() + || event.getFrom().getBlockZ() != event.getTo().getBlockZ()) { + boolean result = checkMove(plugin, player, event.getFrom(), event.getTo()); + if (result) { + Location newLoc = event.getFrom(); + newLoc.setX(newLoc.getBlockX() + 0.5); + newLoc.setY(newLoc.getBlockY()); + newLoc.setZ(newLoc.getBlockZ() + 0.5); + event.setTo(newLoc); + } + } + } + } + } + + @EventHandler + public void onPlayerGameModeChange(PlayerGameModeChangeEvent event) { + Player player = event.getPlayer(); + WorldConfiguration wcfg = plugin.getGlobalStateManager().get(player.getWorld()); + if (wcfg.useRegions && !plugin.getGlobalRegionManager().hasBypass(player, player.getWorld())) { + GameMode gameMode = plugin.getGlobalRegionManager().get(player.getWorld()) + .getApplicableRegions(player.getLocation()).getFlag(DefaultFlag.GAME_MODE); + if (plugin.getFlagStateManager().getState(player).lastGameMode != null + && gameMode != null && event.getNewGameMode() != gameMode) { + event.setCancelled(true); + } + } + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + World world = player.getWorld(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (cfg.activityHaltToggle) { + player.sendMessage(ChatColor.YELLOW + + "Intensive server activity has been HALTED."); + + int removed = 0; + + for (Entity entity : world.getEntities()) { + if (BukkitUtil.isIntensiveEntity(entity)) { + entity.remove(); + removed++; + } + } + + if (removed > 10) { + plugin.getLogger().info("Halt-Act: " + removed + " entities (>10) auto-removed from " + + player.getWorld().toString()); + } + } + + if (wcfg.fireSpreadDisableToggle) { + player.sendMessage(ChatColor.YELLOW + + "Fire spread is currently globally disabled for this world."); + } + + if (!cfg.hasCommandBookGodMode() && cfg.autoGodMode && (plugin.inGroup(player, "wg-invincible") + || plugin.hasPermission(player, "worldguard.auto-invincible"))) { + plugin.getLogger().log(Level.INFO, "Enabled auto-god mode for " + player.getName()); + cfg.enableGodMode(player); + } + + if (plugin.inGroup(player, "wg-amphibious")) { + plugin.getLogger().log(Level.INFO, "Enabled no-drowning mode for " + player.getName() + " (player is in group 'wg-amphibious')"); + cfg.enableAmphibiousMode(player); + } + + if (wcfg.useRegions) { + PlayerFlagState state = plugin.getFlagStateManager().getState(player); + Location loc = player.getLocation(); + state.lastWorld = loc.getWorld(); + state.lastBlockX = loc.getBlockX(); + state.lastBlockY = loc.getBlockY(); + state.lastBlockZ = loc.getBlockZ(); + } + } + + @EventHandler(ignoreCancelled = true) + public void onPlayerChat(AsyncPlayerChatEvent event) { + Player player = event.getPlayer(); + WorldConfiguration wcfg = plugin.getGlobalStateManager().get(player.getWorld()); + if (wcfg.useRegions) { + if (!plugin.getGlobalRegionManager().allows(DefaultFlag.SEND_CHAT, player.getLocation())) { + player.sendMessage(ChatColor.RED + "You don't have permission to chat in this region!"); + event.setCancelled(true); + return; + } + + for (Iterator i = event.getRecipients().iterator(); i.hasNext();) { + if (!plugin.getGlobalRegionManager().allows(DefaultFlag.RECEIVE_CHAT, i.next().getLocation())) { + i.remove(); + } + } + if (event.getRecipients().size() == 0) { + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true) + public void onPlayerLogin(PlayerLoginEvent event) { + Player player = event.getPlayer(); + ConfigurationManager cfg = plugin.getGlobalStateManager(); + + String hostKey = cfg.hostKeys.get(player.getName().toLowerCase()); + if (hostKey != null) { + String hostname = event.getHostname(); + int colonIndex = hostname.indexOf(':'); + if (colonIndex != -1) { + hostname = hostname.substring(0, colonIndex); + } + + if (!hostname.equals(hostKey)) { + event.disallow(PlayerLoginEvent.Result.KICK_OTHER, + "You did not join with the valid host key!"); + plugin.getLogger().warning("WorldGuard host key check: " + + player.getName() + " joined with '" + hostname + + "' but '" + hostKey + "' was expected. Kicked!"); + return; + } + } + + if (cfg.deopOnJoin) { + player.setOp(false); + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + Player player = event.getPlayer(); + World world = player.getWorld(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + // This is to make the enter/exit flags accurate -- move events are not + // sent constantly, so it is possible to move just a little enough to + // not trigger the event and then rejoin so that you are then considered + // outside the border. This should work around that. + if (wcfg.useRegions) { + boolean hasBypass = plugin.getGlobalRegionManager().hasBypass(player, world); + PlayerFlagState state = plugin.getFlagStateManager().getState(player); + + if (state.lastWorld != null && !hasBypass) { + LocalPlayer localPlayer = plugin.wrapPlayer(player); + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + Location loc = player.getLocation(); + Vector pt = new Vector(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + if (state.lastExitAllowed == null) { + state.lastExitAllowed = set.allows(DefaultFlag.EXIT, localPlayer); + } + + if (!state.lastExitAllowed || !set.allows(DefaultFlag.ENTRY, localPlayer)) { + // Only if we have the last location cached + if (state.lastWorld.equals(world)) { + Location newLoc = new Location(world, state.lastBlockX + 0.5, + state.lastBlockY, state.lastBlockZ + 0.5); + player.teleport(newLoc); + } + } + } + } + + cfg.forgetPlayer(plugin.wrapPlayer(player)); + plugin.forgetPlayer(player); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerInteract(PlayerInteractEvent event) { + Player player = event.getPlayer(); + World world = player.getWorld(); + + if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { + handleBlockRightClick(event); + } else if (event.getAction() == Action.PHYSICAL) { + handlePhysicalInteract(event); + } + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (wcfg.removeInfiniteStacks + && !plugin.hasPermission(player, "worldguard.override.infinite-stack")) { + int slot = player.getInventory().getHeldItemSlot(); + ItemStack heldItem = player.getInventory().getItem(slot); + if (heldItem != null && heldItem.getAmount() < 0) { + player.getInventory().setItem(slot, null); + player.sendMessage(ChatColor.RED + "Infinite stack removed."); + } + } + } + + /** + * Called when a player right clicks a block. + * + * @param event Thrown event + */ + private void handleBlockRightClick(PlayerInteractEvent event) { + if (event.isCancelled()) { + return; + } + + Block block = event.getClickedBlock(); + World world = block.getWorld(); + int type = block.getTypeId(); + Player player = event.getPlayer(); + ItemStack item = player.getItemInHand(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + // Infinite stack removal + if ((type == BlockID.CHEST + || type == BlockID.JUKEBOX + || type == BlockID.DISPENSER + || type == BlockID.FURNACE + || type == BlockID.BURNING_FURNACE + || type == BlockID.BREWING_STAND + || type == BlockID.ENCHANTMENT_TABLE) + && wcfg.removeInfiniteStacks + && !plugin.hasPermission(player, "worldguard.override.infinite-stack")) { + for (int slot = 0; slot < 40; slot++) { + ItemStack heldItem = player.getInventory().getItem(slot); + if (heldItem != null && heldItem.getAmount() < 0) { + player.getInventory().setItem(slot, null); + player.sendMessage(ChatColor.RED + "Infinite stack in slot #" + slot + " removed."); + } + } + } + + if (wcfg.useRegions) { + Vector pt = toVector(block); + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + Block placedIn = block.getRelative(event.getBlockFace()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet placedInSet = mgr.getApplicableRegions(placedIn.getLocation()); + LocalPlayer localPlayer = plugin.wrapPlayer(player); + + if (item.getTypeId() == wcfg.regionWand && plugin.hasPermission(player, "worldguard.region.wand")) { + if (set.size() > 0) { + player.sendMessage(ChatColor.YELLOW + "Can you build? " + + (set.canBuild(localPlayer) ? "Yes" : "No")); + + StringBuilder str = new StringBuilder(); + for (Iterator it = set.iterator(); it.hasNext();) { + str.append(it.next().getId()); + if (it.hasNext()) { + str.append(", "); + } + } + + player.sendMessage(ChatColor.YELLOW + "Applicable regions: " + str.toString()); + } else { + player.sendMessage(ChatColor.YELLOW + "WorldGuard: No defined regions here!"); + } + + event.setCancelled(true); + } + } + } + + /** + * Called when a player steps on a pressure plate or tramples crops. + * + * @param event Thrown event + */ + private void handlePhysicalInteract(PlayerInteractEvent event) { + if (event.isCancelled()) return; + + Player player = event.getPlayer(); + Block block = event.getClickedBlock(); //not actually clicked but whatever + int type = block.getTypeId(); + World world = player.getWorld(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (block.getTypeId() == BlockID.SOIL && wcfg.disablePlayerCropTrampling) { + event.setCancelled(true); + return; + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPlayerDropItem(PlayerDropItemEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getPlayer().getWorld()); + Player player = event.getPlayer(); + + if (wcfg.useRegions) { + if (!plugin.getGlobalRegionManager().hasBypass(player, player.getWorld()) + && !plugin.getGlobalRegionManager().allows(DefaultFlag.ITEM_DROP, player.getLocation())) { + event.setCancelled(true); + player.sendMessage(ChatColor.RED + "You don't have permission to do that in this area."); + } + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPlayerFish(PlayerFishEvent event) { + WorldConfiguration wcfg = plugin.getGlobalStateManager().get(event.getPlayer().getWorld()); + + if (wcfg.disableExpDrops || !plugin.getGlobalRegionManager().allows(DefaultFlag.EXP_DROPS, + event.getPlayer().getLocation())) { + event.setExpToDrop(0); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerRespawn(PlayerRespawnEvent event) { + Player player = event.getPlayer(); + Location location = player.getLocation(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(player.getWorld()); + + if (wcfg.useRegions) { + Vector pt = toVector(location); + RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + LocalPlayer localPlayer = plugin.wrapPlayer(player); + com.sk89q.worldedit.Location spawn = set.getFlag(DefaultFlag.SPAWN_LOC, localPlayer); + + if (spawn != null) { + event.setRespawnLocation(com.sk89q.worldedit.bukkit.BukkitUtil.toLocation(spawn)); + } + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onItemHeldChange(PlayerItemHeldEvent event) { + Player player = event.getPlayer(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(player.getWorld()); + + if (wcfg.removeInfiniteStacks + && !plugin.hasPermission(player, "worldguard.override.infinite-stack")) { + int newSlot = event.getNewSlot(); + ItemStack heldItem = player.getInventory().getItem(newSlot); + if (heldItem != null && heldItem.getAmount() < 0) { + player.getInventory().setItem(newSlot, null); + player.sendMessage(ChatColor.RED + "Infinite stack removed."); + } + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onPlayerBedEnter(PlayerBedEnterEvent event) { + Player player = event.getPlayer(); + Location location = player.getLocation(); + + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(player.getWorld()); + + if (wcfg.useRegions) { + Vector pt = toVector(location); + RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + if (!plugin.getGlobalRegionManager().hasBypass(player, player.getWorld()) + && !set.allows(DefaultFlag.SLEEP, plugin.wrapPlayer(player))) { + event.setCancelled(true); + player.sendMessage("This bed doesn't belong to you!"); + return; + } + } + } + + @EventHandler(priority= EventPriority.LOW, ignoreCancelled = true) + public void onPlayerTeleport(PlayerTeleportEvent event) { + World world = event.getFrom().getWorld(); + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (wcfg.useRegions) { + RegionManager mgr = plugin.getGlobalRegionManager().get(event.getFrom().getWorld()); + Vector pt = new Vector(event.getTo().getBlockX(), event.getTo().getBlockY(), event.getTo().getBlockZ()); + Vector ptFrom = new Vector(event.getFrom().getBlockX(), event.getFrom().getBlockY(), event.getFrom().getBlockZ()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet setFrom = mgr.getApplicableRegions(ptFrom); + LocalPlayer localPlayer = plugin.wrapPlayer(event.getPlayer()); + + if (cfg.usePlayerTeleports) { + boolean result = checkMove(plugin, event.getPlayer(), event.getFrom(), event.getTo()); + if (result) { + event.setCancelled(true); + return; + } + } + + if (event.getCause() == TeleportCause.ENDER_PEARL) { + if (!plugin.getGlobalRegionManager().hasBypass(localPlayer, world) + && !(set.allows(DefaultFlag.ENDERPEARL, localPlayer) + && setFrom.allows(DefaultFlag.ENDERPEARL, localPlayer))) { + event.getPlayer().sendMessage(ChatColor.DARK_RED + "You're not allowed to go there."); + event.setCancelled(true); + return; + } + } + } + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { + Player player = event.getPlayer(); + LocalPlayer localPlayer = plugin.wrapPlayer(player); + World world = player.getWorld(); + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (wcfg.useRegions && !plugin.getGlobalRegionManager().hasBypass(player, world)) { + Vector pt = toVector(player.getLocation()); + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + Set allowedCommands = set.getFlag(DefaultFlag.ALLOWED_CMDS, localPlayer); + Set blockedCommands = set.getFlag(DefaultFlag.BLOCKED_CMDS, localPlayer); + CommandFilter test = new CommandFilter(allowedCommands, blockedCommands); + + if (!test.apply(event.getMessage())) { + player.sendMessage(ChatColor.RED + event.getMessage() + " is not allowed in this area."); + event.setCancelled(true); + return; + } + } + + if (cfg.blockInGameOp) { + if (opPattern.matcher(event.getMessage()).matches()) { + player.sendMessage(ChatColor.RED + "/op can only be used in console (as set by a WG setting)."); + event.setCancelled(true); + return; + } + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardServerListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardServerListener.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/bukkit/WorldGuardServerListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardServerListener.java index 6ab068bb..2d3b049b 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardServerListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardServerListener.java @@ -17,8 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.bukkit; +package com.sk89q.worldguard.bukkit.listener; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.server.PluginDisableEvent; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardVehicleListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardVehicleListener.java similarity index 92% rename from src/main/java/com/sk89q/worldguard/bukkit/WorldGuardVehicleListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardVehicleListener.java index 6cc4c7ad..8d5b1b6a 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardVehicleListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardVehicleListener.java @@ -1,72 +1,75 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import org.bukkit.World; -import org.bukkit.entity.Player; -import org.bukkit.entity.Vehicle; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.vehicle.VehicleMoveEvent; - -public class WorldGuardVehicleListener implements Listener { - - private WorldGuardPlugin plugin; - - /** - * Construct the object; - * - * @param plugin - */ - public WorldGuardVehicleListener(WorldGuardPlugin plugin) { - this.plugin = plugin; - } - - /** - * Register events. - */ - public void registerEvents() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @EventHandler - public void onVehicleMove(VehicleMoveEvent event) { - Vehicle vehicle = event.getVehicle(); - if (vehicle.getPassenger() == null - || !(vehicle.getPassenger() instanceof Player)) return; - Player player = (Player) vehicle.getPassenger(); - World world = vehicle.getWorld(); - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - if (wcfg.useRegions) { - // Did we move a block? - if (event.getFrom().getBlockX() != event.getTo().getBlockX() - || event.getFrom().getBlockY() != event.getTo().getBlockY() - || event.getFrom().getBlockZ() != event.getTo().getBlockZ()) { - boolean result = WorldGuardPlayerListener.checkMove(plugin, player, event.getFrom(), event.getTo()); - if (result) { - vehicle.setVelocity(new org.bukkit.util.Vector(0,0,0)); - vehicle.teleport(event.getFrom()); - } - } - } - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.vehicle.VehicleMoveEvent; + +public class WorldGuardVehicleListener implements Listener { + + private WorldGuardPlugin plugin; + + /** + * Construct the object; + * + * @param plugin + */ + public WorldGuardVehicleListener(WorldGuardPlugin plugin) { + this.plugin = plugin; + } + + /** + * Register events. + */ + public void registerEvents() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @EventHandler + public void onVehicleMove(VehicleMoveEvent event) { + Vehicle vehicle = event.getVehicle(); + if (vehicle.getPassenger() == null + || !(vehicle.getPassenger() instanceof Player)) return; + Player player = (Player) vehicle.getPassenger(); + World world = vehicle.getWorld(); + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + if (wcfg.useRegions) { + // Did we move a block? + if (event.getFrom().getBlockX() != event.getTo().getBlockX() + || event.getFrom().getBlockY() != event.getTo().getBlockY() + || event.getFrom().getBlockZ() != event.getTo().getBlockZ()) { + boolean result = WorldGuardPlayerListener.checkMove(plugin, player, event.getFrom(), event.getTo()); + if (result) { + vehicle.setVelocity(new org.bukkit.util.Vector(0,0,0)); + vehicle.teleport(event.getFrom()); + } + } + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardWeatherListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardWeatherListener.java similarity index 94% rename from src/main/java/com/sk89q/worldguard/bukkit/WorldGuardWeatherListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardWeatherListener.java index a3019a96..7fc1578e 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardWeatherListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardWeatherListener.java @@ -1,111 +1,114 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - -import org.bukkit.Location; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.weather.LightningStrikeEvent; -import org.bukkit.event.weather.ThunderChangeEvent; -import org.bukkit.event.weather.WeatherChangeEvent; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; - -public class WorldGuardWeatherListener implements Listener { - - /** - * Plugin. - */ - private WorldGuardPlugin plugin; - - /** - * Construct the object; - * - * @param plugin The plugin instance - */ - public WorldGuardWeatherListener(WorldGuardPlugin plugin) { - this.plugin = plugin; - } - - public void registerEvents() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onWeatherChange(WeatherChangeEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getWorld()); - - if (event.toWeatherState()) { - if (wcfg.disableWeather) { - event.setCancelled(true); - } - } else { - if (!wcfg.disableWeather && wcfg.alwaysRaining) { - event.setCancelled(true); - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onThunderChange(ThunderChangeEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getWorld()); - - if (event.toThunderState()) { - if (wcfg.disableThunder) { - event.setCancelled(true); - } - } else { - if (!wcfg.disableWeather && wcfg.alwaysThundering) { - event.setCancelled(true); - } - } - } - - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onLightningStrike(LightningStrikeEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(event.getWorld()); - - if (wcfg.disallowedLightningBlocks.size() > 0) { - int targetId = event.getLightning().getLocation().getBlock().getTypeId(); - if (wcfg.disallowedLightningBlocks.contains(targetId)) { - event.setCancelled(true); - } - } - - Location loc = event.getLightning().getLocation(); - if (wcfg.useRegions) { - Vector pt = toVector(loc); - RegionManager mgr = plugin.getGlobalRegionManager().get(loc.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - if (!set.allows(DefaultFlag.LIGHTNING)) { - event.setCancelled(true); - } - } - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; + +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.weather.LightningStrikeEvent; +import org.bukkit.event.weather.ThunderChangeEvent; +import org.bukkit.event.weather.WeatherChangeEvent; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; + +public class WorldGuardWeatherListener implements Listener { + + /** + * Plugin. + */ + private WorldGuardPlugin plugin; + + /** + * Construct the object; + * + * @param plugin The plugin instance + */ + public WorldGuardWeatherListener(WorldGuardPlugin plugin) { + this.plugin = plugin; + } + + public void registerEvents() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onWeatherChange(WeatherChangeEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getWorld()); + + if (event.toWeatherState()) { + if (wcfg.disableWeather) { + event.setCancelled(true); + } + } else { + if (!wcfg.disableWeather && wcfg.alwaysRaining) { + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onThunderChange(ThunderChangeEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getWorld()); + + if (event.toThunderState()) { + if (wcfg.disableThunder) { + event.setCancelled(true); + } + } else { + if (!wcfg.disableWeather && wcfg.alwaysThundering) { + event.setCancelled(true); + } + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onLightningStrike(LightningStrikeEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(event.getWorld()); + + if (wcfg.disallowedLightningBlocks.size() > 0) { + int targetId = event.getLightning().getLocation().getBlock().getTypeId(); + if (wcfg.disallowedLightningBlocks.contains(targetId)) { + event.setCancelled(true); + } + } + + Location loc = event.getLightning().getLocation(); + if (wcfg.useRegions) { + Vector pt = toVector(loc); + RegionManager mgr = plugin.getGlobalRegionManager().get(loc.getWorld()); + ApplicableRegionSet set = mgr.getApplicableRegions(pt); + + if (!set.allows(DefaultFlag.LIGHTNING)) { + event.setCancelled(true); + } + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardWorldListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardWorldListener.java similarity index 92% rename from src/main/java/com/sk89q/worldguard/bukkit/WorldGuardWorldListener.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardWorldListener.java index 9b94ede4..89a498c7 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardWorldListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardWorldListener.java @@ -1,97 +1,101 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import org.bukkit.World; -import org.bukkit.entity.Entity; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.world.ChunkLoadEvent; -import org.bukkit.event.world.WorldLoadEvent; - -public class WorldGuardWorldListener implements Listener { - - private WorldGuardPlugin plugin; - - /** - * Construct the object; - * - * @param plugin The plugin instance - */ - public WorldGuardWorldListener(WorldGuardPlugin plugin) { - this.plugin = plugin; - } - - /** - * Register events. - */ - public void registerEvents() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @EventHandler - public void onChunkLoad(ChunkLoadEvent event) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - - if (cfg.activityHaltToggle) { - int removed = 0; - - for (Entity entity : event.getChunk().getEntities()) { - if (BukkitUtil.isIntensiveEntity(entity)) { - entity.remove(); - removed++; - } - } - - if (removed > 50) { - plugin.getLogger().info("Halt-Act: " + removed + " entities (>50) auto-removed from " - + event.getChunk().toString()); - } - } - } - - @EventHandler - public void onWorldLoad(WorldLoadEvent event) { - initWorld(event.getWorld()); - } - - /** - * Initialize the settings for the specified world - * @see WorldConfiguration#alwaysRaining - * @see WorldConfiguration#disableWeather - * @see WorldConfiguration#alwaysThundering - * @see WorldConfiguration#disableThunder - * @param world The specified world - */ - public void initWorld(World world) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - if (wcfg.alwaysRaining && !wcfg.disableWeather) { - world.setStorm(true); - } else if (wcfg.disableWeather && !wcfg.alwaysRaining) { - world.setStorm(false); - } - if (wcfg.alwaysThundering && !wcfg.disableThunder) { - world.setThundering(true); - } else if (wcfg.disableThunder && !wcfg.alwaysThundering) { - world.setStorm(false); - } - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.sk89q.worldguard.bukkit.BukkitUtil; +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.WorldLoadEvent; + +public class WorldGuardWorldListener implements Listener { + + private WorldGuardPlugin plugin; + + /** + * Construct the object; + * + * @param plugin The plugin instance + */ + public WorldGuardWorldListener(WorldGuardPlugin plugin) { + this.plugin = plugin; + } + + /** + * Register events. + */ + public void registerEvents() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @EventHandler + public void onChunkLoad(ChunkLoadEvent event) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + + if (cfg.activityHaltToggle) { + int removed = 0; + + for (Entity entity : event.getChunk().getEntities()) { + if (BukkitUtil.isIntensiveEntity(entity)) { + entity.remove(); + removed++; + } + } + + if (removed > 50) { + plugin.getLogger().info("Halt-Act: " + removed + " entities (>50) auto-removed from " + + event.getChunk().toString()); + } + } + } + + @EventHandler + public void onWorldLoad(WorldLoadEvent event) { + initWorld(event.getWorld()); + } + + /** + * Initialize the settings for the specified world + * @see WorldConfiguration#alwaysRaining + * @see WorldConfiguration#disableWeather + * @see WorldConfiguration#alwaysThundering + * @see WorldConfiguration#disableThunder + * @param world The specified world + */ + public void initWorld(World world) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + if (wcfg.alwaysRaining && !wcfg.disableWeather) { + world.setStorm(true); + } else if (wcfg.disableWeather && !wcfg.alwaysRaining) { + world.setStorm(false); + } + if (wcfg.alwaysThundering && !wcfg.disableThunder) { + world.setThundering(true); + } else if (wcfg.disableThunder && !wcfg.alwaysThundering) { + world.setStorm(false); + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/internal/Events.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java similarity index 98% rename from src/main/java/com/sk89q/worldguard/internal/Events.java rename to src/main/java/com/sk89q/worldguard/bukkit/util/Events.java index 4394acef..baea5c6e 100644 --- a/src/main/java/com/sk89q/worldguard/internal/Events.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal; +package com.sk89q.worldguard.bukkit.util; import org.bukkit.Bukkit; import org.bukkit.event.Cancellable; diff --git a/src/main/java/com/sk89q/worldguard/internal/Blocks.java b/src/main/java/com/sk89q/worldguard/internal/Blocks.java deleted file mode 100644 index 7d82b045..00000000 --- a/src/main/java/com/sk89q/worldguard/internal/Blocks.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.internal; - -import org.bukkit.block.Block; -import org.bukkit.block.Hopper; -import org.bukkit.material.Attachable; -import org.bukkit.material.MaterialData; - -import javax.annotation.Nullable; - -/** - * Block related utility methods. - */ -public final class Blocks { - - private Blocks() { - } - - /** - * Get the block that this block attaches to. - * - * @param block the block to check - * @return the block attached to or null - */ - @Nullable - public static Block getAttachesTo(Block block) { - MaterialData data = block.getState().getData(); - - if (data instanceof Attachable) { - Attachable attachable = (Attachable) data; - return block.getRelative(attachable.getAttachedFace()); - } - - return null; - } - -} diff --git a/src/main/java/com/sk89q/worldguard/internal/cause/BlockCause.java b/src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/internal/cause/BlockCause.java rename to src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java index d19d2fd0..76fea257 100644 --- a/src/main/java/com/sk89q/worldguard/internal/cause/BlockCause.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.cause; +package com.sk89q.worldguard.util.cause; import org.bukkit.block.Block; diff --git a/src/main/java/com/sk89q/worldguard/internal/cause/Cause.java b/src/main/java/com/sk89q/worldguard/util/cause/Cause.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/internal/cause/Cause.java rename to src/main/java/com/sk89q/worldguard/util/cause/Cause.java index 828f9db1..65a0a4b4 100644 --- a/src/main/java/com/sk89q/worldguard/internal/cause/Cause.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/Cause.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.cause; +package com.sk89q.worldguard.util.cause; /** * Represents a possible cause of an event. diff --git a/src/main/java/com/sk89q/worldguard/internal/cause/Causes.java b/src/main/java/com/sk89q/worldguard/util/cause/Causes.java similarity index 98% rename from src/main/java/com/sk89q/worldguard/internal/cause/Causes.java rename to src/main/java/com/sk89q/worldguard/util/cause/Causes.java index e549319b..6cd9532f 100644 --- a/src/main/java/com/sk89q/worldguard/internal/cause/Causes.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/Causes.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.cause; +package com.sk89q.worldguard.util.cause; import org.bukkit.block.Block; import org.bukkit.entity.Entity; diff --git a/src/main/java/com/sk89q/worldguard/internal/cause/EntityCause.java b/src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/internal/cause/EntityCause.java rename to src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java index f24774b7..893da290 100644 --- a/src/main/java/com/sk89q/worldguard/internal/cause/EntityCause.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.cause; +package com.sk89q.worldguard.util.cause; import org.bukkit.entity.Entity; diff --git a/src/main/java/com/sk89q/worldguard/internal/cause/PlayerCause.java b/src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/internal/cause/PlayerCause.java rename to src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java index 2b4e63f5..f016b270 100644 --- a/src/main/java/com/sk89q/worldguard/internal/cause/PlayerCause.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.cause; +package com.sk89q.worldguard.util.cause; import org.bukkit.entity.Player; diff --git a/src/main/java/com/sk89q/worldguard/internal/cause/UnknownCause.java b/src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/internal/cause/UnknownCause.java rename to src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java index d811ac20..9216c409 100644 --- a/src/main/java/com/sk89q/worldguard/internal/cause/UnknownCause.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.internal.cause; +package com.sk89q.worldguard.util.cause; import static com.google.common.base.Preconditions.checkNotNull; From 110aa2e56b5afa6191a3b1f4ec71618ca3196f3b Mon Sep 17 00:00:00 2001 From: sk89q Date: Mon, 11 Aug 2014 17:54:32 -0700 Subject: [PATCH 023/133] Clean up docs / code for utility classes. --- .../sk89q/worldguard/bukkit/util/Blocks.java | 10 ++++++ .../{DamageCauses.java => EventEnums.java} | 33 ++++++++++++++++--- .../worldguard/bukkit/util/RegionQuery.java | 11 ++----- 3 files changed, 40 insertions(+), 14 deletions(-) rename src/main/java/com/sk89q/worldguard/bukkit/util/{DamageCauses.java => EventEnums.java} (65%) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/Blocks.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Blocks.java index 7620fdcf..2a7323ee 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/Blocks.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Blocks.java @@ -27,11 +27,21 @@ import java.util.Collections; import java.util.List; +/** + * Utility methods to deal with blocks. + */ public final class Blocks { private Blocks() { } + /** + * Get a list of connected blocks to the given block, not including + * the given block. + * + * @param block the block + * @return a list of connected blocks, not including the given block + */ public static List getConnected(Block block) { MaterialData data = block.getState().getData(); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/DamageCauses.java b/src/main/java/com/sk89q/worldguard/bukkit/util/EventEnums.java similarity index 65% rename from src/main/java/com/sk89q/worldguard/bukkit/util/DamageCauses.java rename to src/main/java/com/sk89q/worldguard/bukkit/util/EventEnums.java index 206be5b0..2db34f0d 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/DamageCauses.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/EventEnums.java @@ -23,26 +23,49 @@ import org.bukkit.entity.LivingEntity; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; -public final class DamageCauses { +/** + * Utility methods to deal with event-related enums in Bukkit. + */ +public final class EventEnums { - private DamageCauses() { + private EventEnums() { } - public static boolean isFire(DamageCause cause) { + /** + * Return whether the given damage cause is fire-reltaed. + * + * @param cause the cause + * @return true if fire related + */ + public static boolean isFireCause(DamageCause cause) { return cause == DamageCause.FIRE || cause == DamageCause.FIRE_TICK; } - public static boolean isExplosion(DamageCause cause) { + /** + * Return whether the given cause is an explosion. + * + * @param cause the cause + * @return true if it is an explosion cuase + */ + public static boolean isExplosionCause(DamageCause cause) { return cause == DamageCause.BLOCK_EXPLOSION || cause == DamageCause.ENTITY_EXPLOSION; } + /** + * Restore the statistic associated with the given cause. For example, + * for the {@link DamageCause#DROWNING} cause, the entity would have its + * air level set to its maximum. + * + * @param entity the entity + * @param cause the cuase + */ public static void restoreStatistic(Entity entity, DamageCause cause) { if (cause == DamageCause.DROWNING && entity instanceof LivingEntity) { LivingEntity living = (LivingEntity) entity; living.setRemainingAir(living.getMaximumAir()); } - if (isFire(cause)) { + if (isFireCause(cause)) { entity.setFireTicks(0); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java b/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java index 3df7e293..12f4f747 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java @@ -29,7 +29,6 @@ import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.block.Block; import org.bukkit.entity.Player; import javax.annotation.Nullable; @@ -38,23 +37,17 @@ public class RegionQuery { - private final WorldGuardPlugin plugin; private final ConfigurationManager config; private final GlobalRegionManager globalManager; @Nullable private final LocalPlayer localPlayer; public RegionQuery(WorldGuardPlugin plugin, @Nullable Player player) { - this.plugin = plugin; this.config = plugin.getGlobalStateManager(); this.globalManager = plugin.getGlobalRegionManager(); this.localPlayer = player != null ? plugin.wrapPlayer(player) : null; } - public boolean canBuild(Block block) { - return canBuild(block.getLocation()); - } - public boolean canBuild(Location location) { World world = location.getWorld(); WorldConfiguration worldConfig = config.get(world); @@ -63,7 +56,7 @@ public boolean canBuild(Location location) { return true; } - if (globalManager.hasBypass(localPlayer, world)) { + if (localPlayer != null && globalManager.hasBypass(localPlayer, world)) { return true; } else { RegionManager manager = globalManager.get(location.getWorld()); @@ -79,7 +72,7 @@ public boolean canConstruct(Location location) { return true; } - if (globalManager.hasBypass(localPlayer, world)) { + if (localPlayer != null && globalManager.hasBypass(localPlayer, world)) { return true; } else { RegionManager manager = globalManager.get(location.getWorld()); From 26d3645f8693af5206a61ff56c6c0d3756947261 Mon Sep 17 00:00:00 2001 From: sk89q Date: Mon, 11 Aug 2014 20:08:38 -0700 Subject: [PATCH 024/133] Improve level of event listening. --- .../worldguard/bukkit/WorldGuardPlugin.java | 10 +- .../listener/ChestProtectionListener.java | 40 ++-- .../listener/EventAbstractionListener.java | 213 +++++++++++++----- .../sk89q/worldguard/bukkit/util/Events.java | 2 +- .../worldguard/bukkit/util/Materials.java | 102 ++++++--- .../worldguard/bukkit/util/WGMetadata.java | 78 +++++++ .../worldguard/util/cause/BlockCause.java | 5 + .../sk89q/worldguard/util/cause/Causes.java | 2 +- .../worldguard/util/cause/EntityCause.java | 4 + .../worldguard/util/cause/PlayerCause.java | 5 + .../worldguard/util/cause/UnknownCause.java | 4 + 11 files changed, 346 insertions(+), 119 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/util/WGMetadata.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 0b205618..75e8a2b6 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -33,6 +33,11 @@ import com.sk89q.worldguard.bukkit.commands.GeneralCommands; import com.sk89q.worldguard.bukkit.commands.ProtectionCommands; import com.sk89q.worldguard.bukkit.commands.ToggleCommands; +import com.sk89q.worldguard.bukkit.listener.BlacklistListener; +import com.sk89q.worldguard.bukkit.listener.BlockedPotionsListener; +import com.sk89q.worldguard.bukkit.listener.ChestProtectionListener; +import com.sk89q.worldguard.bukkit.listener.EventAbstractionListener; +import com.sk89q.worldguard.bukkit.listener.RegionProtectionListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardBlockListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardCommandBookListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardEntityListener; @@ -42,10 +47,6 @@ import com.sk89q.worldguard.bukkit.listener.WorldGuardVehicleListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardWeatherListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardWorldListener; -import com.sk89q.worldguard.bukkit.listener.BlacklistListener; -import com.sk89q.worldguard.bukkit.listener.BlockedPotionsListener; -import com.sk89q.worldguard.bukkit.listener.ChestProtectionListener; -import com.sk89q.worldguard.bukkit.listener.RegionProtectionListener; import com.sk89q.worldguard.protection.GlobalRegionManager; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; @@ -200,6 +201,7 @@ public void run() { (new ChestProtectionListener(this)).registerEvents(); (new RegionProtectionListener(this)).registerEvents(); (new BlockedPotionsListener(this)).registerEvents(); + (new EventAbstractionListener(this)).registerEvents(); configuration.updateCommandBookGodMode(); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java index 3757ad98..fc91edbb 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java @@ -71,21 +71,23 @@ public void onBreakBlock(BreakBlockEvent event) { Player player = Causes.getInvolvedPlayer(event.getCauses()); Location target = event.getTarget(); + WorldConfiguration wcfg = getWorldConfig(target.getWorld()); + + // Early guard + if (!wcfg.signChestProtection) { + return; + } + if (player != null) { - WorldConfiguration wcfg = getWorldConfig(player); - - // Early guard - if (!wcfg.signChestProtection) { - return; - } - if (wcfg.isChestProtected(target.getBlock(), player)) { player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); event.setCancelled(true); } } else { - // No player? Deny anyway - event.setCancelled(true); + if (wcfg.isChestProtected(target.getBlock())) { + // No player? Deny anyway + event.setCancelled(true); + } } } @@ -94,21 +96,23 @@ public void onUseBlock(UseBlockEvent event) { Player player = Causes.getInvolvedPlayer(event.getCauses()); Location target = event.getTarget(); + WorldConfiguration wcfg = getWorldConfig(target.getWorld()); + + // Early guard + if (!wcfg.signChestProtection) { + return; + } + if (player != null) { - WorldConfiguration wcfg = getWorldConfig(player); - - // Early guard - if (!wcfg.signChestProtection) { - return; - } - if (wcfg.isChestProtected(target.getBlock(), player)) { player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); event.setCancelled(true); } } else { - // No player? Deny anyway - event.setCancelled(true); + if (wcfg.isChestProtected(target.getBlock())) { + // No player? Deny anyway + event.setCancelled(true); + } } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java index 7e25d31e..d6d0c757 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java @@ -20,11 +20,6 @@ package com.sk89q.worldguard.bukkit.listener; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.bukkit.util.Blocks; -import com.sk89q.worldguard.bukkit.util.Materials; -import com.sk89q.worldguard.bukkit.util.Events; -import com.sk89q.worldguard.util.cause.Cause; -import com.sk89q.worldguard.util.cause.Causes; import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; @@ -32,16 +27,26 @@ import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; +import com.sk89q.worldguard.bukkit.util.Blocks; +import com.sk89q.worldguard.bukkit.util.Events; +import com.sk89q.worldguard.bukkit.util.Materials; +import com.sk89q.worldguard.bukkit.util.WGMetadata; +import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.util.cause.Causes; import org.bukkit.DyeColor; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.entity.FallingBlock; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.ThrownPotion; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; import org.bukkit.event.Event.Result; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -55,6 +60,7 @@ import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.event.entity.EntityCombustByBlockEvent; import org.bukkit.event.entity.EntityCombustByEntityEvent; @@ -80,6 +86,8 @@ import org.bukkit.event.vehicle.VehicleDamageEvent; import org.bukkit.event.vehicle.VehicleDestroyEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.material.Dispenser; +import org.bukkit.material.MaterialData; import javax.annotation.Nullable; import java.util.Collections; @@ -91,6 +99,13 @@ public class EventAbstractionListener implements Listener { + /** + * Abstract {@link BlockFromToEvent}s into break and place events. + * Currently disabled as it creates a lot of new events. + */ + public static final boolean ABSTRACT_FROM_TO_EVENTS = false; + private static final String FALLING_SOURCE_KEY = "worldguard.fallingSource"; + private final WorldGuardPlugin plugin; public EventAbstractionListener(WorldGuardPlugin plugin) { @@ -112,7 +127,14 @@ public void onBlockBreak(BlockBreakEvent event) { @EventHandler public void onBlockPlace(BlockPlaceEvent event) { - Events.fireToCancel(event, new UseBlockEvent(event, create(event.getPlayer()), event.getBlock())); + BlockState previousState = event.getBlockReplacedState(); + + // Some blocks, like tall grass and fire, get replaced + if (previousState.getType() != Material.AIR) { + Events.fireToCancel(event, new BreakBlockEvent(event, create(event.getPlayer()), previousState.getLocation(), previousState.getType())); + } + + Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getPlayer()), event.getBlock())); } @EventHandler @@ -124,15 +146,37 @@ public void onBlockBurn(BlockBurnEvent event) { @EventHandler public void onEntityChangeBlock(EntityChangeBlockEvent event) { + Block block = event.getBlock(); + Entity entity = event.getEntity(); + Material to = event.getTo(); + // Fire two events: one as BREAK and one as PLACE if (event.getTo() != Material.AIR && event.getBlock().getType() != Material.AIR) { - Events.fireToCancel(event, new BreakBlockEvent(event, create(event.getEntity()), event.getBlock())); - Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getEntity()), event.getBlock())); + Events.fireToCancel(event, new BreakBlockEvent(event, create(entity), block)); + Events.fireToCancel(event, new PlaceBlockEvent(event, create(entity), block.getLocation(), to)); } else { if (event.getTo() == Material.AIR) { - Events.fireToCancel(event, new BreakBlockEvent(event, create(event.getEntity()), event.getBlock())); + // Track the source so later we can create a proper chain of causes + if (entity instanceof FallingBlock) { + WGMetadata.put(entity, FALLING_SOURCE_KEY, block); + + // Switch around the event + Events.fireToCancel(event, new SpawnEntityEvent(event, create(block), entity)); + } else { + Events.fireToCancel(event, new BreakBlockEvent(event, create(entity), event.getBlock())); + } } else { - Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getEntity()), event.getBlock())); + List> causes; + + // Return the source for falling blocks + if (entity instanceof FallingBlock) { + Block source = WGMetadata.getIfPresent(entity, FALLING_SOURCE_KEY, Block.class); + causes = create(source, entity); + } else { + causes = create(entity); + } + + Events.fireToCancel(event, new PlaceBlockEvent(event, causes, event.getBlock().getLocation(), to)); } } } @@ -159,69 +203,37 @@ public void onBlockDamage(BlockDamageEvent event) { public void onPlayerInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); @Nullable ItemStack item = player.getItemInHand(); - Block block = event.getClickedBlock(); + Block clicked = event.getClickedBlock(); + Block placed; List> causes = create(player); switch (event.getAction()) { case PHYSICAL: - // TODO: Don't fire events for blocks that can't be interacted with using PHYSICAL - if (Events.fireAndTestCancel(new UseBlockEvent(event, causes, block))) { + if (Events.fireAndTestCancel(new UseBlockEvent(event, causes, clicked))) { event.setUseInteractedBlock(Result.DENY); event.setCancelled(true); } break; case RIGHT_CLICK_BLOCK: - if (item != null && item.getType() == Material.TNT) { - // Workaround for a bug that allowed tnt to trigger instantly if placed - // next to redstone, without plugins getting the block place event - // (not sure if this actually still happens) - Events.fireToCancel(event, new UseBlockEvent(event, create(event.getPlayer()), block.getLocation(), Material.TNT)); - } + placed = clicked.getRelative(event.getBlockFace()); - // Handle created Minecarts - if (item != null && Materials.isMinecart(item.getType())) { - // TODO: Give a more specific minecart type - Block placedBlock = block.getRelative(event.getBlockFace()); - Events.fireToCancel(event, new SpawnEntityEvent(event, create(event.getPlayer()), placedBlock.getLocation().add(0.5, 0, 0.5), EntityType.MINECART)); - } - - // Handle cocoa beans - if (item != null && item.getType() == Material.INK_SACK && Materials.isDyeColor(item.getData(), DyeColor.BROWN)) { - // CraftBukkit doesn't or didn't throw a block place for this - if (!(event.getBlockFace() == BlockFace.DOWN || event.getBlockFace() == BlockFace.UP)) { - Block placedBlock = block.getRelative(event.getBlockFace()); - Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getPlayer()), placedBlock.getLocation(), Material.COCOA)); - } - } - - // Workaround for http://leaky.bukkit.org/issues/1034 - if (item != null && item.getType() == Material.TNT) { - Block placedBlock = block.getRelative(event.getBlockFace()); - Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getPlayer()), placedBlock.getLocation(), Material.TNT)); - } - - // Handle flint and steel and fire charge as fire place - if (item != null && (item.getType() == Material.FIREBALL || item.getType() == Material.FLINT_AND_STEEL)) { - Block placedBlock = block.getRelative(event.getBlockFace()); - if (!Events.fireAndTestCancel(new PlaceBlockEvent(event, create(event.getPlayer()), placedBlock.getLocation(), Material.FIRE))) { - event.setUseItemInHand(Result.DENY); - } - } + // Re-used for dispensers + handleBlockRightClick(event, create(event.getPlayer()), item, clicked, event.getBlockFace(), placed); case LEFT_CLICK_BLOCK: - // TODO: Don't fire events for blocks that can't be interacted with using clicks + placed = clicked.getRelative(event.getBlockFace()); // As of MC ~1.6, sneaking blocks the use of blocks with right click if (!player.isSneaking() || event.getAction() == Action.LEFT_CLICK_BLOCK) { // Only fire events for blocks that are modified when right clicked - if (isBlockModifiedOnClick(block.getType()) || (item != null && isItemAppliedToBlock(item.getType(), block.getType()))) { - if (Events.fireAndTestCancel(new UseBlockEvent(event, causes, block))) { + if (isBlockModifiedOnClick(clicked.getType()) || (item != null && isItemAppliedToBlock(item.getType(), clicked.getType()))) { + if (Events.fireAndTestCancel(new UseBlockEvent(event, causes, clicked))) { event.setUseInteractedBlock(Result.DENY); } // Handle connected blocks (i.e. beds, chests) - for (Block connected : Blocks.getConnected(block)) { + for (Block connected : Blocks.getConnected(clicked)) { if (Events.fireAndTestCancel(new UseBlockEvent(event, create(event.getPlayer()), connected))) { event.setUseInteractedBlock(Result.DENY); break; @@ -230,8 +242,8 @@ public void onPlayerInteract(PlayerInteractEvent event) { } // Special handling of flint and steel on TNT - if (block.getType() == Material.TNT && item != null && item.getType() == Material.FLINT_AND_STEEL) { - if (Events.fireAndTestCancel(new BreakBlockEvent(event, create(event.getPlayer()), block))) { + if (clicked.getType() == Material.TNT && item != null && item.getType() == Material.FLINT_AND_STEEL) { + if (Events.fireAndTestCancel(new BreakBlockEvent(event, create(event.getPlayer()), clicked))) { event.setUseInteractedBlock(Result.DENY); break; } @@ -239,8 +251,8 @@ public void onPlayerInteract(PlayerInteractEvent event) { } // Special handling of putting out fires - if (event.getAction() == Action.LEFT_CLICK_BLOCK && block.getType() == Material.FIRE) { - if (Events.fireAndTestCancel(new BreakBlockEvent(event, create(event.getPlayer()), block))) { + if (event.getAction() == Action.LEFT_CLICK_BLOCK && placed.getType() == Material.FIRE) { + if (Events.fireAndTestCancel(new BreakBlockEvent(event, create(event.getPlayer()), placed))) { event.setUseInteractedBlock(Result.DENY); break; } @@ -248,7 +260,7 @@ public void onPlayerInteract(PlayerInteractEvent event) { case LEFT_CLICK_AIR: case RIGHT_CLICK_AIR: - if (item != null && Events.fireAndTestCancel(new UseItemEvent(event, causes, player.getWorld(), item))) { + if (item != null && !item.getType().isBlock() && Events.fireAndTestCancel(new UseItemEvent(event, causes, player.getWorld(), item))) { event.setUseItemInHand(Result.DENY); } @@ -263,6 +275,7 @@ public void onEntityInteract(org.bukkit.event.entity.EntityInteractEvent event) @EventHandler public void onBlockIgnite(BlockIgniteEvent event) { + Block block = event.getBlock(); List> causes; // Find the cause @@ -276,7 +289,9 @@ public void onBlockIgnite(BlockIgniteEvent event) { causes = Collections.emptyList(); } - Events.fireToCancel(event, new BreakBlockEvent(event, causes, event.getBlock())); + if (block.getType() != Material.AIR) { + Events.fireToCancel(event, new BreakBlockEvent(event, causes, event.getBlock())); + } // This is also handled in the PlayerInteractEvent listener if (event.getCause() == IgniteCause.FLINT_AND_STEEL || event.getCause() == IgniteCause.FIREBALL) { @@ -303,7 +318,8 @@ public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) { // Milk buckets can't be emptied as of writing if (event.getBucket() != Material.MILK_BUCKET) { ItemStack item = new ItemStack(event.getBucket(), 1); - Events.fireToCancel(event, new PlaceBlockEvent(event, create(player), blockAffected)); + Material blockMaterial = Materials.getBucketBlockMaterial(event.getBucket()); + Events.fireToCancel(event, new PlaceBlockEvent(event, create(player), blockAffected.getLocation(), blockMaterial)); Events.fireToCancel(event, new UseItemEvent(event, create(player), player.getWorld(), item)); } } @@ -329,13 +345,34 @@ public void onPlayerBucketFill(PlayerBucketFillEvent event) { @EventHandler public void onBlockFromTo(BlockFromToEvent event) { - Events.fireToCancel(event, new PlaceBlockEvent(event, create(event.getBlock()), event.getToBlock())); + if (ABSTRACT_FROM_TO_EVENTS) { + Block from = event.getBlock(); + Block to = event.getToBlock(); + + // Liquids pass this event when flowing to solid blocks + if (to.getType().isSolid() && Materials.isLiquid(from.getType())) { + return; + } + + List> causes = create(from); + + if (from.getType() != Material.AIR) { + Events.fireToCancel(event, new BreakBlockEvent(event, causes, to)); + } + + Events.fireToCancel(event, new PlaceBlockEvent(event, causes, to.getLocation(), from.getType())); + } } //------------------------------------------------------------------------- // Entity break / place //------------------------------------------------------------------------- + @EventHandler + public void onCreatureSpawn(CreatureSpawnEvent event) { + Events.fireToCancel(event, new SpawnEntityEvent(event, Collections.>emptyList(), event.getEntity())); + } + @EventHandler public void onHangingPlace(HangingPlaceEvent event) { Events.fireToCancel(event, new SpawnEntityEvent(event, create(event.getPlayer()), event.getEntity())); @@ -355,6 +392,8 @@ public void onVehicleDestroy(VehicleDestroyEvent event) { Events.fireToCancel(event, new DestroyEntityEvent(event, create(event.getAttacker()), event.getVehicle())); } + // TODO: XP pickup is an entity destroy event + //------------------------------------------------------------------------- // Entity external interaction //------------------------------------------------------------------------- @@ -482,7 +521,59 @@ public void onPotionSplash(PotionSplashEvent event) { @EventHandler public void onBlockDispense(BlockDispenseEvent event) { - Events.fireToCancel(event, new UseItemEvent(event, create(event.getBlock()), event.getBlock().getWorld(), event.getItem())); + List> causes = create(event.getBlock()); + Block dispenserBlock = event.getBlock(); + ItemStack item = event.getItem(); + MaterialData materialData = dispenserBlock.getState().getData(); + + Events.fireToCancel(event, new UseItemEvent(event, causes, dispenserBlock.getWorld(), item)); + + // Simulate right click event as players have it + if (materialData instanceof Dispenser) { + Dispenser dispenser = (Dispenser) materialData; + Block placed = dispenserBlock.getRelative(dispenser.getFacing()); + Block clicked = placed.getRelative(dispenser.getFacing()); + handleBlockRightClick(event, causes, item, clicked, dispenser.getFacing().getOppositeFace(), placed); + } + } + + /** + * Handle the right click of a block while an item is held. + * + * @param event the original event + * @param causes the list of causes + * @param item the item + * @param clicked the clicked block + * @param faceClicked the face of the clicked block + * @param placed the placed block + * @param the event type + */ + private static void handleBlockRightClick(T event, List> causes, @Nullable ItemStack item, Block clicked, BlockFace faceClicked, Block placed) { + if (item != null && item.getType() == Material.TNT) { + // Workaround for a bug that allowed TNT to trigger instantly if placed + // next to redstone, without plugins getting the clicked place event + // (not sure if this actually still happens) + Events.fireToCancel(event, new UseBlockEvent(event, causes, clicked.getLocation(), Material.TNT)); + } + + // Handle created Minecarts + if (item != null && Materials.isMinecart(item.getType())) { + // TODO: Give a more specific Minecart type + Events.fireToCancel(event, new SpawnEntityEvent(event, causes, placed.getLocation().add(0.5, 0, 0.5), EntityType.MINECART)); + } + + // Handle cocoa beans + if (item != null && item.getType() == Material.INK_SACK && Materials.isDyeColor(item.getData(), DyeColor.BROWN)) { + // CraftBukkit doesn't or didn't throw a clicked place for this + if (!(faceClicked == BlockFace.DOWN || faceClicked == BlockFace.UP)) { + Events.fireToCancel(event, new PlaceBlockEvent(event, causes, placed.getLocation(), Material.COCOA)); + } + } + + // Workaround for http://leaky.bukkit.org/issues/1034 + if (item != null && item.getType() == Material.TNT) { + Events.fireToCancel(event, new PlaceBlockEvent(event, causes, placed.getLocation(), Material.TNT)); + } } // TODO: Inventory events? diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java index baea5c6e..6ad8ec73 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java @@ -38,7 +38,7 @@ private Events() { * * @param eventToFire the event to fire * @param an event that can be fired and is cancellable - * @return true if the event was fired + * @return true if the event was cancelled */ public static boolean fireAndTestCancel( T eventToFire) { Bukkit.getServer().getPluginManager().callEvent(eventToFire); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/Materials.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Materials.java index 317b5a5c..12e1a095 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/Materials.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Materials.java @@ -36,8 +36,8 @@ */ public final class Materials { - private static final int MODIFED_ON_CLICK = 1; - private static final int MODIFIES_BLOCKS = 1; + private static final int MODIFIED_ON_CLICK = 1; + private static final int MODIFIES_BLOCKS = 2; private static final BiMap ENTITY_ITEMS = HashBiMap.create(); private static final Map MATERIAL_FLAGS = new HashMap(); @@ -86,10 +86,10 @@ public final class Materials { MATERIAL_FLAGS.put(Material.GLASS, 0); MATERIAL_FLAGS.put(Material.LAPIS_ORE, 0); MATERIAL_FLAGS.put(Material.LAPIS_BLOCK, 0); - MATERIAL_FLAGS.put(Material.DISPENSER, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.DISPENSER, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.SANDSTONE, 0); - MATERIAL_FLAGS.put(Material.NOTE_BLOCK, MODIFED_ON_CLICK); - MATERIAL_FLAGS.put(Material.BED_BLOCK, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.NOTE_BLOCK, MODIFIED_ON_CLICK); + MATERIAL_FLAGS.put(Material.BED_BLOCK, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.POWERED_RAIL, 0); MATERIAL_FLAGS.put(Material.DETECTOR_RAIL, 0); MATERIAL_FLAGS.put(Material.PISTON_STICKY_BASE, 0); @@ -109,7 +109,7 @@ public final class Materials { MATERIAL_FLAGS.put(Material.DOUBLE_STEP, 0); MATERIAL_FLAGS.put(Material.STEP, 0); MATERIAL_FLAGS.put(Material.BRICK, 0); - MATERIAL_FLAGS.put(Material.TNT, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.TNT, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.BOOKSHELF, 0); MATERIAL_FLAGS.put(Material.MOSSY_COBBLESTONE, 0); MATERIAL_FLAGS.put(Material.OBSIDIAN, 0); @@ -117,37 +117,37 @@ public final class Materials { MATERIAL_FLAGS.put(Material.FIRE, 0); MATERIAL_FLAGS.put(Material.MOB_SPAWNER, 0); MATERIAL_FLAGS.put(Material.WOOD_STAIRS, 0); - MATERIAL_FLAGS.put(Material.CHEST, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.CHEST, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.REDSTONE_WIRE, 0); MATERIAL_FLAGS.put(Material.DIAMOND_ORE, 0); MATERIAL_FLAGS.put(Material.DIAMOND_BLOCK, 0); MATERIAL_FLAGS.put(Material.WORKBENCH, 0); MATERIAL_FLAGS.put(Material.CROPS, 0); MATERIAL_FLAGS.put(Material.SOIL, 0); - MATERIAL_FLAGS.put(Material.FURNACE, MODIFED_ON_CLICK); - MATERIAL_FLAGS.put(Material.BURNING_FURNACE, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.FURNACE, MODIFIED_ON_CLICK); + MATERIAL_FLAGS.put(Material.BURNING_FURNACE, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.SIGN_POST, 0); MATERIAL_FLAGS.put(Material.WOODEN_DOOR, 0); MATERIAL_FLAGS.put(Material.LADDER, 0); MATERIAL_FLAGS.put(Material.RAILS, 0); MATERIAL_FLAGS.put(Material.COBBLESTONE_STAIRS, 0); MATERIAL_FLAGS.put(Material.WALL_SIGN, 0); - MATERIAL_FLAGS.put(Material.LEVER, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.LEVER, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.STONE_PLATE, 0); - MATERIAL_FLAGS.put(Material.IRON_DOOR_BLOCK, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.IRON_DOOR_BLOCK, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.WOOD_PLATE, 0); MATERIAL_FLAGS.put(Material.REDSTONE_ORE, 0); MATERIAL_FLAGS.put(Material.GLOWING_REDSTONE_ORE, 0); MATERIAL_FLAGS.put(Material.REDSTONE_TORCH_OFF, 0); MATERIAL_FLAGS.put(Material.REDSTONE_TORCH_ON, 0); - MATERIAL_FLAGS.put(Material.STONE_BUTTON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.STONE_BUTTON, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.SNOW, 0); MATERIAL_FLAGS.put(Material.ICE, 0); MATERIAL_FLAGS.put(Material.SNOW_BLOCK, 0); MATERIAL_FLAGS.put(Material.CACTUS, 0); MATERIAL_FLAGS.put(Material.CLAY, 0); MATERIAL_FLAGS.put(Material.SUGAR_CANE_BLOCK, 0); - MATERIAL_FLAGS.put(Material.JUKEBOX, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.JUKEBOX, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.FENCE, 0); MATERIAL_FLAGS.put(Material.PUMPKIN, 0); MATERIAL_FLAGS.put(Material.NETHERRACK, 0); @@ -155,11 +155,11 @@ public final class Materials { MATERIAL_FLAGS.put(Material.GLOWSTONE, 0); MATERIAL_FLAGS.put(Material.PORTAL, 0); MATERIAL_FLAGS.put(Material.JACK_O_LANTERN, 0); - MATERIAL_FLAGS.put(Material.CAKE_BLOCK, MODIFED_ON_CLICK); - MATERIAL_FLAGS.put(Material.DIODE_BLOCK_OFF, MODIFED_ON_CLICK); - MATERIAL_FLAGS.put(Material.DIODE_BLOCK_ON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.CAKE_BLOCK, MODIFIED_ON_CLICK); + MATERIAL_FLAGS.put(Material.DIODE_BLOCK_OFF, MODIFIED_ON_CLICK); + MATERIAL_FLAGS.put(Material.DIODE_BLOCK_ON, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.STAINED_GLASS, 0); - MATERIAL_FLAGS.put(Material.TRAP_DOOR, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.TRAP_DOOR, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.MONSTER_EGGS, 0); MATERIAL_FLAGS.put(Material.SMOOTH_BRICK, 0); MATERIAL_FLAGS.put(Material.HUGE_MUSHROOM_1, 0); @@ -170,7 +170,7 @@ public final class Materials { MATERIAL_FLAGS.put(Material.PUMPKIN_STEM, 0); MATERIAL_FLAGS.put(Material.MELON_STEM, 0); MATERIAL_FLAGS.put(Material.VINE, 0); - MATERIAL_FLAGS.put(Material.FENCE_GATE, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.FENCE_GATE, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.BRICK_STAIRS, 0); MATERIAL_FLAGS.put(Material.SMOOTH_STAIRS, 0); MATERIAL_FLAGS.put(Material.MYCEL, 0); @@ -180,12 +180,12 @@ public final class Materials { MATERIAL_FLAGS.put(Material.NETHER_BRICK_STAIRS, 0); MATERIAL_FLAGS.put(Material.NETHER_WARTS, 0); MATERIAL_FLAGS.put(Material.ENCHANTMENT_TABLE, 0); - MATERIAL_FLAGS.put(Material.BREWING_STAND, MODIFED_ON_CLICK); - MATERIAL_FLAGS.put(Material.CAULDRON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.BREWING_STAND, MODIFIED_ON_CLICK); + MATERIAL_FLAGS.put(Material.CAULDRON, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.ENDER_PORTAL, 0); MATERIAL_FLAGS.put(Material.ENDER_PORTAL_FRAME, 0); MATERIAL_FLAGS.put(Material.ENDER_STONE, 0); - MATERIAL_FLAGS.put(Material.DRAGON_EGG, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.DRAGON_EGG, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.REDSTONE_LAMP_OFF, 0); MATERIAL_FLAGS.put(Material.REDSTONE_LAMP_ON, 0); MATERIAL_FLAGS.put(Material.WOOD_DOUBLE_STEP, 0); @@ -200,28 +200,28 @@ public final class Materials { MATERIAL_FLAGS.put(Material.SPRUCE_WOOD_STAIRS, 0); MATERIAL_FLAGS.put(Material.BIRCH_WOOD_STAIRS, 0); MATERIAL_FLAGS.put(Material.JUNGLE_WOOD_STAIRS, 0); - MATERIAL_FLAGS.put(Material.COMMAND, MODIFED_ON_CLICK); - MATERIAL_FLAGS.put(Material.BEACON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.COMMAND, MODIFIED_ON_CLICK); + MATERIAL_FLAGS.put(Material.BEACON, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.COBBLE_WALL, 0); - MATERIAL_FLAGS.put(Material.FLOWER_POT, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.FLOWER_POT, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.CARROT, 0); MATERIAL_FLAGS.put(Material.POTATO, 0); - MATERIAL_FLAGS.put(Material.WOOD_BUTTON, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.WOOD_BUTTON, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.SKULL, 0); - MATERIAL_FLAGS.put(Material.ANVIL, MODIFED_ON_CLICK); - MATERIAL_FLAGS.put(Material.TRAPPED_CHEST, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.ANVIL, MODIFIED_ON_CLICK); + MATERIAL_FLAGS.put(Material.TRAPPED_CHEST, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.GOLD_PLATE, 0); MATERIAL_FLAGS.put(Material.IRON_PLATE, 0); - MATERIAL_FLAGS.put(Material.REDSTONE_COMPARATOR_OFF, MODIFED_ON_CLICK); - MATERIAL_FLAGS.put(Material.REDSTONE_COMPARATOR_ON, MODIFED_ON_CLICK); - MATERIAL_FLAGS.put(Material.DAYLIGHT_DETECTOR, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.REDSTONE_COMPARATOR_OFF, MODIFIED_ON_CLICK); + MATERIAL_FLAGS.put(Material.REDSTONE_COMPARATOR_ON, MODIFIED_ON_CLICK); + MATERIAL_FLAGS.put(Material.DAYLIGHT_DETECTOR, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.REDSTONE_BLOCK, 0); MATERIAL_FLAGS.put(Material.QUARTZ_ORE, 0); - MATERIAL_FLAGS.put(Material.HOPPER, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.HOPPER, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.QUARTZ_BLOCK, 0); MATERIAL_FLAGS.put(Material.QUARTZ_STAIRS, 0); MATERIAL_FLAGS.put(Material.ACTIVATOR_RAIL, 0); - MATERIAL_FLAGS.put(Material.DROPPER, MODIFED_ON_CLICK); + MATERIAL_FLAGS.put(Material.DROPPER, MODIFIED_ON_CLICK); MATERIAL_FLAGS.put(Material.STAINED_CLAY, 0); MATERIAL_FLAGS.put(Material.STAINED_GLASS_PANE, 0); MATERIAL_FLAGS.put(Material.LEAVES_2, 0); @@ -422,6 +422,30 @@ public static Material getRelatedMaterial(EntityType type) { return ENTITY_ITEMS.get(type); } + /** + * Get the material of the block placed by the given bucket, defaulting + * to water if the bucket type is not known. + * + *

If a non-bucket material is given, it will be assumed to be + * an unknown bucket type. If the given bucket doesn't have a block form + * (it can't be placed), then water will be returned (i.e. for milk). + * Be aware that either the stationary or non-stationary material may be + * returned.

+ * + * @param type the bucket material + * @return the block material + */ + public static Material getBucketBlockMaterial(Material type) { + switch (type) { + case LAVA_BUCKET: + return Material.LAVA; + case MILK_BUCKET: + return Material.WATER; + default: + return Material.WATER; + } + } + /** * Test whether the given material is a mushroom. * @@ -442,6 +466,16 @@ public static boolean isLeaf(Material material) { return material == Material.LEAVES || material == Material.LEAVES_2; } + /** + * Test whether the given material is a liquid block. + * + * @param material the material + * @return true if a liquid block + */ + public static boolean isLiquid(Material material) { + return isWater(material) || isLava(material); + } + /** * Test whether the given material is water. * @@ -542,13 +576,13 @@ public static boolean isInventoryBlock(Material material) { */ public static boolean isBlockModifiedOnClick(Material material) { Integer flags = MATERIAL_FLAGS.get(material); - return flags == null || (flags & MODIFED_ON_CLICK) == MODIFED_ON_CLICK; + return flags == null || (flags & MODIFIED_ON_CLICK) == MODIFIED_ON_CLICK; } /** * Test whether the given item modifies a given block when right clicked. * - *

This test is conservative, returning true for blocks that it is not + *

This test is conservative, returning true for items that it is not * aware of or does not have the details for.

* * @param item the item diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/WGMetadata.java b/src/main/java/com/sk89q/worldguard/bukkit/util/WGMetadata.java new file mode 100644 index 00000000..6aecfa60 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/WGMetadata.java @@ -0,0 +1,78 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.util; + +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.metadata.MetadataValue; +import org.bukkit.metadata.Metadatable; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * Utility methods for dealing with metadata on entities. + * + *

WorldGuard is placed as the owner of all values.

+ */ +public final class WGMetadata { + + private WGMetadata() { + } + + /** + * Add some metadata to a target. + * + * @param target the target + * @param key the key + * @param value the value + */ + public static void put(Metadatable target, String key, Object value) { + target.setMetadata(key, new FixedMetadataValue(WorldGuardPlugin.inst(), value)); + } + + /** + * Get the (first) metadata value on the given target that has the given + * key and is of the given class type. + * + * @param target the target + * @param key the key + * @param expected the type of the value + * @param the type of the value + * @return a value, or {@code null} if one does not exists + */ + @Nullable + @SuppressWarnings("unchecked") + public static T getIfPresent(Metadatable target, String key, Class expected) { + List values = target.getMetadata(key); + WorldGuardPlugin owner = WorldGuardPlugin.inst(); + for (MetadataValue value : values) { + if (value.getOwningPlugin() == owner) { + Object v = value.value(); + if (expected.isInstance(v)) { + return (T) v; + } + } + } + + return null; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java b/src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java index 76fea257..e1c7998d 100644 --- a/src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java @@ -45,4 +45,9 @@ public Block get() { return block; } + @Override + public String toString() { + return block.toString(); + } + } diff --git a/src/main/java/com/sk89q/worldguard/util/cause/Causes.java b/src/main/java/com/sk89q/worldguard/util/cause/Causes.java index 6cd9532f..4f171369 100644 --- a/src/main/java/com/sk89q/worldguard/util/cause/Causes.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/Causes.java @@ -90,7 +90,7 @@ public static List> create(Object ... cause) { } else if (o instanceof Block) { causes.add(new BlockCause((Block) o)); } else if (o instanceof Projectile) { - causes.addAll(create(o)); + causes.addAll(create(((Projectile) o).getShooter())); causes.add(new EntityCause((Entity) o)); } else if (o instanceof Entity) { causes.add(new EntityCause((Entity) o)); diff --git a/src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java b/src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java index 893da290..09e962dc 100644 --- a/src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java @@ -45,4 +45,8 @@ public Entity get() { return entity; } + @Override + public String toString() { + return entity.toString(); + } } diff --git a/src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java b/src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java index f016b270..03a473b3 100644 --- a/src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java @@ -45,4 +45,9 @@ public Player get() { return player; } + @Override + public String toString() { + return player.getName(); + } + } diff --git a/src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java b/src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java index 9216c409..40cfcfc6 100644 --- a/src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java +++ b/src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java @@ -43,4 +43,8 @@ public Object get() { return cause; } + @Override + public String toString() { + return "unknown(" + cause + ")"; + } } From 0fc3d21b90beb511fe228ebafdc94c57f65ac691 Mon Sep 17 00:00:00 2001 From: sk89q Date: Mon, 11 Aug 2014 20:08:56 -0700 Subject: [PATCH 025/133] Add abstracted event debugging listener. --- .../bukkit/listener/DebuggingListener.java | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java new file mode 100644 index 00000000..83b1ecaa --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java @@ -0,0 +1,169 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.google.common.base.Joiner; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; +import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; +import com.sk89q.worldguard.bukkit.event.entity.DestroyEntityEvent; +import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; +import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; +import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; +import com.sk89q.worldguard.util.cause.Cause; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; + +import java.util.List; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class DebuggingListener extends AbstractListener { + + private final Logger logger; + + /** + * Construct the listener. + * + * @param plugin an instance of WorldGuardPlugin + * @param logger the logger + */ + public DebuggingListener(WorldGuardPlugin plugin, Logger logger) { + super(plugin); + checkNotNull(logger); + this.logger = logger; + } + + @EventHandler + public void onPlaceBlock(PlaceBlockEvent event) { + StringBuilder builder = new StringBuilder(); + builder.append("PLACE"); + builder.append(" "); + builder.append("").append(event.getEffectiveMaterial()); + builder.append(" "); + builder.append("@").append(toBlockString(event.getTarget())); + builder.append(" "); + builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append(" "); + builder.append(":").append(event.getOriginalEvent().getEventName()); + logger.info(builder.toString()); + } + + @EventHandler + public void onBreakBlock(BreakBlockEvent event) { + StringBuilder builder = new StringBuilder(); + builder.append("DIG"); + builder.append(" "); + builder.append("").append(event.getEffectiveMaterial()); + builder.append(" "); + builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append(" "); + builder.append("@").append(toBlockString(event.getTarget())); + builder.append(" "); + builder.append(":").append(event.getOriginalEvent().getEventName()); + logger.info(builder.toString()); + } + + @EventHandler + public void onUseBlock(UseBlockEvent event) { + StringBuilder builder = new StringBuilder(); + builder.append("INTERACT"); + builder.append(" "); + builder.append("").append(event.getEffectiveMaterial()); + builder.append(" "); + builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append(" "); + builder.append("@").append(toBlockString(event.getTarget())); + builder.append(" "); + builder.append(":").append(event.getOriginalEvent().getEventName()); + logger.info(builder.toString()); + } + + @EventHandler + public void onSpawnEntity(SpawnEntityEvent event) { + StringBuilder builder = new StringBuilder(); + builder.append("SPAWN"); + builder.append(" "); + builder.append("").append(event.getEffectiveType()); + builder.append(" "); + builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append(" "); + builder.append("@").append(toBlockString(event.getTarget())); + builder.append(" "); + builder.append(":").append(event.getOriginalEvent().getEventName()); + logger.info(builder.toString()); + } + + @EventHandler + public void onDestroyEntity(DestroyEntityEvent event) { + StringBuilder builder = new StringBuilder(); + builder.append("DESTROY"); + builder.append(" "); + builder.append("").append(event.getEntity().getType()); + builder.append(" "); + builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append(" "); + builder.append("@").append(toBlockString(event.getTarget())); + builder.append(" "); + builder.append(":").append(event.getOriginalEvent().getEventName()); + logger.info(builder.toString()); + } + + @EventHandler + public void onUseEntity(UseEntityEvent event) { + StringBuilder builder = new StringBuilder(); + builder.append("INTERACT"); + builder.append(" "); + builder.append("").append(event.getEntity().getType()); + builder.append(" "); + builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append(" "); + builder.append("@").append(toBlockString(event.getTarget())); + builder.append(" "); + builder.append(":").append(event.getOriginalEvent().getEventName()); + logger.info(builder.toString()); + } + + @EventHandler + public void onUseItem(UseItemEvent event) { + StringBuilder builder = new StringBuilder(); + builder.append("USE"); + builder.append(" "); + builder.append("").append(event.getItemStack().getType()); + builder.append(" "); + builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append(" "); + builder.append("@").append(event.getWorld().getName()); + builder.append(" "); + builder.append(":").append(event.getOriginalEvent().getEventName()); + logger.info(builder.toString()); + } + + private static String toCause(List> causes) { + return Joiner.on("|").join(causes); + } + + private static String toBlockString(Location location) { + return location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ(); + } + +} From f92e55e0616d6be3b3836d625e6406b53d424bbd Mon Sep 17 00:00:00 2001 From: sk89q Date: Tue, 12 Aug 2014 19:48:20 -0700 Subject: [PATCH 026/133] Simplfy Cause objects. --- .../worldguard/bukkit/WorldGuardPlugin.java | 2 + .../sk89q/worldguard/bukkit/cause/Cause.java | 132 ++++++++++++++++++ .../bukkit/event/AbstractInteractEvent.java | 24 ++-- .../event/block/AbstractBlockEvent.java | 11 +- .../bukkit/event/block/BreakBlockEvent.java | 12 +- .../bukkit/event/block/PlaceBlockEvent.java | 12 +- .../bukkit/event/block/UseBlockEvent.java | 12 +- .../event/entity/AbstractEntityEvent.java | 25 +--- .../event/entity/DestroyEntityEvent.java | 14 +- .../bukkit/event/entity/SpawnEntityEvent.java | 12 +- .../bukkit/event/entity/UseEntityEvent.java | 14 +- .../bukkit/event/inventory/UseItemEvent.java | 16 +-- .../bukkit/listener/BlacklistListener.java | 15 +- .../listener/BlockedPotionsListener.java | 5 +- .../listener/ChestProtectionListener.java | 7 +- .../bukkit/listener/DebuggingListener.java | 23 ++- .../listener/EventAbstractionListener.java | 77 +++++----- .../listener/RegionProtectionListener.java | 19 ++- .../worldguard/util/cause/BlockCause.java | 53 ------- .../sk89q/worldguard/util/cause/Cause.java | 38 ----- .../sk89q/worldguard/util/cause/Causes.java | 104 -------------- .../worldguard/util/cause/EntityCause.java | 52 ------- .../worldguard/util/cause/PlayerCause.java | 53 ------- .../worldguard/util/cause/UnknownCause.java | 50 ------- 24 files changed, 249 insertions(+), 533 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java delete mode 100644 src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java delete mode 100644 src/main/java/com/sk89q/worldguard/util/cause/Cause.java delete mode 100644 src/main/java/com/sk89q/worldguard/util/cause/Causes.java delete mode 100644 src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java delete mode 100644 src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java delete mode 100644 src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 75e8a2b6..99cf1b65 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -36,6 +36,7 @@ import com.sk89q.worldguard.bukkit.listener.BlacklistListener; import com.sk89q.worldguard.bukkit.listener.BlockedPotionsListener; import com.sk89q.worldguard.bukkit.listener.ChestProtectionListener; +import com.sk89q.worldguard.bukkit.listener.DebuggingListener; import com.sk89q.worldguard.bukkit.listener.EventAbstractionListener; import com.sk89q.worldguard.bukkit.listener.RegionProtectionListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardBlockListener; @@ -202,6 +203,7 @@ public void run() { (new RegionProtectionListener(this)).registerEvents(); (new BlockedPotionsListener(this)).registerEvents(); (new EventAbstractionListener(this)).registerEvents(); + (new DebuggingListener(this, getLogger())).registerEvents(); configuration.updateCommandBookGodMode(); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java b/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java new file mode 100644 index 00000000..c9adfe32 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java @@ -0,0 +1,132 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.cause; + +import com.google.common.base.Joiner; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An instance of this object describes the actors that played a role in + * causing an event, with the ability to describe a situation where one actor + * controls several other actors to create the event. + * + *

For example, if a player fires an arrow that hits an item frame, the player + * is the initiator, while the arrow is merely controlled by the player to + * hit the item frame.

+ */ +public class Cause { + + private static final Cause UNKNOWN = new Cause(Collections.emptyList()); + + private final List causes; + + /** + * Create a new instance. + * + * @param causes a list of causes + */ + private Cause(List causes) { + checkNotNull(causes); + this.causes = causes; + } + + /** + * Return whether a cause is known. + * + * @return true if known + */ + public boolean isKnown() { + return !causes.isEmpty(); + } + + @Nullable + public Player getPlayerRootCause() { + for (Object object : causes) { + if (object instanceof Player) { + return (Player) object; + } + } + + return null; + } + + @Override + public String toString() { + return Joiner.on(" | ").join(causes); + } + + /** + * Expand an cause object. + * + * @param list the list to add elements to + * @param element an array of objects + */ + private static void expand(List list, @Nullable Object ... element) { + if (element != null) { + for (Object o : element) { + if (o == null) { + continue; + } + + if (o instanceof Projectile) { + expand(list, ((Projectile) o).getShooter()); + } else { + list.add(o); + } + } + } + } + + /** + * Create a new instance with the given objects as the cause, + * where the first-most object is the initial initiator and those + * following it are controlled by the previous entry. + * + * @param cause an array of causing objects + * @return a cause + */ + public static Cause create(@Nullable Object ... cause) { + if (cause != null) { + List causes = new ArrayList(cause.length); + expand(causes, cause); + return new Cause(causes); + } else { + return UNKNOWN; + } + } + + /** + * Create a new instance that indicates that the cause is not known. + * + * @return a cause + */ + public static Cause unknown() { + return UNKNOWN; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/AbstractInteractEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/AbstractInteractEvent.java index a2a8d22e..0c4aa410 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/AbstractInteractEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/AbstractInteractEvent.java @@ -19,32 +19,29 @@ package com.sk89q.worldguard.bukkit.event; -import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; -import java.util.Collections; -import java.util.List; - import static com.google.common.base.Preconditions.checkNotNull; public abstract class AbstractInteractEvent extends Event implements Cancellable { private final Event originalEvent; - private final List> causes; + private final Cause cause; private boolean cancelled; /** * Create a new instance * * @param originalEvent the original event - * @param causes a list of causes, where the originating causes are at the beginning + * @param cause the cause */ - protected AbstractInteractEvent(Event originalEvent, List> causes) { + protected AbstractInteractEvent(Event originalEvent, Cause cause) { checkNotNull(originalEvent); - checkNotNull(causes); + checkNotNull(cause); this.originalEvent = originalEvent; - this.causes = causes; + this.cause = cause; } /** @@ -57,13 +54,12 @@ public Event getOriginalEvent() { } /** - * Return an unmodifiable list of causes, where the originating causes are - * at the beginning of the list. + * Return the cause. * - * @return a list of causes + * @return the cause */ - public List> getCauses() { - return Collections.unmodifiableList(causes); + public Cause getCause() { + return cause; } @Override diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java index a9f423ef..1d01971d 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java @@ -19,7 +19,7 @@ package com.sk89q.worldguard.bukkit.event.block; -import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.cause.Cause; import com.sk89q.worldguard.bukkit.event.AbstractInteractEvent; import org.bukkit.Location; import org.bukkit.Material; @@ -27,7 +27,6 @@ import org.bukkit.event.Event; import javax.annotation.Nullable; -import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -38,16 +37,16 @@ abstract class AbstractBlockEvent extends AbstractInteractEvent { private final Block block; private final Material effectiveMaterial; - protected AbstractBlockEvent(Event originalEvent, List> causes, Block block) { - super(originalEvent, causes); + protected AbstractBlockEvent(Event originalEvent, Cause cause, Block block) { + super(originalEvent, cause); checkNotNull(block); this.target = block.getLocation(); this.block = block; this.effectiveMaterial = block.getType(); } - protected AbstractBlockEvent(Event originalEvent, List> causes, Location target, Material effectiveMaterial) { - super(originalEvent, causes); + protected AbstractBlockEvent(Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { + super(originalEvent, cause); this.target = target; this.block = null; this.effectiveMaterial = effectiveMaterial; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java index fb929316..e3647c62 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java @@ -19,25 +19,23 @@ package com.sk89q.worldguard.bukkit.event.block; -import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import java.util.List; - public class BreakBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); - public BreakBlockEvent(Event originalEvent, List> causes, Block block) { - super(originalEvent, causes, block); + public BreakBlockEvent(Event originalEvent, Cause cause, Block block) { + super(originalEvent, cause, block); } - public BreakBlockEvent(Event originalEvent, List> causes, Location target, Material effectiveMaterial) { - super(originalEvent, causes, target, effectiveMaterial); + public BreakBlockEvent(Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { + super(originalEvent, cause, target, effectiveMaterial); } @Override diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java index 68d0d7c9..7a02a876 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java @@ -19,25 +19,23 @@ package com.sk89q.worldguard.bukkit.event.block; -import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import java.util.List; - public class PlaceBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); - public PlaceBlockEvent(Event originalEvent, List> causes, Block block) { - super(originalEvent, causes, block); + public PlaceBlockEvent(Event originalEvent, Cause cause, Block block) { + super(originalEvent, cause, block); } - public PlaceBlockEvent(Event originalEvent, List> causes, Location target, Material effectiveMaterial) { - super(originalEvent, causes, target, effectiveMaterial); + public PlaceBlockEvent(Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { + super(originalEvent, cause, target, effectiveMaterial); } @Override diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java index 21ba32de..bd22dbf0 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java @@ -19,15 +19,13 @@ package com.sk89q.worldguard.bukkit.event.block; -import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import java.util.List; - /** * Fired when a block is interacted with. */ @@ -35,12 +33,12 @@ public class UseBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); - public UseBlockEvent(Event originalEvent, List> causes, Block block) { - super(originalEvent, causes, block); + public UseBlockEvent(Event originalEvent, Cause cause, Block block) { + super(originalEvent, cause, block); } - public UseBlockEvent(Event originalEvent, List> causes, Location target, Material effectiveMaterial) { - super(originalEvent, causes, target, effectiveMaterial); + public UseBlockEvent(Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { + super(originalEvent, cause, target, effectiveMaterial); } @Override diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java index bb723f4b..093325e8 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java @@ -19,14 +19,13 @@ package com.sk89q.worldguard.bukkit.event.entity; -import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.cause.Cause; import com.sk89q.worldguard.bukkit.event.AbstractInteractEvent; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.event.Event; import javax.annotation.Nullable; -import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -36,29 +35,15 @@ abstract class AbstractEntityEvent extends AbstractInteractEvent { @Nullable private final Entity entity; - /** - * Create a new instance - * - * @param originalEvent the original event - * @param causes a list of causes, where the originating causes are at the beginning - * @param entity the target - */ - protected AbstractEntityEvent(Event originalEvent, List> causes, Entity entity) { - super(originalEvent, causes); + protected AbstractEntityEvent(Event originalEvent, Cause cause, Entity entity) { + super(originalEvent, cause); checkNotNull(entity); this.target = entity.getLocation(); this.entity = entity; } - /** - * Create a new instance - * - * @param originalEvent the original event - * @param causes a list of causes, where the originating causes are at the beginning - * @param target the target - */ - protected AbstractEntityEvent(Event originalEvent, List> causes, Location target) { - super(originalEvent, causes); + protected AbstractEntityEvent(Event originalEvent, Cause cause, Location target) { + super(originalEvent, cause); checkNotNull(target); this.target = target; this.entity = null; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java index 3b74ee09..db53d8eb 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java @@ -19,13 +19,12 @@ package com.sk89q.worldguard.bukkit.event.entity; -import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.entity.Entity; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import javax.annotation.Nonnull; -import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -33,15 +32,8 @@ public class DestroyEntityEvent extends AbstractEntityEvent { private static final HandlerList handlers = new HandlerList(); - /** - * Create a new instance. - * - * @param originalEvent the original event - * @param causes a list of causes, where the originating causes are at the beginning - * @param target the target entity being affected - */ - public DestroyEntityEvent(Event originalEvent, List> causes, Entity target) { - super(originalEvent, causes, checkNotNull(target)); + public DestroyEntityEvent(Event originalEvent, Cause cause, Entity target) { + super(originalEvent, cause, checkNotNull(target)); } @Override diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java index bfd41a9f..05fee2f7 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java @@ -19,15 +19,13 @@ package com.sk89q.worldguard.bukkit.event.entity; -import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import java.util.List; - import static com.google.common.base.Preconditions.checkNotNull; public class SpawnEntityEvent extends AbstractEntityEvent { @@ -35,13 +33,13 @@ public class SpawnEntityEvent extends AbstractEntityEvent { private static final HandlerList handlers = new HandlerList(); private final EntityType effectiveType; - public SpawnEntityEvent(Event originalEvent, List> causes, Entity target) { - super(originalEvent, causes, checkNotNull(target)); + public SpawnEntityEvent(Event originalEvent, Cause cause, Entity target) { + super(originalEvent, cause, checkNotNull(target)); this.effectiveType = target.getType(); } - public SpawnEntityEvent(Event originalEvent, List> causes, Location location, EntityType type) { - super(originalEvent, causes, location); + public SpawnEntityEvent(Event originalEvent, Cause cause, Location location, EntityType type) { + super(originalEvent, cause, location); checkNotNull(type); this.effectiveType = type; } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java index 08933202..66b34282 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java @@ -19,13 +19,12 @@ package com.sk89q.worldguard.bukkit.event.entity; -import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.entity.Entity; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import javax.annotation.Nonnull; -import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -36,15 +35,8 @@ public class UseEntityEvent extends AbstractEntityEvent { private static final HandlerList handlers = new HandlerList(); - /** - * Create a new instance. - * - * @param originalEvent the original event - * @param causes a list of causes, where the originating causes are at the beginning - * @param target the target entity being affected - */ - public UseEntityEvent(Event originalEvent, List> causes, Entity target) { - super(originalEvent, causes, checkNotNull(target)); + public UseEntityEvent(Event originalEvent, Cause cause, Entity target) { + super(originalEvent, cause, checkNotNull(target)); } @Override diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java index 2d35d804..acd0185e 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java @@ -19,15 +19,13 @@ package com.sk89q.worldguard.bukkit.event.inventory; -import com.sk89q.worldguard.util.cause.Cause; +import com.sk89q.worldguard.bukkit.cause.Cause; import com.sk89q.worldguard.bukkit.event.AbstractInteractEvent; import org.bukkit.World; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.bukkit.inventory.ItemStack; -import java.util.List; - import static com.google.common.base.Preconditions.checkNotNull; /** @@ -39,16 +37,8 @@ public class UseItemEvent extends AbstractInteractEvent { private final World world; private final ItemStack itemStack; - /** - * Create a new instance. - * - * @param originalEvent the original event - * @param causes a list of causes, where the originating causes are at the beginning - * @param world the world - * @param itemStack the item - */ - public UseItemEvent(Event originalEvent, List> causes, World world, ItemStack itemStack) { - super(originalEvent, causes); + public UseItemEvent(Event originalEvent, Cause cause, World world, ItemStack itemStack) { + super(originalEvent, cause); checkNotNull(world); checkNotNull(itemStack); this.world = world; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java index 7399d3ea..549981fe 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java @@ -31,14 +31,13 @@ import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.bukkit.util.Materials; -import com.sk89q.worldguard.util.cause.Causes; import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; import com.sk89q.worldguard.bukkit.event.entity.DestroyEntityEvent; import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; +import com.sk89q.worldguard.bukkit.util.Materials; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Entity; @@ -70,7 +69,7 @@ public BlacklistListener(WorldGuardPlugin plugin) { @EventHandler(ignoreCancelled = true) public void onBreakBlock(BreakBlockEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; @@ -96,7 +95,7 @@ public void onBreakBlock(BreakBlockEvent event) { @EventHandler(ignoreCancelled = true) public void onPlaceBlock(PlaceBlockEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; @@ -119,7 +118,7 @@ localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiv @EventHandler(ignoreCancelled = true) public void onUseBlock(UseBlockEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; @@ -142,7 +141,7 @@ localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiv @EventHandler(ignoreCancelled = true) public void onSpawnEntity(SpawnEntityEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; @@ -166,7 +165,7 @@ public void onSpawnEntity(SpawnEntityEvent event) { @EventHandler(ignoreCancelled = true) public void onDestroyEntity(DestroyEntityEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; @@ -192,7 +191,7 @@ public void onDestroyEntity(DestroyEntityEvent event) { @EventHandler(ignoreCancelled = true) public void onUseItem(UseItemEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/BlockedPotionsListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlockedPotionsListener.java index a02a979c..96c32236 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/BlockedPotionsListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlockedPotionsListener.java @@ -23,7 +23,6 @@ import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.util.cause.Causes; import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -51,7 +50,7 @@ public BlockedPotionsListener(WorldGuardPlugin plugin) { @EventHandler public void onItemInteract(UseItemEvent event) { // We only care about player caused events - if (!Causes.mayInvolvePlayer(event.getCauses())) { + if (event.getCause().getPlayerRootCause() == null) { return; } @@ -84,7 +83,7 @@ public void onItemInteract(UseItemEvent event) { } if (blockedEffect != null) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); if (player != null) { if (getPlugin().hasPermission(player, "worldguard.override.potions")) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java index fc91edbb..9f8536c0 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java @@ -22,7 +22,6 @@ import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.util.cause.Causes; import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; @@ -48,7 +47,7 @@ public ChestProtectionListener(WorldGuardPlugin plugin) { @EventHandler(ignoreCancelled = true) public void onPlaceBlock(PlaceBlockEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); Location target = event.getTarget(); if (player != null) { @@ -68,7 +67,7 @@ public void onPlaceBlock(PlaceBlockEvent event) { @EventHandler(ignoreCancelled = true) public void onBreakBlock(BreakBlockEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); Location target = event.getTarget(); WorldConfiguration wcfg = getWorldConfig(target.getWorld()); @@ -93,7 +92,7 @@ public void onBreakBlock(BreakBlockEvent event) { @EventHandler(ignoreCancelled = true) public void onUseBlock(UseBlockEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); Location target = event.getTarget(); WorldConfiguration wcfg = getWorldConfig(target.getWorld()); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java index 83b1ecaa..4943be13 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java @@ -19,7 +19,6 @@ package com.sk89q.worldguard.bukkit.listener; -import com.google.common.base.Joiner; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; @@ -28,11 +27,9 @@ import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; -import com.sk89q.worldguard.util.cause.Cause; import org.bukkit.Location; import org.bukkit.event.EventHandler; -import java.util.List; import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; @@ -62,7 +59,7 @@ public void onPlaceBlock(PlaceBlockEvent event) { builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); - builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append("[").append(event.getCause()).append("]"); builder.append(" "); builder.append(":").append(event.getOriginalEvent().getEventName()); logger.info(builder.toString()); @@ -75,7 +72,7 @@ public void onBreakBlock(BreakBlockEvent event) { builder.append(" "); builder.append("").append(event.getEffectiveMaterial()); builder.append(" "); - builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append("[").append(event.getCause()).append("]"); builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); @@ -90,7 +87,7 @@ public void onUseBlock(UseBlockEvent event) { builder.append(" "); builder.append("").append(event.getEffectiveMaterial()); builder.append(" "); - builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append("[").append(event.getCause()).append("]"); builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); @@ -105,7 +102,7 @@ public void onSpawnEntity(SpawnEntityEvent event) { builder.append(" "); builder.append("").append(event.getEffectiveType()); builder.append(" "); - builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append("[").append(event.getCause()).append("]"); builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); @@ -120,7 +117,7 @@ public void onDestroyEntity(DestroyEntityEvent event) { builder.append(" "); builder.append("").append(event.getEntity().getType()); builder.append(" "); - builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append("[").append(event.getCause()).append("]"); builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); @@ -135,7 +132,7 @@ public void onUseEntity(UseEntityEvent event) { builder.append(" "); builder.append("").append(event.getEntity().getType()); builder.append(" "); - builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append("[").append(event.getCause()).append("]"); builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); @@ -150,18 +147,14 @@ public void onUseItem(UseItemEvent event) { builder.append(" "); builder.append("").append(event.getItemStack().getType()); builder.append(" "); - builder.append("[").append(toCause(event.getCauses())).append("]"); + builder.append("[").append(event.getCause()).append("]"); builder.append(" "); builder.append("@").append(event.getWorld().getName()); builder.append(" "); builder.append(":").append(event.getOriginalEvent().getEventName()); logger.info(builder.toString()); } - - private static String toCause(List> causes) { - return Joiner.on("|").join(causes); - } - + private static String toBlockString(Location location) { return location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ(); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java index d6d0c757..c4e83da4 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java @@ -20,6 +20,7 @@ package com.sk89q.worldguard.bukkit.listener; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.cause.Cause; import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; @@ -31,8 +32,6 @@ import com.sk89q.worldguard.bukkit.util.Events; import com.sk89q.worldguard.bukkit.util.Materials; import com.sk89q.worldguard.bukkit.util.WGMetadata; -import com.sk89q.worldguard.util.cause.Cause; -import com.sk89q.worldguard.util.cause.Causes; import org.bukkit.DyeColor; import org.bukkit.Material; import org.bukkit.World; @@ -90,12 +89,10 @@ import org.bukkit.material.MaterialData; import javax.annotation.Nullable; -import java.util.Collections; -import java.util.List; +import static com.sk89q.worldguard.bukkit.cause.Cause.create; import static com.sk89q.worldguard.bukkit.util.Materials.isBlockModifiedOnClick; import static com.sk89q.worldguard.bukkit.util.Materials.isItemAppliedToBlock; -import static com.sk89q.worldguard.util.cause.Causes.create; public class EventAbstractionListener implements Listener { @@ -139,7 +136,7 @@ public void onBlockPlace(BlockPlaceEvent event) { @EventHandler public void onBlockBurn(BlockBurnEvent event) { - Events.fireToCancel(event, new UseBlockEvent(event, Collections.>emptyList(), event.getBlock())); + Events.fireToCancel(event, new UseBlockEvent(event, Cause.unknown(), event.getBlock())); } // TODO: Handle EntityCreatePortalEvent? @@ -166,17 +163,17 @@ public void onEntityChangeBlock(EntityChangeBlockEvent event) { Events.fireToCancel(event, new BreakBlockEvent(event, create(entity), event.getBlock())); } } else { - List> causes; + Cause cause; // Return the source for falling blocks if (entity instanceof FallingBlock) { Block source = WGMetadata.getIfPresent(entity, FALLING_SOURCE_KEY, Block.class); - causes = create(source, entity); + cause = create(source, entity); } else { - causes = create(entity); + cause = create(entity); } - Events.fireToCancel(event, new PlaceBlockEvent(event, causes, event.getBlock().getLocation(), to)); + Events.fireToCancel(event, new PlaceBlockEvent(event, cause, event.getBlock().getLocation(), to)); } } } @@ -205,11 +202,11 @@ public void onPlayerInteract(PlayerInteractEvent event) { @Nullable ItemStack item = player.getItemInHand(); Block clicked = event.getClickedBlock(); Block placed; - List> causes = create(player); + Cause cause = create(player); switch (event.getAction()) { case PHYSICAL: - if (Events.fireAndTestCancel(new UseBlockEvent(event, causes, clicked))) { + if (Events.fireAndTestCancel(new UseBlockEvent(event, cause, clicked))) { event.setUseInteractedBlock(Result.DENY); event.setCancelled(true); } @@ -228,7 +225,7 @@ public void onPlayerInteract(PlayerInteractEvent event) { if (!player.isSneaking() || event.getAction() == Action.LEFT_CLICK_BLOCK) { // Only fire events for blocks that are modified when right clicked if (isBlockModifiedOnClick(clicked.getType()) || (item != null && isItemAppliedToBlock(item.getType(), clicked.getType()))) { - if (Events.fireAndTestCancel(new UseBlockEvent(event, causes, clicked))) { + if (Events.fireAndTestCancel(new UseBlockEvent(event, cause, clicked))) { event.setUseInteractedBlock(Result.DENY); } @@ -260,7 +257,7 @@ public void onPlayerInteract(PlayerInteractEvent event) { case LEFT_CLICK_AIR: case RIGHT_CLICK_AIR: - if (item != null && !item.getType().isBlock() && Events.fireAndTestCancel(new UseItemEvent(event, causes, player.getWorld(), item))) { + if (item != null && !item.getType().isBlock() && Events.fireAndTestCancel(new UseItemEvent(event, cause, player.getWorld(), item))) { event.setUseItemInHand(Result.DENY); } @@ -276,27 +273,27 @@ public void onEntityInteract(org.bukkit.event.entity.EntityInteractEvent event) @EventHandler public void onBlockIgnite(BlockIgniteEvent event) { Block block = event.getBlock(); - List> causes; + Cause cause; // Find the cause if (event.getPlayer() != null) { - causes = create(event.getPlayer()); + cause = create(event.getPlayer()); } else if (event.getIgnitingEntity() != null) { - causes = create(event.getIgnitingEntity()); + cause = create(event.getIgnitingEntity()); } else if (event.getIgnitingBlock() != null) { - causes = create(event.getIgnitingBlock()); + cause = create(event.getIgnitingBlock()); } else { - causes = Collections.emptyList(); + cause = Cause.unknown(); } if (block.getType() != Material.AIR) { - Events.fireToCancel(event, new BreakBlockEvent(event, causes, event.getBlock())); + Events.fireToCancel(event, new BreakBlockEvent(event, cause, event.getBlock())); } // This is also handled in the PlayerInteractEvent listener if (event.getCause() == IgniteCause.FLINT_AND_STEEL || event.getCause() == IgniteCause.FIREBALL) { // TODO: Test location of block - Events.fireToCancel(event, new PlaceBlockEvent(event, causes, event.getBlock().getLocation(), Material.FIRE)); + Events.fireToCancel(event, new PlaceBlockEvent(event, cause, event.getBlock().getLocation(), Material.FIRE)); } } @@ -354,13 +351,13 @@ public void onBlockFromTo(BlockFromToEvent event) { return; } - List> causes = create(from); + Cause cause = create(from); if (from.getType() != Material.AIR) { - Events.fireToCancel(event, new BreakBlockEvent(event, causes, to)); + Events.fireToCancel(event, new BreakBlockEvent(event, cause, to)); } - Events.fireToCancel(event, new PlaceBlockEvent(event, causes, to.getLocation(), from.getType())); + Events.fireToCancel(event, new PlaceBlockEvent(event, cause, to.getLocation(), from.getType())); } } @@ -370,7 +367,7 @@ public void onBlockFromTo(BlockFromToEvent event) { @EventHandler public void onCreatureSpawn(CreatureSpawnEvent event) { - Events.fireToCancel(event, new SpawnEntityEvent(event, Collections.>emptyList(), event.getEntity())); + Events.fireToCancel(event, new SpawnEntityEvent(event, Cause.unknown(), event.getEntity())); } @EventHandler @@ -383,7 +380,7 @@ public void onHangingBreak(HangingBreakEvent event) { if (event instanceof HangingBreakByEntityEvent) { Events.fireToCancel(event, new DestroyEntityEvent(event, create(((HangingBreakByEntityEvent) event).getRemover()), event.getEntity())); } else { - Events.fireToCancel(event, new DestroyEntityEvent(event, Collections.>emptyList(), event.getEntity())); + Events.fireToCancel(event, new DestroyEntityEvent(event, Cause.unknown(), event.getEntity())); } } @@ -431,7 +428,7 @@ public void onEntityDamage(EntityDamageEvent event) { } } else { - Events.fireToCancel(event, new UseEntityEvent(event, Collections.>emptyList(), event.getEntity())); + Events.fireToCancel(event, new UseEntityEvent(event, Cause.unknown(), event.getEntity())); } } @@ -444,7 +441,7 @@ public void onEntityCombust(EntityCombustEvent event) { Events.fireToCancel(event, new UseEntityEvent(event, create(((EntityCombustByEntityEvent) event).getCombuster()), event.getEntity())); } else { - Events.fireToCancel(event, new UseEntityEvent(event, Collections.>emptyList(), event.getEntity())); + Events.fireToCancel(event, new UseEntityEvent(event, Cause.unknown(), event.getEntity())); } } @@ -497,17 +494,17 @@ public void onPotionSplash(PotionSplashEvent event) { Entity entity = event.getEntity(); ThrownPotion potion = event.getPotion(); World world = entity.getWorld(); - List> causes = Causes.create(potion.getShooter()); + Cause cause = create(potion.getShooter()); // Fire item interaction event - Events.fireToCancel(event, new UseItemEvent(event, causes, world, potion.getItem())); + Events.fireToCancel(event, new UseItemEvent(event, cause, world, potion.getItem())); // Fire entity interaction event if (!event.isCancelled()) { int blocked = 0; for (LivingEntity affected : event.getAffectedEntities()) { - if (Events.fireAndTestCancel(new UseEntityEvent(event, causes, affected))) { + if (Events.fireAndTestCancel(new UseEntityEvent(event, cause, affected))) { event.setIntensity(affected, 0); blocked++; } @@ -521,19 +518,19 @@ public void onPotionSplash(PotionSplashEvent event) { @EventHandler public void onBlockDispense(BlockDispenseEvent event) { - List> causes = create(event.getBlock()); + Cause cause = create(event.getBlock()); Block dispenserBlock = event.getBlock(); ItemStack item = event.getItem(); MaterialData materialData = dispenserBlock.getState().getData(); - Events.fireToCancel(event, new UseItemEvent(event, causes, dispenserBlock.getWorld(), item)); + Events.fireToCancel(event, new UseItemEvent(event, cause, dispenserBlock.getWorld(), item)); // Simulate right click event as players have it if (materialData instanceof Dispenser) { Dispenser dispenser = (Dispenser) materialData; Block placed = dispenserBlock.getRelative(dispenser.getFacing()); Block clicked = placed.getRelative(dispenser.getFacing()); - handleBlockRightClick(event, causes, item, clicked, dispenser.getFacing().getOppositeFace(), placed); + handleBlockRightClick(event, cause, item, clicked, dispenser.getFacing().getOppositeFace(), placed); } } @@ -541,38 +538,38 @@ public void onBlockDispense(BlockDispenseEvent event) { * Handle the right click of a block while an item is held. * * @param event the original event - * @param causes the list of causes + * @param cause the list of cause * @param item the item * @param clicked the clicked block * @param faceClicked the face of the clicked block * @param placed the placed block * @param the event type */ - private static void handleBlockRightClick(T event, List> causes, @Nullable ItemStack item, Block clicked, BlockFace faceClicked, Block placed) { + private static void handleBlockRightClick(T event, Cause cause, @Nullable ItemStack item, Block clicked, BlockFace faceClicked, Block placed) { if (item != null && item.getType() == Material.TNT) { // Workaround for a bug that allowed TNT to trigger instantly if placed // next to redstone, without plugins getting the clicked place event // (not sure if this actually still happens) - Events.fireToCancel(event, new UseBlockEvent(event, causes, clicked.getLocation(), Material.TNT)); + Events.fireToCancel(event, new UseBlockEvent(event, cause, clicked.getLocation(), Material.TNT)); } // Handle created Minecarts if (item != null && Materials.isMinecart(item.getType())) { // TODO: Give a more specific Minecart type - Events.fireToCancel(event, new SpawnEntityEvent(event, causes, placed.getLocation().add(0.5, 0, 0.5), EntityType.MINECART)); + Events.fireToCancel(event, new SpawnEntityEvent(event, cause, placed.getLocation().add(0.5, 0, 0.5), EntityType.MINECART)); } // Handle cocoa beans if (item != null && item.getType() == Material.INK_SACK && Materials.isDyeColor(item.getData(), DyeColor.BROWN)) { // CraftBukkit doesn't or didn't throw a clicked place for this if (!(faceClicked == BlockFace.DOWN || faceClicked == BlockFace.UP)) { - Events.fireToCancel(event, new PlaceBlockEvent(event, causes, placed.getLocation(), Material.COCOA)); + Events.fireToCancel(event, new PlaceBlockEvent(event, cause, placed.getLocation(), Material.COCOA)); } } // Workaround for http://leaky.bukkit.org/issues/1034 if (item != null && item.getType() == Material.TNT) { - Events.fireToCancel(event, new PlaceBlockEvent(event, causes, placed.getLocation(), Material.TNT)); + Events.fireToCancel(event, new PlaceBlockEvent(event, cause, placed.getLocation(), Material.TNT)); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java index 4c9c1a4c..ee6bc082 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java @@ -20,16 +20,15 @@ package com.sk89q.worldguard.bukkit.listener; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.bukkit.util.Entities; -import com.sk89q.worldguard.bukkit.util.Materials; -import com.sk89q.worldguard.bukkit.util.RegionQuery; -import com.sk89q.worldguard.util.cause.Causes; import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; import com.sk89q.worldguard.bukkit.event.entity.DestroyEntityEvent; import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; +import com.sk89q.worldguard.bukkit.util.Entities; +import com.sk89q.worldguard.bukkit.util.Materials; +import com.sk89q.worldguard.bukkit.util.RegionQuery; import com.sk89q.worldguard.protection.flags.DefaultFlag; import org.bukkit.ChatColor; import org.bukkit.Location; @@ -59,7 +58,7 @@ private void tellErrorMessage(CommandSender sender, Object subject) { @EventHandler(ignoreCancelled = true) public void onPlaceBlock(PlaceBlockEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); Location target = event.getTarget(); Material type = event.getEffectiveMaterial(); @@ -84,7 +83,7 @@ public void onPlaceBlock(PlaceBlockEvent event) { @EventHandler(ignoreCancelled = true) public void onBreakBlock(BreakBlockEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); Location target = event.getTarget(); if (player != null) { @@ -100,7 +99,7 @@ public void onBreakBlock(BreakBlockEvent event) { @EventHandler(ignoreCancelled = true) public void onUseBlock(UseBlockEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); Location target = event.getTarget(); Material type = event.getEffectiveMaterial(); @@ -140,7 +139,7 @@ public void onUseBlock(UseBlockEvent event) { @EventHandler(ignoreCancelled = true) public void onSpawnEntity(SpawnEntityEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); Location target = event.getTarget(); EntityType type = event.getEffectiveType(); @@ -163,7 +162,7 @@ public void onSpawnEntity(SpawnEntityEvent event) { @EventHandler(ignoreCancelled = true) public void onDestroyEntity(DestroyEntityEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); Location target = event.getTarget(); EntityType type = event.getEntity().getType(); @@ -186,7 +185,7 @@ public void onDestroyEntity(DestroyEntityEvent event) { @EventHandler(ignoreCancelled = true) public void onUseEntity(UseEntityEvent event) { - Player player = Causes.getInvolvedPlayer(event.getCauses()); + Player player = event.getCause().getPlayerRootCause(); Location target = event.getTarget(); if (player != null) { diff --git a/src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java b/src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java deleted file mode 100644 index e1c7998d..00000000 --- a/src/main/java/com/sk89q/worldguard/util/cause/BlockCause.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.util.cause; - -import org.bukkit.block.Block; - -/** - * A cause that is a block. - */ -import static com.google.common.base.Preconditions.checkNotNull; - -public class BlockCause implements Cause { - - private final Block block; - - /** - * Create a new instance. - * - * @param block the block - */ - public BlockCause(Block block) { - checkNotNull(block); - this.block = block; - } - - @Override - public Block get() { - return block; - } - - @Override - public String toString() { - return block.toString(); - } - -} diff --git a/src/main/java/com/sk89q/worldguard/util/cause/Cause.java b/src/main/java/com/sk89q/worldguard/util/cause/Cause.java deleted file mode 100644 index 65a0a4b4..00000000 --- a/src/main/java/com/sk89q/worldguard/util/cause/Cause.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.util.cause; - -/** - * Represents a possible cause of an event. - * - *

Example causes include players, blocks, entities, and many more.

- * - * @param the wrapped object type - */ -public interface Cause { - - /** - * Get the underlying object. - * - * @return the underlying object - */ - T get(); - -} diff --git a/src/main/java/com/sk89q/worldguard/util/cause/Causes.java b/src/main/java/com/sk89q/worldguard/util/cause/Causes.java deleted file mode 100644 index 4f171369..00000000 --- a/src/main/java/com/sk89q/worldguard/util/cause/Causes.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.util.cause; - -import org.bukkit.block.Block; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.entity.Projectile; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - -/** - * Utility methods to handle {@code Cause}s. - */ -public final class Causes { - - private Causes() { - } - - /** - * Get the first player that is in the list of causes. - * - * @param causes a list of causes, where the originating causes are at the beginning - * @return the player or null - */ - @Nullable - public static Player getInvolvedPlayer(List> causes) { - for (Cause cause : causes) { - if (cause instanceof PlayerCause) { - return ((PlayerCause) cause).get(); - } - } - - return null; - } - - /** - * Test whether the list of causes may indicate that a player was part of - * the cause, directly or indirectly. - * - *

An indirect cause would be a dispenser, for example.

- * - * @param causes a list of cuases - * @return true if the event involved a player - */ - public static boolean mayInvolvePlayer(List> causes) { - for (Cause cause : causes) { - if (cause instanceof PlayerCause || cause instanceof BlockCause) { - return true; // This needs to be made smarter later - } - } - - return false; - } - - /** - * Create a list of causes from a list of objects representing causes. - * - * @param cause an array of causes, where the originating causes are at the beginning - * @return a list of causes, where the originating causes are at the beginning - */ - public static List> create(Object ... cause) { - List> causes = new ArrayList>(cause.length); - for (Object o : cause) { - if (o == null) { - continue; - } - - if (o instanceof Player) { - causes.add(new PlayerCause((Player) o)); - } else if (o instanceof Block) { - causes.add(new BlockCause((Block) o)); - } else if (o instanceof Projectile) { - causes.addAll(create(((Projectile) o).getShooter())); - causes.add(new EntityCause((Entity) o)); - } else if (o instanceof Entity) { - causes.add(new EntityCause((Entity) o)); - } else { - causes.add(new UnknownCause(o)); - } - } - return causes; - } - -} diff --git a/src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java b/src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java deleted file mode 100644 index 09e962dc..00000000 --- a/src/main/java/com/sk89q/worldguard/util/cause/EntityCause.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.util.cause; - -import org.bukkit.entity.Entity; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * A cause that is an entity. - */ -public class EntityCause implements Cause { - - private final Entity entity; - - /** - * Create a new instance. - * - * @param entity the entity - */ - public EntityCause(Entity entity) { - checkNotNull(entity); - this.entity = entity; - } - - @Override - public Entity get() { - return entity; - } - - @Override - public String toString() { - return entity.toString(); - } -} diff --git a/src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java b/src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java deleted file mode 100644 index 03a473b3..00000000 --- a/src/main/java/com/sk89q/worldguard/util/cause/PlayerCause.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.util.cause; - -import org.bukkit.entity.Player; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * A cause that is the player. - */ -public class PlayerCause implements Cause { - - private final Player player; - - /** - * Create a new instance. - * - * @param player the player - */ - public PlayerCause(Player player) { - checkNotNull(player); - this.player = player; - } - - @Override - public Player get() { - return player; - } - - @Override - public String toString() { - return player.getName(); - } - -} diff --git a/src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java b/src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java deleted file mode 100644 index 40cfcfc6..00000000 --- a/src/main/java/com/sk89q/worldguard/util/cause/UnknownCause.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.util.cause; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * A cause that is not known. - */ -public class UnknownCause implements Cause { - - private final Object cause; - - /** - * Create a new instance. - * - * @param cause the underlying object - */ - public UnknownCause(Object cause) { - checkNotNull(cause); - this.cause = cause; - } - - @Override - public Object get() { - return cause; - } - - @Override - public String toString() { - return "unknown(" + cause + ")"; - } -} From 4379c7e989d808562d6276c181a721b6f2fef401 Mon Sep 17 00:00:00 2001 From: sk89q Date: Tue, 12 Aug 2014 23:15:47 -0700 Subject: [PATCH 027/133] Handle Vehicle.getPassenger() as a parent cause. --- src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java b/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java index c9adfe32..906f644a 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java @@ -22,6 +22,7 @@ import com.google.common.base.Joiner; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; +import org.bukkit.entity.Vehicle; import javax.annotation.Nullable; import java.util.ArrayList; @@ -95,6 +96,8 @@ private static void expand(List list, @Nullable Object ... element) { if (o instanceof Projectile) { expand(list, ((Projectile) o).getShooter()); + } else if (o instanceof Vehicle) { + expand(list, ((Vehicle) o).getPassenger()); } else { list.add(o); } From eb23e28c164d47de8d68d30ed6204fe1f7a15d9a Mon Sep 17 00:00:00 2001 From: sk89q Date: Tue, 12 Aug 2014 23:17:36 -0700 Subject: [PATCH 028/133] Handle Tameable as a cause. --- src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java b/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java index 906f644a..a114578d 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java @@ -22,6 +22,7 @@ import com.google.common.base.Joiner; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; +import org.bukkit.entity.Tameable; import org.bukkit.entity.Vehicle; import javax.annotation.Nullable; @@ -98,6 +99,8 @@ private static void expand(List list, @Nullable Object ... element) { expand(list, ((Projectile) o).getShooter()); } else if (o instanceof Vehicle) { expand(list, ((Vehicle) o).getPassenger()); + } else if (o instanceof Tameable) { + expand(list, ((Tameable) o).getOwner()); } else { list.add(o); } From 38587a1c6156e877290adffb2d3612eb455b5090 Mon Sep 17 00:00:00 2001 From: sk89q Date: Wed, 13 Aug 2014 00:34:36 -0700 Subject: [PATCH 029/133] Add a hash table index for regions that hashes on chunk coordinates. --- .../listener/RegionProtectionListener.java | 35 ++ .../protection/GlobalRegionManager.java | 19 +- .../protection/ManagerContainer.java | 3 +- .../protection/managers/RegionManager.java | 29 ++ .../managers/index/ChunkHashTable.java | 335 ++++++++++++++++++ .../managers/index/HashMapIndex.java | 21 ++ .../managers/index/RegionIndex.java | 32 ++ .../RegionCollectionConsumer.java | 6 +- .../worldguard/util/collect/EntryBase.java | 30 ++ .../util/collect/LongBaseHashTable.java | 130 +++++++ .../worldguard/util/collect/LongHash.java | 48 +++ .../worldguard/util/collect/LongHashSet.java | 199 +++++++++++ .../util/collect/LongHashTable.java | 65 ++++ 13 files changed, 947 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java rename src/main/java/com/sk89q/worldguard/protection/{managers => util}/RegionCollectionConsumer.java (90%) create mode 100644 src/main/java/com/sk89q/worldguard/util/collect/EntryBase.java create mode 100644 src/main/java/com/sk89q/worldguard/util/collect/LongBaseHashTable.java create mode 100644 src/main/java/com/sk89q/worldguard/util/collect/LongHash.java create mode 100644 src/main/java/com/sk89q/worldguard/util/collect/LongHashSet.java create mode 100644 src/main/java/com/sk89q/worldguard/util/collect/LongHashTable.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java index ee6bc082..d800380e 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java @@ -19,6 +19,7 @@ package com.sk89q.worldguard.bukkit.listener; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; @@ -30,13 +31,19 @@ import com.sk89q.worldguard.bukkit.util.Materials; import com.sk89q.worldguard.bukkit.util.RegionQuery; import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.ChatColor; +import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.command.CommandSender; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; /** * Handle events that need to be processed by region protection. @@ -56,6 +63,34 @@ private void tellErrorMessage(CommandSender sender, Object subject) { sender.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); } + @EventHandler + public void onWorldLoad(WorldLoadEvent event) { + getPlugin().getGlobalRegionManager().load(event.getWorld()); + } + + @EventHandler + public void onWorldUnload(WorldUnloadEvent event) { + getPlugin().getGlobalRegionManager().unload(event.getWorld()); + } + + @EventHandler + public void onChunkLoad(ChunkLoadEvent event) { + RegionManager manager = getPlugin().getGlobalRegionManager().get(event.getWorld()); + if (manager != null) { + Chunk chunk = event.getChunk(); + manager.loadChunk(new Vector2D(chunk.getX(), chunk.getZ())); + } + } + + @EventHandler + public void onChunkUnload(ChunkUnloadEvent event) { + RegionManager manager = getPlugin().getGlobalRegionManager().get(event.getWorld()); + if (manager != null) { + Chunk chunk = event.getChunk(); + manager.unloadChunk(new Vector2D(chunk.getX(), chunk.getZ())); + } + } + @EventHandler(ignoreCancelled = true) public void onPlaceBlock(PlaceBlockEvent event) { Player player = event.getCause().getPlayerRootCause(); diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index b5260092..2810df23 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -19,6 +19,7 @@ package com.sk89q.worldguard.protection; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.bukkit.BukkitUtil; import com.sk89q.worldguard.bukkit.ConfigurationManager; @@ -26,14 +27,17 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.List; +import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; public class GlobalRegionManager { @@ -50,7 +54,16 @@ public GlobalRegionManager(WorldGuardPlugin plugin) { @Nullable public RegionManager load(World world) { - return container.load(world.getName()); + checkNotNull(world); + RegionManager manager = container.load(world.getName()); + if (manager != null) { + List positions = new ArrayList(); + for (Chunk chunk : world.getLoadedChunks()) { + positions.add(new Vector2D(chunk.getX(), chunk.getZ())); + } + manager.loadChunks(positions); + } + return manager; } public void preload() { @@ -59,6 +72,10 @@ public void preload() { } } + public void unload(World world) { + unload(world.getName()); + } + public void unload(String name) { container.unload(name); } diff --git a/src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java b/src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java index 521f0fc1..2ce82afb 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java +++ b/src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java @@ -22,6 +22,7 @@ import com.google.common.base.Supplier; import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.index.ChunkHashTable; import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex; import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex; import com.sk89q.worldguard.protection.managers.storage.RegionStore; @@ -57,7 +58,7 @@ class ManagerContainer { private final Object lock = new Object(); private final EnumMap drivers = new EnumMap(DriverType.class); private final RegionStoreDriver defaultDriver; - private final Supplier indexFactory = new PriorityRTreeIndex.Factory(); + private final Supplier indexFactory = new ChunkHashTable.Factory(new PriorityRTreeIndex.Factory()); private final Timer timer = new Timer(); ManagerContainer(ConfigurationManager config) { diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java index 3ccfe844..122c4e35 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java @@ -22,6 +22,7 @@ import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex; @@ -29,6 +30,7 @@ import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException; import com.sk89q.worldguard.protection.managers.storage.RegionStore; import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.util.RegionCollectionConsumer; import com.sk89q.worldguard.util.Normal; import javax.annotation.Nullable; @@ -113,6 +115,33 @@ public void saveChanges() throws IOException { } } + /** + * Load the regions for a chunk. + * + * @param position the position + */ + public void loadChunk(Vector2D position) { + index.bias(position); + } + + /** + * Load the regions for a chunk. + * + * @param positions a collection of positions + */ + public void loadChunks(Collection positions) { + index.biasAll(positions); + } + + /** + * Unload the regions for a chunk. + * + * @param position the position + */ + public void unloadChunk(Vector2D position) { + index.forget(position); + } + /** * Get an unmodifiable map of regions containing the state of the * index at the time of call. diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java new file mode 100644 index 00000000..07edebc1 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java @@ -0,0 +1,335 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection.managers.index; + +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.sk89q.odeum.concurrent.EvenMoreExecutors; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldguard.protection.managers.RegionDifference; +import com.sk89q.worldguard.protection.managers.RemovalStrategy; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.util.RegionCollectionConsumer; +import com.sk89q.worldguard.util.collect.LongHashTable; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Maintains a hash table for each chunk containing a list of regions that + * are contained within that chunk, allowing for fast spatial lookup. + */ +public class ChunkHashTable implements ConcurrentRegionIndex { + + private ListeningExecutorService executor = createExecutor(); + private LongHashTable states = new LongHashTable(); + private final RegionIndex index; + private final Object lock = new Object(); + + /** + * Create a new instance. + * + * @param index the index + */ + public ChunkHashTable(RegionIndex index) { + checkNotNull(index); + this.index = index; + } + + /** + * Create an executor. + * + * @return an executor service + */ + private ListeningExecutorService createExecutor() { + return MoreExecutors.listeningDecorator( + EvenMoreExecutors.newBoundedCachedThreadPool(0, 4, Integer.MAX_VALUE)); + } + + /** + * Get a state object at the given position. + * + * @param position the position + * @param create true to create an entry if one does not exist + * @return a chunk state object, or {@code null} (only if {@code create} is false) + */ + @Nullable + private ChunkState get(Vector2D position, boolean create) { + ChunkState state; + synchronized (lock) { + state = states.get(position.getBlockX(), position.getBlockZ()); + if (state == null && create) { + state = new ChunkState(position); + states.put(position.getBlockX(), position.getBlockZ(), state); + executor.submit(new EnumerateRegions(position)); + } + } + return state; + } + + /** + * Get a state at the given position or create a new entry if one does + * not exist. + * + * @param position the position + * @return a state + */ + private ChunkState getOrCreate(Vector2D position) { + return get(position, true); + } + + /** + * Clear the current hash table and rebuild it in the background. + */ + private void rebuild() { + synchronized (lock) { + ListeningExecutorService previousExecutor = executor; + LongHashTable previousStates = states; + + previousExecutor.shutdownNow(); + states = new LongHashTable(); + executor = createExecutor(); + + List positions = new ArrayList(); + for (ChunkState state : previousStates.values()) { + Vector2D position = state.getPosition(); + positions.add(position); + states.put(position.getBlockX(), position.getBlockZ(), new ChunkState(position)); + } + + if (!positions.isEmpty()) { + executor.submit(new EnumerateRegions(positions)); + } + } + } + + @Override + public void bias(Vector2D chunkPosition) { + checkNotNull(chunkPosition); + getOrCreate(chunkPosition); + } + + @Override + public void biasAll(Collection chunkPositions) { + synchronized (lock) { + for (Vector2D position : chunkPositions) { + bias(position); + } + } + } + + @Override + public void forget(Vector2D chunkPosition) { + checkNotNull(chunkPosition); + synchronized (lock) { + states.remove(chunkPosition.getBlockX(), chunkPosition.getBlockZ()); + } + } + + @Override + public void forgetAll() { + synchronized (lock) { + executor.shutdownNow(); + states = new LongHashTable(); + executor = createExecutor(); + } + } + + @Override + public void add(ProtectedRegion region) { + index.add(region); + rebuild(); + } + + @Override + public void addAll(Collection regions) { + index.addAll(regions); + rebuild(); + } + + @Override + public Set remove(String id, RemovalStrategy strategy) { + Set removed = index.remove(id, strategy); + rebuild(); + return removed; + } + + @Override + public boolean contains(String id) { + return index.contains(id); + } + + @Nullable + @Override + public ProtectedRegion get(String id) { + return index.get(id); + } + + @Override + public void apply(Predicate consumer) { + index.apply(consumer); + } + + @Override + public void applyContaining(Vector position, Predicate consumer) { + checkNotNull(position); + checkNotNull(consumer); + + ChunkState state = get(new Vector2D(position.getBlockX() >> 4, position.getBlockZ() >> 4), false); + if (state != null && state.isLoaded()) { + for (ProtectedRegion region : state.getRegions()) { + if (region.contains(position)) { + consumer.apply(region); + } + } + } else { + index.applyContaining(position, consumer); + } + } + + @Override + public void applyIntersecting(ProtectedRegion region, Predicate consumer) { + index.applyIntersecting(region, consumer); + } + + @Override + public int size() { + return index.size(); + } + + @Override + public RegionDifference getAndClearDifference() { + return index.getAndClearDifference(); + } + + @Override + public Collection values() { + return index.values(); + } + + @Override + public boolean isDirty() { + return index.isDirty(); + } + + @Override + public void setDirty(boolean dirty) { + index.setDirty(dirty); + } + + /** + * A task to enumerate the regions for a list of provided chunks. + */ + private class EnumerateRegions implements Runnable { + private final List positions; + + private EnumerateRegions(Vector2D position) { + this(Arrays.asList(checkNotNull(position))); + } + + private EnumerateRegions(List positions) { + checkNotNull(positions); + checkArgument(!positions.isEmpty(), "List of positions can't be empty"); + this.positions = positions; + } + + @Override + public void run() { + for (Vector2D position : positions) { + ChunkState state = get(position, false); + + if (state != null) { + List regions = new ArrayList(); + ProtectedRegion chunkRegion = new ProtectedCuboidRegion( + "_", + position.toVector(0).toBlockVector(), + position.add(16, 16).toVector(Integer.MAX_VALUE).toBlockVector()); + index.applyIntersecting(chunkRegion, new RegionCollectionConsumer(regions, false)); + + state.setRegions(Collections.unmodifiableList(regions)); + + if (Thread.currentThread().isInterrupted()) { + return; + } + } + } + } + } + + /** + * Stores a cache of region data for a chunk. + */ + private class ChunkState { + private final Vector2D position; + private boolean loaded = false; + private List regions = Collections.emptyList(); + + private ChunkState(Vector2D position) { + this.position = position; + } + + public Vector2D getPosition() { + return position; + } + + public List getRegions() { + return regions; + } + + public void setRegions(List regions) { + this.regions = regions; + this.loaded = true; + } + + public boolean isLoaded() { + return loaded; + } + } + + /** + * A factory for instances of {@code ChunkHashCache}. + */ + public static class Factory implements Supplier { + private final Supplier supplier; + + public Factory(Supplier supplier) { + checkNotNull(supplier); + this.supplier = supplier; + } + + @Override + public ChunkHashTable get() { + return new ChunkHashTable(supplier.get()); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/index/HashMapIndex.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/HashMapIndex.java index aaa86a93..6d9f43ba 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/index/HashMapIndex.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/HashMapIndex.java @@ -22,6 +22,7 @@ import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldguard.protection.managers.RegionDifference; import com.sk89q.worldguard.protection.managers.RemovalStrategy; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -102,6 +103,26 @@ public void addAll(Collection regions) { } } + @Override + public void bias(Vector2D chunkPosition) { + // Nothing to do + } + + @Override + public void biasAll(Collection chunkPositions) { + // Nothing to do + } + + @Override + public void forget(Vector2D chunkPosition) { + // Nothing to do + } + + @Override + public void forgetAll() { + // Nothing to do + } + @Override public void add(ProtectedRegion region) { synchronized (lock) { diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/index/RegionIndex.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/RegionIndex.java index 76e43812..264dceae 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/index/RegionIndex.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/RegionIndex.java @@ -21,6 +21,7 @@ import com.google.common.base.Predicate; import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.Vector2D; import com.sk89q.worldguard.protection.managers.RegionDifference; import com.sk89q.worldguard.protection.managers.RemovalStrategy; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -39,6 +40,37 @@ */ public interface RegionIndex extends ChangeTracked { + /** + * Bias the given chunk for faster lookups (put it in a hash table, etc.). + * + *

Implementations may choose to do nothing.

+ * + * @param chunkPosition the chunk position + */ + void bias(Vector2D chunkPosition); + + /** + * Bias the given chunk for faster lookups (put it in a hash table, etc.). + * + *

Implementations may choose to do nothing.

+ * + * @param chunkPosition the chunk position + */ + void biasAll(Collection chunkPosition); + + /** + * No longer bias the given chunk for faster lookup. + * + * @param chunkPosition the chunk position + */ + void forget(Vector2D chunkPosition); + + /** + * Clearly all extra cache data created by any calls to + * {@link #bias(Vector2D)}. + */ + void forgetAll(); + /** * Add a region to this index, replacing any existing one with the same * name (equality determined using {@link Normal}). diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/RegionCollectionConsumer.java b/src/main/java/com/sk89q/worldguard/protection/util/RegionCollectionConsumer.java similarity index 90% rename from src/main/java/com/sk89q/worldguard/protection/managers/RegionCollectionConsumer.java rename to src/main/java/com/sk89q/worldguard/protection/util/RegionCollectionConsumer.java index 0ab9d6fd..a36554ff 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/RegionCollectionConsumer.java +++ b/src/main/java/com/sk89q/worldguard/protection/util/RegionCollectionConsumer.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection.managers; +package com.sk89q.worldguard.protection.util; import com.google.common.base.Predicate; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -33,7 +33,7 @@ * to the collection, although it may result in duplicates in the collection * if the collection is not a set.

*/ -class RegionCollectionConsumer implements Predicate { +public class RegionCollectionConsumer implements Predicate { private final Collection collection; private final boolean addParents; @@ -44,7 +44,7 @@ class RegionCollectionConsumer implements Predicate { * @param collection the collection to add regions to * @param addParents true to also add the parents to the collection */ - RegionCollectionConsumer(Collection collection, boolean addParents) { + public RegionCollectionConsumer(Collection collection, boolean addParents) { checkNotNull(collection); this.collection = collection; diff --git a/src/main/java/com/sk89q/worldguard/util/collect/EntryBase.java b/src/main/java/com/sk89q/worldguard/util/collect/EntryBase.java new file mode 100644 index 00000000..41a58ec5 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/collect/EntryBase.java @@ -0,0 +1,30 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.collect; + +public class EntryBase { + + protected long key; + + public EntryBase(long key) { + this.key = key; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/util/collect/LongBaseHashTable.java b/src/main/java/com/sk89q/worldguard/util/collect/LongBaseHashTable.java new file mode 100644 index 00000000..0ae50759 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/collect/LongBaseHashTable.java @@ -0,0 +1,130 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.collect; + +import java.util.ArrayList; +import java.util.Arrays; + +public class LongBaseHashTable extends LongHash { + + EntryBase[][][] values = new EntryBase[256][][]; + EntryBase cache = null; + + public void put(int msw, int lsw, EntryBase entry) { + put(entry); + } + + public EntryBase getEntry(int msw, int lsw) { + return getEntry(toLong(msw, lsw)); + } + + public synchronized void put(EntryBase entry) { + int mainIdx = (int) (entry.key & 255); + EntryBase[][] outer = this.values[mainIdx]; + if (outer == null) this.values[mainIdx] = outer = new EntryBase[256][]; + + int outerIdx = (int) ((entry.key >> 32) & 255); + EntryBase[] inner = outer[outerIdx]; + + if (inner == null) { + outer[outerIdx] = inner = new EntryBase[5]; + inner[0] = this.cache = entry; + } else { + int i; + for (i = 0; i < inner.length; i++) { + if (inner[i] == null || inner[i].key == entry.key) { + inner[i] = this.cache = entry; + return; + } + } + + outer[outerIdx] = inner = Arrays.copyOf(inner, i + i); + inner[i] = entry; + } + } + + public synchronized EntryBase getEntry(long key) { + return containsKey(key) ? cache : null; + } + + public synchronized boolean containsKey(long key) { + if (this.cache != null && cache.key == key) return true; + + int outerIdx = (int) ((key >> 32) & 255); + EntryBase[][] outer = this.values[(int) (key & 255)]; + if (outer == null) return false; + + EntryBase[] inner = outer[outerIdx]; + if (inner == null) return false; + + for (int i = 0; i < inner.length; i++) { + EntryBase e = inner[i]; + if (e == null) { + return false; + } else if (e.key == key) { + this.cache = e; + return true; + } + } + return false; + } + + public synchronized void remove(long key) { + EntryBase[][] outer = this.values[(int) (key & 255)]; + if (outer == null) return; + + EntryBase[] inner = outer[(int) ((key >> 32) & 255)]; + if (inner == null) return; + + for (int i = 0; i < inner.length; i++) { + if (inner[i] == null) continue; + + if (inner[i].key == key) { + for (i++; i < inner.length; i++) { + if (inner[i] == null) break; + inner[i - 1] = inner[i]; + } + + inner[i-1] = null; + this.cache = null; + return; + } + } + } + + public synchronized ArrayList entries() { + ArrayList ret = new ArrayList(); + + for (EntryBase[][] outer : this.values) { + if (outer == null) continue; + + for (EntryBase[] inner : outer) { + if (inner == null) continue; + + for (EntryBase entry : inner) { + if (entry == null) break; + + ret.add(entry); + } + } + } + return ret; + } +} diff --git a/src/main/java/com/sk89q/worldguard/util/collect/LongHash.java b/src/main/java/com/sk89q/worldguard/util/collect/LongHash.java new file mode 100644 index 00000000..89554007 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/collect/LongHash.java @@ -0,0 +1,48 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.collect; + +public abstract class LongHash { + + public static long toLong(int msw, int lsw) { + return ((long) msw << 32) + lsw - Integer.MIN_VALUE; + } + + public static int msw(long l) { + return (int) (l >> 32); + } + + public static int lsw(long l) { + return (int) (l & 0xFFFFFFFF) + Integer.MIN_VALUE; + } + + public boolean containsKey(int msw, int lsw) { + return containsKey(toLong(msw, lsw)); + } + + public void remove(int msw, int lsw) { + remove(toLong(msw, lsw)); + } + + public abstract boolean containsKey(long key); + + public abstract void remove(long key); + +} diff --git a/src/main/java/com/sk89q/worldguard/util/collect/LongHashSet.java b/src/main/java/com/sk89q/worldguard/util/collect/LongHashSet.java new file mode 100644 index 00000000..a23789d1 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/collect/LongHashSet.java @@ -0,0 +1,199 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.collect; + +import java.util.Arrays; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +public class LongHashSet extends LongHash { + + protected long[][][] values = new long[256][][]; + protected int count = 0; + protected ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); + protected ReadLock rl = rwl.readLock(); + protected WriteLock wl = rwl.writeLock(); + + public boolean isEmpty() { + rl.lock(); + try { + return this.count == 0; + } finally { + rl.unlock(); + } + } + + public int size() { + return count; + } + + public void add(int msw, int lsw) { + add(toLong(msw, lsw)); + } + + public void add(long key) { + wl.lock(); + try { + int mainIdx = (int) (key & 255); + long outer[][] = this.values[mainIdx]; + if (outer == null) this.values[mainIdx] = outer = new long[256][]; + + int outerIdx = (int) ((key >> 32) & 255); + long inner[] = outer[outerIdx]; + + if (inner == null) { + synchronized (this) { + outer[outerIdx] = inner = new long[1]; + inner[0] = key; + this.count++; + } + } else { + int i; + for (i = 0; i < inner.length; i++) { + if (inner[i] == key) { + return; + } + } + inner = Arrays.copyOf(inner, i + 1); + outer[outerIdx] = inner; + inner[i] = key; + this.count++; + } + } finally { + wl.unlock(); + } + } + + public boolean containsKey(long key) { + rl.lock(); + try { + long[][] outer = this.values[(int) (key & 255)]; + if (outer == null) return false; + + long[] inner = outer[(int) ((key >> 32) & 255)]; + if (inner == null) return false; + + for (long entry : inner) { + if (entry == key) return true; + } + return false; + } finally { + rl.unlock(); + } + } + + public void remove(long key) { + wl.lock(); + try { + long[][] outer = this.values[(int) (key & 255)]; + if (outer == null) return; + + long[] inner = outer[(int) ((key >> 32) & 255)]; + if (inner == null) return; + + int max = inner.length - 1; + for (int i = 0; i <= max; i++) { + if (inner[i] == key) { + this.count--; + if (i != max) { + inner[i] = inner[max]; + } + + outer[(int) ((key >> 32) & 255)] = (max == 0 ? null : Arrays.copyOf(inner, max)); + return; + } + } + } finally { + wl.unlock(); + } + } + + public long popFirst() { + wl.lock(); + try { + for (long[][] outer: this.values) { + if (outer == null) continue; + + for (int i = 0; i < outer.length; i++) { + long[] inner = outer[i]; + if (inner == null || inner.length == 0) continue; + + this.count--; + long ret = inner[inner.length - 1]; + outer[i] = Arrays.copyOf(inner, inner.length - 1); + + return ret; + } + } + } finally { + wl.unlock(); + } + return 0; + } + + public long[] popAll() { + int index = 0; + wl.lock(); + try { + long[] ret = new long[this.count]; + for (long[][] outer : this.values) { + if (outer == null) continue; + + for (int oIdx = outer.length - 1; oIdx >= 0; oIdx--) { + long[] inner = outer[oIdx]; + if (inner == null) continue; + + for (long entry: inner) { + ret[index++] = entry; + } + outer[oIdx] = null; + } + } + count = 0; + return ret; + } finally { + wl.unlock(); + } + } + + public long[] keys() { + int index = 0; + rl.lock(); + try { + long[] ret = new long[this.count]; + for (long[][] outer : this.values) { + if (outer == null) continue; + + for (long[] inner : outer) { + if (inner == null) continue; + + for (long entry : inner) { + ret[index++] = entry; + } + } + } + return ret; + } finally { + rl.unlock(); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/util/collect/LongHashTable.java b/src/main/java/com/sk89q/worldguard/util/collect/LongHashTable.java new file mode 100644 index 00000000..46462562 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/collect/LongHashTable.java @@ -0,0 +1,65 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.collect; + +import java.util.ArrayList; + +public class LongHashTable extends LongBaseHashTable { + + public void put(int msw, int lsw, V value) { + put(toLong(msw, lsw), value); + } + + public V get(int msw, int lsw) { + return get(toLong(msw, lsw)); + } + + public synchronized void put(long key, V value) { + put(new Entry(key, value)); + } + + @SuppressWarnings("unchecked") + public synchronized V get(long key) { + Entry entry = ((Entry) getEntry(key)); + return entry != null ? entry.value : null; + } + + @SuppressWarnings("unchecked") + public synchronized ArrayList values() { + ArrayList ret = new ArrayList(); + + ArrayList entries = entries(); + + for (EntryBase entry : entries) { + ret.add(((Entry) entry).value); + } + return ret; + } + + private class Entry extends EntryBase { + V value; + + Entry(long k, V v) { + super(k); + this.value = v; + } + } + +} From 5ddc4b9251da966ee96b358d6d351ea4e0c43c28 Mon Sep 17 00:00:00 2001 From: sk89q Date: Wed, 13 Aug 2014 01:02:55 -0700 Subject: [PATCH 030/133] Properly convert chunk coordinates in ChunkHashTable. --- .../worldguard/protection/managers/index/ChunkHashTable.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java index 07edebc1..85f90839 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java @@ -271,8 +271,8 @@ public void run() { List regions = new ArrayList(); ProtectedRegion chunkRegion = new ProtectedCuboidRegion( "_", - position.toVector(0).toBlockVector(), - position.add(16, 16).toVector(Integer.MAX_VALUE).toBlockVector()); + position.multiply(16).toVector(0).toBlockVector(), + position.add(1, 1).multiply(16).toVector(Integer.MAX_VALUE).toBlockVector()); index.applyIntersecting(chunkRegion, new RegionCollectionConsumer(regions, false)); state.setRegions(Collections.unmodifiableList(regions)); From 5b685e71d3fc625458a95abae8ddf768f1da7b88 Mon Sep 17 00:00:00 2001 From: sk89q Date: Wed, 13 Aug 2014 07:51:13 -0700 Subject: [PATCH 031/133] Add ChunkHashTable.awaitCompletion(). --- .../managers/index/ChunkHashTable.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java index 85f90839..7fcb15b2 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java @@ -40,6 +40,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -132,6 +133,25 @@ private void rebuild() { } } + /** + * Waits until all currently executing background tasks complete. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @return {@code true} if this executor terminated and + * {@code false} if the timeout elapsed before termination + * @throws InterruptedException on interruption + */ + public boolean awaitCompletion(long timeout, TimeUnit unit) throws InterruptedException { + ListeningExecutorService previousExecutor; + synchronized (lock) { + previousExecutor = executor; + executor = createExecutor(); + } + previousExecutor.shutdown(); + return previousExecutor.awaitTermination(timeout, unit); + } + @Override public void bias(Vector2D chunkPosition) { checkNotNull(chunkPosition); From ad1c10a3e9220e8c8dd40e3a399928e9abfd3230 Mon Sep 17 00:00:00 2001 From: sk89q Date: Wed, 13 Aug 2014 14:06:37 -0700 Subject: [PATCH 032/133] Reorganize Bukkit portion of the region API. --- .../worldguard/bukkit/WorldGuardPlugin.java | 9 +- .../bukkit/commands/RegionCommands.java | 2 +- .../bukkit/commands/WorldGuardCommands.java | 5 +- .../listener/RegionProtectionListener.java | 12 +- .../AbstractPermissionModel.java | 3 +- .../RegionPermissionModel.java | 13 +- ...onQuery.java => ProtectedRegionQuery.java} | 14 +- .../protection/GlobalRegionManager.java | 271 +++++++++++++----- 8 files changed, 234 insertions(+), 95 deletions(-) rename src/main/java/com/sk89q/worldguard/bukkit/{ => permission}/AbstractPermissionModel.java (93%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => permission}/RegionPermissionModel.java (94%) rename src/main/java/com/sk89q/worldguard/bukkit/util/{RegionQuery.java => ProtectedRegionQuery.java} (84%) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 1b2fcc15..b1605983 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -64,11 +64,12 @@ import com.sk89q.worldguard.bukkit.listener.WorldGuardWeatherListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardWorldListener; import com.sk89q.worldguard.protection.GlobalRegionManager; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.util.UnresolvedNamesException; import com.sk89q.worldguard.protection.util.migrator.MigrationException; import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator; -import com.sk89q.worldguard.protection.util.UnresolvedNamesException; -import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.World; @@ -190,7 +191,7 @@ public void run() { getLogger().info("Loading region data..."); globalRegionManager = new GlobalRegionManager(this); - globalRegionManager.preload(); + globalRegionManager.loadAll(Bukkit.getServer().getWorlds()); migrateRegionUniqueIds(); // Migrate to UUIDs } catch (FatalConfigurationLoadingException e) { @@ -281,7 +282,7 @@ public void onDisable() { getLogger().log(Level.WARNING, "Some tasks failed while waiting for remaining tasks to finish", e); } - globalRegionManager.unload(); + globalRegionManager.unloadAll(); configuration.unload(); this.getServer().getScheduler().cancelTasks(this); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java index fff71fe0..e4928023 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java @@ -35,7 +35,7 @@ import com.sk89q.worldedit.bukkit.selections.Selection; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.bukkit.LoggerToChatHandler; -import com.sk89q.worldguard.bukkit.RegionPermissionModel; +import com.sk89q.worldguard.bukkit.permission.RegionPermissionModel; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java index 22991901..17e7dd7c 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java @@ -30,6 +30,7 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.util.PastebinPoster; import com.sk89q.worldguard.util.PastebinPoster.PasteCallback; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -77,9 +78,9 @@ public void reload(CommandContext args, CommandSender sender) throws CommandExce try { plugin.getGlobalStateManager().unload(); - plugin.getGlobalRegionManager().unload(); + plugin.getGlobalRegionManager().unloadAll(); plugin.getGlobalStateManager().load(); - plugin.getGlobalRegionManager().preload(); + plugin.getGlobalRegionManager().loadAll(Bukkit.getServer().getWorlds()); // WGBukkit.cleanCache(); sender.sendMessage("WorldGuard configuration reloaded."); } catch (Throwable t) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java index d800380e..66300fe3 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java @@ -29,7 +29,7 @@ import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; import com.sk89q.worldguard.bukkit.util.Entities; import com.sk89q.worldguard.bukkit.util.Materials; -import com.sk89q.worldguard.bukkit.util.RegionQuery; +import com.sk89q.worldguard.bukkit.util.ProtectedRegionQuery; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.ChatColor; @@ -98,7 +98,7 @@ public void onPlaceBlock(PlaceBlockEvent event) { Material type = event.getEffectiveMaterial(); if (player != null) { - RegionQuery query = new RegionQuery(getPlugin(), player); + ProtectedRegionQuery query = new ProtectedRegionQuery(getPlugin(), player); boolean canPlace; // Flint and steel, fire charge @@ -139,7 +139,7 @@ public void onUseBlock(UseBlockEvent event) { Material type = event.getEffectiveMaterial(); if (player != null) { - RegionQuery query = new RegionQuery(getPlugin(), player); + ProtectedRegionQuery query = new ProtectedRegionQuery(getPlugin(), player); boolean canUse; // Inventory blocks (CHEST_ACCESS) @@ -179,7 +179,7 @@ public void onSpawnEntity(SpawnEntityEvent event) { EntityType type = event.getEffectiveType(); if (player != null) { - RegionQuery query = new RegionQuery(getPlugin(), player); + ProtectedRegionQuery query = new ProtectedRegionQuery(getPlugin(), player); boolean canSpawn; if (Entities.isVehicle(type)) { @@ -202,7 +202,7 @@ public void onDestroyEntity(DestroyEntityEvent event) { EntityType type = event.getEntity().getType(); if (player != null) { - RegionQuery query = new RegionQuery(getPlugin(), player); + ProtectedRegionQuery query = new ProtectedRegionQuery(getPlugin(), player); boolean canDestroy; if (Entities.isVehicle(type)) { @@ -224,7 +224,7 @@ public void onUseEntity(UseEntityEvent event) { Location target = event.getTarget(); if (player != null) { - RegionQuery query = new RegionQuery(getPlugin(), player); + ProtectedRegionQuery query = new ProtectedRegionQuery(getPlugin(), player); boolean canUse = query.canBuild(target) || query.allows(DefaultFlag.USE, target); if (!canUse) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/AbstractPermissionModel.java b/src/main/java/com/sk89q/worldguard/bukkit/permission/AbstractPermissionModel.java similarity index 93% rename from src/main/java/com/sk89q/worldguard/bukkit/AbstractPermissionModel.java rename to src/main/java/com/sk89q/worldguard/bukkit/permission/AbstractPermissionModel.java index a885e2ba..ff7538f3 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/AbstractPermissionModel.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/permission/AbstractPermissionModel.java @@ -17,8 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.bukkit; +package com.sk89q.worldguard.bukkit.permission; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import org.bukkit.command.CommandSender; import com.sk89q.worldguard.internal.PermissionModel; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionPermissionModel.java b/src/main/java/com/sk89q/worldguard/bukkit/permission/RegionPermissionModel.java similarity index 94% rename from src/main/java/com/sk89q/worldguard/bukkit/RegionPermissionModel.java rename to src/main/java/com/sk89q/worldguard/bukkit/permission/RegionPermissionModel.java index 3e0ba0fe..3807468b 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionPermissionModel.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/permission/RegionPermissionModel.java @@ -17,14 +17,15 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.bukkit; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; +package com.sk89q.worldguard.bukkit.permission; import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; /** * Used for querying region-related permissions. @@ -34,6 +35,10 @@ public class RegionPermissionModel extends AbstractPermissionModel { public RegionPermissionModel(WorldGuardPlugin plugin, CommandSender sender) { super(plugin, sender); } + + public boolean mayIgnoreRegionProtection(World world) { + return hasPluginPermission("region.bypass." + world.getName()); + } public boolean mayForceLoadRegions() { return hasPluginPermission("region.load"); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java b/src/main/java/com/sk89q/worldguard/bukkit/util/ProtectedRegionQuery.java similarity index 84% rename from src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java rename to src/main/java/com/sk89q/worldguard/bukkit/util/ProtectedRegionQuery.java index 12f4f747..9d26992a 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQuery.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/ProtectedRegionQuery.java @@ -24,6 +24,7 @@ import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.GlobalRegionManager; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.RegionManager; @@ -35,17 +36,21 @@ import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; -public class RegionQuery { +public class ProtectedRegionQuery { private final ConfigurationManager config; private final GlobalRegionManager globalManager; @Nullable private final LocalPlayer localPlayer; - public RegionQuery(WorldGuardPlugin plugin, @Nullable Player player) { + public ProtectedRegionQuery(WorldGuardPlugin plugin, @Nullable Player player) { + this(plugin, player != null ? plugin.wrapPlayer(player) : null); + } + + public ProtectedRegionQuery(WorldGuardPlugin plugin, @Nullable LocalPlayer player) { this.config = plugin.getGlobalStateManager(); this.globalManager = plugin.getGlobalRegionManager(); - this.localPlayer = player != null ? plugin.wrapPlayer(player) : null; + this.localPlayer = player; } public boolean canBuild(Location location) { @@ -76,7 +81,8 @@ public boolean canConstruct(Location location) { return true; } else { RegionManager manager = globalManager.get(location.getWorld()); - return manager.getApplicableRegions(BukkitUtil.toVector(location)).canConstruct(localPlayer); + ApplicableRegionSet result = manager.getApplicableRegions(BukkitUtil.toVector(location)); + return result.canBuild(localPlayer) && result.canConstruct(localPlayer); } } diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index 2810df23..651d6f9e 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -21,10 +21,9 @@ import com.sk89q.worldedit.Vector2D; import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.bukkit.BukkitUtil; import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.util.ProtectedRegionQuery; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.Chunk; @@ -35,146 +34,272 @@ import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; +/** + * A global region manager loads, saves and caches region data for zero or + * more worlds at a time. + * + *

This class is thread safe and its contents can be accessed from + * multiple concurrent threads.

+ */ public class GlobalRegionManager { private final WorldGuardPlugin plugin; - private final ConfigurationManager config; private final ManagerContainer container; + /** + * Create a new instance. + * + * @param plugin the plugin + */ public GlobalRegionManager(WorldGuardPlugin plugin) { this.plugin = plugin; - config = plugin.getGlobalStateManager(); + + ConfigurationManager config = plugin.getGlobalStateManager(); container = new ManagerContainer(config); } + /** + * Load the region data for a world if it has not been loaded already. + * + *

This method is called by WorldGuard and should not be called + * by other plugins.

+ * + * @param world the world + * @return a region manager, either returned from the cache or newly loaded + */ @Nullable public RegionManager load(World world) { checkNotNull(world); + RegionManager manager = container.load(world.getName()); + if (manager != null) { + // Bias the region data for loaded chunks List positions = new ArrayList(); for (Chunk chunk : world.getLoadedChunks()) { positions.add(new Vector2D(chunk.getX(), chunk.getZ())); } manager.loadChunks(positions); } + return manager; } - public void preload() { - for (World world : plugin.getServer().getWorlds()) { + /** + * Load the region data for a list of worlds. + * + *

This method is called by WorldGuard and should not be called + * by other plugins.

+ * + * @param worlds a list of worlds + */ + public void loadAll(Collection worlds) { + checkNotNull(worlds); + + for (World world : worlds) { load(world); } } + /** + * Unload the region data for a world. + * + * @param world a world + */ public void unload(World world) { - unload(world.getName()); - } - - public void unload(String name) { - container.unload(name); - } - - public void unload() { - container.unloadAll(); + checkNotNull(world); + + container.unload(world.getName()); } + /** + * Unload all region data for all worlds that region data has + * been loaded for. + */ public void unloadAll() { container.unloadAll(); } + /** + * Get the region manager for a world if one exists. + * + *

This method may return {@code null} if region data for the given + * world has not been loaded, has failed to load, or support for regions + * has been disabled.

+ * + * @param world the world + * @return a region manager, or {@code null} if one is not availale + */ @Nullable public RegionManager get(World world) { return container.get(world.getName()); } + /** + * Get an immutable list of loaded {@link RegionManager}s. + * + * @return a list of managers + */ public List getLoaded() { - return container.getLoaded(); + return Collections.unmodifiableList(container.getLoaded()); } + /** + * Create a new region query with no player. + * + * @return a new query + */ + public ProtectedRegionQuery createAnonymousQuery() { + return new ProtectedRegionQuery(plugin, (Player) null); + } + + /** + * Create a new region query. + * + * @param player a player, or {@code null} + * @return a new query + */ + public ProtectedRegionQuery createQuery(@Nullable Player player) { + return new ProtectedRegionQuery(plugin, player); + } + + /** + * Create a new region query. + * + * @param player a player, or {@code null} + * @return a new query + */ + private ProtectedRegionQuery createQuery(@Nullable LocalPlayer player) { + return new ProtectedRegionQuery(plugin, player); + } + + /** + * Test whether the given player has region protection bypass permission. + * + * @param player the player + * @param world the world + * @return true if a bypass is permitted + * @deprecated use {@link #createQuery(Player)} + */ + @Deprecated public boolean hasBypass(LocalPlayer player, World world) { return player.hasPermission("worldguard.region.bypass." + world.getName()); } + /** + * Test whether the given player has region protection bypass permission. + * + * @param player the player + * @param world the world + * @return true if a bypass is permitted + * @deprecated use {@link #createQuery(Player)} + */ + @Deprecated public boolean hasBypass(Player player, World world) { return plugin.hasPermission(player, "worldguard.region.bypass." + world.getName()); } + /** + * Test whether the player can build (place, use, destroy blocks and + * entities) at the given position, considering only the build flag + * and the region's members. + * + *

This method is not an absolute test as to whether WorldGuard + * would allow or block an event because this method doesn't + * consider flags (i.e. chest-access flags when concerning a chest) or + * other modules in WorldGuard (i.e chest protection).

+ * + * @param player the player + * @param block the block + * @return true if a bypass is permitted + * @deprecated use {@link #createQuery(Player)} + */ + @SuppressWarnings("deprecation") + @Deprecated public boolean canBuild(Player player, Block block) { return canBuild(player, block.getLocation()); } - public boolean canBuild(Player player, Location loc) { - World world = loc.getWorld(); - WorldConfiguration worldConfig = config.get(world); - - if (!worldConfig.useRegions) { - return true; - } - - LocalPlayer localPlayer = plugin.wrapPlayer(player); - - if (!hasBypass(player, world)) { - RegionManager mgr = get(world); - - if (mgr != null && !mgr.getApplicableRegions(BukkitUtil.toVector(loc)).canBuild(localPlayer)) { - return false; - } - } - - return true; + /** + * Test whether the player can build (place, use, destroy blocks and + * entities) at the given position, considering only the build flag + * and the region's members. + * + *

This method is not an absolute test as to whether WorldGuard + * would allow or block an event because this method doesn't + * consider flags (i.e. chest-access flags when concerning a chest) or + * other modules in WorldGuard (i.e chest protection).

+ * + * @param player the player + * @param location the location + * @return true if a bypass is permitted + * @deprecated use {@link #createQuery(Player)} + */ + @Deprecated + public boolean canBuild(Player player, Location location) { + return createQuery(player).canBuild(location); } + /** + * Test whether the player can place blocks at the given position. + * + * @param player the player + * @param block the block + * @return true if permitted + * @deprecated use {@link #createQuery(Player)} + */ + @Deprecated + @SuppressWarnings("deprecation") public boolean canConstruct(Player player, Block block) { return canConstruct(player, block.getLocation()); } - public boolean canConstruct(Player player, Location loc) { - World world = loc.getWorld(); - WorldConfiguration worldConfig = config.get(world); - - if (!worldConfig.useRegions) { - return true; - } - - LocalPlayer localPlayer = plugin.wrapPlayer(player); - - if (!hasBypass(player, world)) { - RegionManager mgr = get(world); - - if (mgr != null) { - final ApplicableRegionSet applicableRegions = mgr.getApplicableRegions(BukkitUtil.toVector(loc)); - if (!applicableRegions.canBuild(localPlayer)) { - return false; - } - if (!applicableRegions.canConstruct(localPlayer)) { - return false; - } - } - } - - return true; + /** + * Test whether the player can place blocks at the given position. + * + * @param player the player + * @param location the location + * @return true if permitted + * @deprecated use {@link #createQuery(Player)} + */ + @Deprecated + public boolean canConstruct(Player player, Location location) { + return createQuery(player).canConstruct(location); } - public boolean allows(StateFlag flag, Location loc) { - return allows(flag, loc, null); + /** + * Test the value of a state flag at a location. + * + * @param flag the flag + * @param location the location + * @return true if set to true + * @deprecated use {@link #createQuery(Player)} + */ + @Deprecated + @SuppressWarnings("deprecation") + public boolean allows(StateFlag flag, Location location) { + return allows(flag, location, null); } - public boolean allows(StateFlag flag, Location loc, @Nullable LocalPlayer player) { - World world = loc.getWorld(); - WorldConfiguration worldConfig = config.get(world); - - if (!worldConfig.useRegions) { - return true; - } - - RegionManager mgr = get(world); - return mgr == null || mgr.getApplicableRegions(toVector(loc)).allows(flag, player); + /** + * Test the value of a state flag at a location, using the player as the + * relevant actor. + * + * @param flag the flag + * @param location the location + * @param player the actor + * @return true if set to true + * @deprecated use {@link #createQuery(Player)} + */ + @Deprecated + public boolean allows(StateFlag flag, Location location, @Nullable LocalPlayer player) { + return createQuery(player).allows(flag, location); } } From 99660920d7c7dc1c698818243f577a5b9ba0eec0 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 01:24:38 -0700 Subject: [PATCH 033/133] Refactor region API with new RegionContainer object. --- .../worldguard/bukkit/BukkitBlacklist.java | 4 +- .../ManagerContainer.java | 63 +- .../worldguard/bukkit/RegionContainer.java | 242 ++++++ .../sk89q/worldguard/bukkit/RegionQuery.java | 196 +++++ .../worldguard/bukkit/WorldGuardPlugin.java | 31 +- .../bukkit/commands/RegionCommands.java | 2 +- .../bukkit/commands/WorldGuardCommands.java | 7 +- ...tEvent.java => AbstractDelegateEvent.java} | 11 +- .../event/block/AbstractBlockEvent.java | 8 +- .../bukkit/event/block/BreakBlockEvent.java | 6 +- .../bukkit/event/block/PlaceBlockEvent.java | 6 +- .../bukkit/event/block/UseBlockEvent.java | 6 +- .../event/entity/AbstractEntityEvent.java | 8 +- .../event/entity/DestroyEntityEvent.java | 3 +- .../bukkit/event/entity/SpawnEntityEvent.java | 6 +- .../bukkit/event/entity/UseEntityEvent.java | 3 +- .../bukkit/event/inventory/UseItemEvent.java | 8 +- .../{ => listener}/FlagStateManager.java | 508 ++++++------- .../listener/RegionProtectionListener.java | 75 +- .../bukkit/{ => listener}/SpongeUtil.java | 261 +++---- .../listener/WorldGuardBlockListener.java | 1 - .../listener/WorldGuardEntityListener.java | 2 +- .../listener/WorldGuardPlayerListener.java | 4 +- .../{ => util}/LoggerToChatHandler.java | 8 +- .../bukkit/util/ProtectedRegionQuery.java | 101 --- .../bukkit/{ => util}/RegionQueryUtil.java | 174 ++--- .../bukkit/{ => util}/ReportWriter.java | 689 +++++++++--------- .../protection/GlobalRegionManager.java | 114 +-- 28 files changed, 1431 insertions(+), 1116 deletions(-) rename src/main/java/com/sk89q/worldguard/{protection => bukkit}/ManagerContainer.java (79%) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java rename src/main/java/com/sk89q/worldguard/bukkit/event/{AbstractInteractEvent.java => AbstractDelegateEvent.java} (86%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/FlagStateManager.java (96%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => listener}/SpongeUtil.java (96%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => util}/LoggerToChatHandler.java (97%) delete mode 100644 src/main/java/com/sk89q/worldguard/bukkit/util/ProtectedRegionQuery.java rename src/main/java/com/sk89q/worldguard/bukkit/{ => util}/RegionQueryUtil.java (95%) rename src/main/java/com/sk89q/worldguard/bukkit/{ => util}/ReportWriter.java (97%) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/BukkitBlacklist.java b/src/main/java/com/sk89q/worldguard/bukkit/BukkitBlacklist.java index e98a7314..d333a650 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/BukkitBlacklist.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/BukkitBlacklist.java @@ -21,7 +21,8 @@ import com.sk89q.worldguard.blacklist.Blacklist; -public class BukkitBlacklist extends Blacklist { +class BukkitBlacklist extends Blacklist { + private WorldGuardPlugin plugin; public BukkitBlacklist(Boolean useAsWhitelist, WorldGuardPlugin plugin) { @@ -33,4 +34,5 @@ public BukkitBlacklist(Boolean useAsWhitelist, WorldGuardPlugin plugin) { public void broadcastNotification(String msg) { plugin.broadcastNotification(msg); } + } diff --git a/src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java b/src/main/java/com/sk89q/worldguard/bukkit/ManagerContainer.java similarity index 79% rename from src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java rename to src/main/java/com/sk89q/worldguard/bukkit/ManagerContainer.java index 2ce82afb..f79f09b7 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ManagerContainer.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/ManagerContainer.java @@ -17,10 +17,9 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.protection; +package com.sk89q.worldguard.bukkit; import com.google.common.base.Supplier; -import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.managers.index.ChunkHashTable; import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex; @@ -54,16 +53,29 @@ class ManagerContainer { private static final Logger log = Logger.getLogger(ManagerContainer.class.getCanonicalName()); private static final int SAVE_INTERVAL = 1000 * 30; + private final ConfigurationManager config; private final ConcurrentMap mapping = new ConcurrentHashMap(); private final Object lock = new Object(); private final EnumMap drivers = new EnumMap(DriverType.class); - private final RegionStoreDriver defaultDriver; + private RegionStoreDriver defaultDriver; private final Supplier indexFactory = new ChunkHashTable.Factory(new PriorityRTreeIndex.Factory()); private final Timer timer = new Timer(); + /** + * Create a new instance. + * + * @param config the configuration + */ ManagerContainer(ConfigurationManager config) { checkNotNull(config); + this.config = config; + timer.schedule(new BackgroundSaver(), SAVE_INTERVAL, SAVE_INTERVAL); + } + /** + * Create drivers from the configuration. + */ + public void initialize() { for (DriverType type : DriverType.values()) { drivers.put(type, type.create(config)); } @@ -73,10 +85,15 @@ class ManagerContainer { } else { defaultDriver = drivers.get(DriverType.YAML); } - - timer.schedule(new BackgroundSaver(), SAVE_INTERVAL, SAVE_INTERVAL); } + /** + * Load the {@code RegionManager} for the world with the given name, + * creating a new instance for the world if one does not exist yet. + * + * @param name the name of the world + * @return a region manager, or {@code null} if loading failed + */ @Nullable public RegionManager load(String name) { checkNotNull(name); @@ -100,6 +117,13 @@ public RegionManager load(String name) { } } + /** + * Create a new region manager and load the data. + * + * @param name the name of the world + * @return a region manager + * @throws IOException thrown if loading fals + */ private RegionManager createAndLoad(String name) throws IOException { RegionStore store = defaultDriver.get(name); RegionManager manager = new RegionManager(store, indexFactory); @@ -107,6 +131,14 @@ private RegionManager createAndLoad(String name) throws IOException { return manager; } + /** + * Unload the region manager associated with the given world name. + * + *

If no region manager has been loaded for the given name, then + * nothing will happen.

+ * + * @param name the name of the world + */ public void unload(String name) { checkNotNull(name); @@ -120,11 +152,16 @@ public void unload(String name) { } catch (IOException e) { log.log(Level.WARNING, "Failed to save the region data for '" + name + "'", e); } + + mapping.remove(normal); } - mapping.remove(normal); } } + /** + * Unload all region managers and save their contents before returning. + * This message may block for an extended period of time. + */ public void unloadAll() { synchronized (lock) { for (Map.Entry entry : mapping.entrySet()) { @@ -141,16 +178,30 @@ public void unloadAll() { } } + /** + * Get the region manager for the given world name. + * + * @param name the name of the world + * @return a region manager, or {@code null} if one was never loaded + */ @Nullable public RegionManager get(String name) { checkNotNull(name); return mapping.get(Normal.normal(name)); } + /** + * Get an immutable list of loaded region managers. + * + * @return an immutable list + */ public List getLoaded() { return Collections.unmodifiableList(new ArrayList(mapping.values())); } + /** + * A task to save managers in the background. + */ private class BackgroundSaver extends TimerTask { @Override public void run() { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java new file mode 100644 index 00000000..bd8f1348 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java @@ -0,0 +1,242 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit; + +import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A region container creates {@link RegionManager}s for loaded worlds, which + * allows access to the region data of a world. Generally, only data is + * loaded for worlds that are loaded in the server. + * + *

This class is thread safe and its contents can be accessed from + * multiple concurrent threads.

+ * + *

An instance of this class can be retrieved using + * {@link WorldGuardPlugin#getRegionContainer()}.

+ */ +public class RegionContainer { + + private final Object lock = new Object(); + private final WorldGuardPlugin plugin; + private final ManagerContainer container; + + /** + * Create a new instance. + * + * @param plugin the plugin + */ + RegionContainer(WorldGuardPlugin plugin) { + this.plugin = plugin; + + ConfigurationManager config = plugin.getGlobalStateManager(); + container = new ManagerContainer(config); + } + + /** + * Initialize the region container. + */ + void initialize() { + container.initialize(); + + loadWorlds(); + + Bukkit.getPluginManager().registerEvents(new Listener() { + @EventHandler + public void onWorldLoad(WorldLoadEvent event) { + load(event.getWorld()); + } + + @EventHandler + public void onWorldUnload(WorldUnloadEvent event) { + unload(event.getWorld()); + } + + @EventHandler + public void onChunkLoad(ChunkLoadEvent event) { + RegionManager manager = get(event.getWorld()); + if (manager != null) { + Chunk chunk = event.getChunk(); + manager.loadChunk(new Vector2D(chunk.getX(), chunk.getZ())); + } + } + + @EventHandler + public void onChunkUnload(ChunkUnloadEvent event) { + RegionManager manager = get(event.getWorld()); + if (manager != null) { + Chunk chunk = event.getChunk(); + manager.unloadChunk(new Vector2D(chunk.getX(), chunk.getZ())); + } + } + }, plugin); + } + + /** + * Save data and unload. + */ + void unload() { + synchronized (lock) { + container.unloadAll(); + } + } + + /** + * Try loading the region managers for all currently loaded worlds. + */ + private void loadWorlds() { + synchronized (lock) { + for (World world : Bukkit.getServer().getWorlds()) { + load(world); + } + } + } + + /** + * Reload the region container. + * + *

This method may block until the data for all loaded worlds has been + * unloaded and new data has been loaded.

+ */ + public void reload() { + synchronized (lock) { + unload(); + loadWorlds(); + } + } + + /** + * Load the region data for a world if it has not been loaded already. + * + * @param world the world + * @return a region manager, either returned from the cache or newly loaded + */ + @Nullable + private RegionManager load(World world) { + checkNotNull(world); + + RegionManager manager; + + synchronized (lock) { + manager = container.load(world.getName()); + + if (manager != null) { + // Bias the region data for loaded chunks + List positions = new ArrayList(); + for (Chunk chunk : world.getLoadedChunks()) { + positions.add(new Vector2D(chunk.getX(), chunk.getZ())); + } + manager.loadChunks(positions); + } + } + + return manager; + } + + /** + * Unload the region data for a world. + * + * @param world a world + */ + void unload(World world) { + checkNotNull(world); + + synchronized (lock) { + container.unload(world.getName()); + } + } + + /** + * Get the region manager for a world if one exists. + * + *

This method may return {@code null} if region data for the given + * world has not been loaded, has failed to load, or support for regions + * has been disabled. If you merely want to query flags or a list of + * regions in a position, using {@link #createQuery(Player)} is much + * simpler and it will handle the case of a {@code null} + * {@code RegionManager}. That said, if you wish to make changes to + * regions, then you will have to get a region manager.

+ * + * @param world the world + * @return a region manager, or {@code null} if one is not available + */ + @Nullable + public RegionManager get(World world) { + return container.get(world.getName()); + } + + /** + * Get an immutable list of loaded {@link RegionManager}s. + * + * @return a list of managers + */ + public List getLoaded() { + return Collections.unmodifiableList(container.getLoaded()); + } + + /** + * Create a new region query with no player. + * + * @return a new query + */ + public RegionQuery createAnonymousQuery() { + return new RegionQuery(plugin, (Player) null); + } + + /** + * Create a new region query. + * + * @param player a player, or {@code null} + * @return a new query + */ + public RegionQuery createQuery(@Nullable Player player) { + return new RegionQuery(plugin, player); + } + + /** + * Create a new region query. + * + * @param player a player, or {@code null} + * @return a new query + */ + public RegionQuery createQuery(@Nullable LocalPlayer player) { + return new RegionQuery(plugin, player); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java new file mode 100644 index 00000000..56f18664 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java @@ -0,0 +1,196 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit; + +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.GlobalRegionManager; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * This object allows easy spatial queries involving region data. + * + *

Results may be cached for brief amounts of time.

+ */ +public class RegionQuery { + + private final ConfigurationManager config; + private final GlobalRegionManager globalManager; + @Nullable + private final LocalPlayer localPlayer; + + /** + * Create a new instance. + * + * @param plugin the plugin + * @param player an optional player + */ + RegionQuery(WorldGuardPlugin plugin, @Nullable Player player) { + this(plugin, player != null ? plugin.wrapPlayer(player) : null); + } + + /** + * Create a new instance. + * + * @param plugin the plugin + * @param player an optional player + */ + RegionQuery(WorldGuardPlugin plugin, @Nullable LocalPlayer player) { + checkNotNull(plugin); + + this.config = plugin.getGlobalStateManager(); + //noinspection deprecation + this.globalManager = plugin.getGlobalRegionManager(); + this.localPlayer = player; + } + + /** + * Test whether the player (which must not be {@code null} can build at + * the given location, using only the membership information and the state + * of the {@link DefaultFlag#BUILD} flag to determine status. + * + *

This method is used to check blocks and entities for which there + * are no other related flags for (i.e. beds have the + * {@link DefaultFlag#SLEEP} flag).

+ * + *

If region data is not available (it failed to load or region support + * is disabled), then either {@code true} or {@code false} may be returned + * depending on the configuration.

+ * + * @param location the location + * @return true if building is permitted + * @throws NullPointerException if there is no player for this query + */ + public boolean testPermission(Location location) { + checkNotNull(location); + checkNotNull(localPlayer, "testPermission() requires a player for the query"); + + World world = location.getWorld(); + WorldConfiguration worldConfig = config.get(world); + + if (!worldConfig.useRegions) { + return true; + } + + if (globalManager.hasBypass(localPlayer, world)) { + return true; + } else { + RegionManager manager = globalManager.get(location.getWorld()); + return manager == null || manager.getApplicableRegions(BukkitUtil.toVector(location)).canBuild(localPlayer); + } + } + + /** + * Test whether the player (which must not be {@code null} can build at + * the given location, using the membership information, state + * of the {@link DefaultFlag#BUILD} flag, and the state of any passed + * flags. + * + *

This method is used to check blocks and entities for which there + * are other related flags for (i.e. beds have the + * {@link DefaultFlag#SLEEP} flag). The criteria under which this method + * returns true is subject to change (i.e. all flags must be true or + * one cannot be DENY, etc.).

+ * + *

If region data is not available (it failed to load or region support + * is disabled), then either {@code true} or {@code false} may be returned + * depending on the configuration.

+ * + * @param location the location to test + * @param flags an array of flags + * @return true if the flag tests true + */ + public boolean testPermission(Location location, StateFlag... flags) { + checkNotNull(location); + checkNotNull(flags); + checkNotNull(localPlayer, "testPermission() requires a player for the query"); + + World world = location.getWorld(); + WorldConfiguration worldConfig = config.get(world); + + if (!worldConfig.useRegions) { + return true; + } + + RegionManager manager = globalManager.get(location.getWorld()); + + if (manager != null) { + ApplicableRegionSet result = manager.getApplicableRegions(BukkitUtil.toVector(location)); + + if (result.canBuild(localPlayer)) { + return true; + } + + for (StateFlag flag : flags) { + if (result.allows(flag, localPlayer)) { + return true; + } + } + + return false; + } else{ + return true; // null manager -> return true for now + } + } + + /** + * Test whether a {@link StateFlag} is evaluates to {@code ALLOW}. + * + *

This method is to check whether certain functionality + * is enabled (i.e. water flow). The player, if provided, may be used + * in evaluation of the flag.

+ * + *

If region data is not available (it failed to load or region support + * is disabled), then either {@code true} or {@code false} may be returned + * depending on the configuration.

+ * + * @param location the location + * @param flag the flag + * @return true if the flag evaluates to {@code ALLOW} + */ + public boolean testEnabled(Location location, StateFlag flag) { + checkNotNull(location); + checkNotNull(flag); + + World world = location.getWorld(); + WorldConfiguration worldConfig = config.get(world); + + if (!worldConfig.useRegions) { + return true; + } + + if (globalManager.hasBypass(localPlayer, world)) { + return true; + } else { + RegionManager manager = globalManager.get(location.getWorld()); + return manager == null || manager.getApplicableRegions(BukkitUtil.toVector(location)).allows(flag, localPlayer); + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index b1605983..82d4c088 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -53,6 +53,7 @@ import com.sk89q.worldguard.bukkit.listener.ChestProtectionListener; import com.sk89q.worldguard.bukkit.listener.DebuggingListener; import com.sk89q.worldguard.bukkit.listener.EventAbstractionListener; +import com.sk89q.worldguard.bukkit.listener.FlagStateManager; import com.sk89q.worldguard.bukkit.listener.RegionProtectionListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardBlockListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardCommandBookListener; @@ -69,7 +70,6 @@ import com.sk89q.worldguard.protection.util.migrator.MigrationException; import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator; import com.sk89q.worldguard.util.FatalConfigurationLoadingException; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.World; @@ -109,8 +109,9 @@ public class WorldGuardPlugin extends JavaPlugin { private static WorldGuardPlugin inst; private final CommandsManager commands; - private GlobalRegionManager globalRegionManager; - private ConfigurationManager configuration; + private final ConfigurationManager configuration = new ConfigurationManager(this); + private final RegionContainer regionContainer = new RegionContainer(this); + private final GlobalRegionManager globalRegionManager = new GlobalRegionManager(this, regionContainer); private FlagStateManager flagStateManager; private final Supervisor supervisor = new SimpleSupervisor(); private ListeningExecutorService executorService; @@ -145,7 +146,8 @@ public static WorldGuardPlugin inst() { @Override @SuppressWarnings("deprecation") public void onEnable() { - configuration = new ConfigurationManager(this); + getDataFolder().mkdirs(); // Need to create the plugins/WorldGuard folder + executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20)); // Set the proper command injector @@ -165,9 +167,6 @@ public void run() { } }, 0L); - // Need to create the plugins/WorldGuard folder - getDataFolder().mkdirs(); - File cacheDir = new File(getDataFolder(), "cache"); cacheDir.mkdirs(); try { @@ -190,8 +189,7 @@ public void run() { configuration.load(); getLogger().info("Loading region data..."); - globalRegionManager = new GlobalRegionManager(this); - globalRegionManager.loadAll(Bukkit.getServer().getWorlds()); + regionContainer.initialize(); migrateRegionUniqueIds(); // Migrate to UUIDs } catch (FatalConfigurationLoadingException e) { @@ -282,7 +280,7 @@ public void onDisable() { getLogger().log(Level.WARNING, "Some tasks failed while waiting for remaining tasks to finish", e); } - globalRegionManager.unloadAll(); + regionContainer.unload(); configuration.unload(); this.getServer().getScheduler().cancelTasks(this); } @@ -362,12 +360,23 @@ public String convertThrowable(@Nullable Throwable throwable) { /** * Get the GlobalRegionManager. * - * @return The plugin's global region manager + * @return the plugin's global region manager + * @deprecated use {@link #getRegionContainer()} */ + @Deprecated public GlobalRegionManager getGlobalRegionManager() { return globalRegionManager; } + /** + * Get the object that manages region data. + * + * @return the region container + */ + public RegionContainer getRegionContainer() { + return regionContainer; + } + /** * Get the WorldGuard Configuration. * diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java index e4928023..e30423ea 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java @@ -34,7 +34,7 @@ import com.sk89q.worldedit.bukkit.selections.Polygonal2DSelection; import com.sk89q.worldedit.bukkit.selections.Selection; import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.bukkit.LoggerToChatHandler; +import com.sk89q.worldguard.bukkit.util.LoggerToChatHandler; import com.sk89q.worldguard.bukkit.permission.RegionPermissionModel; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java index 17e7dd7c..9812e0eb 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java @@ -25,8 +25,8 @@ import com.sk89q.minecraft.util.commands.CommandPermissions; import com.sk89q.odeum.task.Task; import com.sk89q.odeum.task.TaskStateComparator; -import com.sk89q.worldguard.bukkit.LoggerToChatHandler; -import com.sk89q.worldguard.bukkit.ReportWriter; +import com.sk89q.worldguard.bukkit.util.LoggerToChatHandler; +import com.sk89q.worldguard.bukkit.util.ReportWriter; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.util.PastebinPoster; import com.sk89q.worldguard.util.PastebinPoster.PasteCallback; @@ -78,9 +78,8 @@ public void reload(CommandContext args, CommandSender sender) throws CommandExce try { plugin.getGlobalStateManager().unload(); - plugin.getGlobalRegionManager().unloadAll(); + plugin.getRegionContainer().reload(); plugin.getGlobalStateManager().load(); - plugin.getGlobalRegionManager().loadAll(Bukkit.getServer().getWorlds()); // WGBukkit.cleanCache(); sender.sendMessage("WorldGuard configuration reloaded."); } catch (Throwable t) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/AbstractInteractEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/AbstractDelegateEvent.java similarity index 86% rename from src/main/java/com/sk89q/worldguard/bukkit/event/AbstractInteractEvent.java rename to src/main/java/com/sk89q/worldguard/bukkit/event/AbstractDelegateEvent.java index 0c4aa410..ce9ce635 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/AbstractInteractEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/AbstractDelegateEvent.java @@ -23,10 +23,13 @@ import org.bukkit.event.Cancellable; import org.bukkit.event.Event; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkNotNull; -public abstract class AbstractInteractEvent extends Event implements Cancellable { +public abstract class AbstractDelegateEvent extends Event implements Cancellable { + @Nullable private final Event originalEvent; private final Cause cause; private boolean cancelled; @@ -37,8 +40,7 @@ public abstract class AbstractInteractEvent extends Event implements Cancellable * @param originalEvent the original event * @param cause the cause */ - protected AbstractInteractEvent(Event originalEvent, Cause cause) { - checkNotNull(originalEvent); + protected AbstractDelegateEvent(@Nullable Event originalEvent, Cause cause) { checkNotNull(cause); this.originalEvent = originalEvent; this.cause = cause; @@ -47,8 +49,9 @@ protected AbstractInteractEvent(Event originalEvent, Cause cause) { /** * Get the original event. * - * @return the original event + * @return the original event, which may be {@code null} if unavailable */ + @Nullable public Event getOriginalEvent() { return originalEvent; } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java index 1d01971d..6ebd57bf 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java @@ -20,7 +20,7 @@ package com.sk89q.worldguard.bukkit.event.block; import com.sk89q.worldguard.bukkit.cause.Cause; -import com.sk89q.worldguard.bukkit.event.AbstractInteractEvent; +import com.sk89q.worldguard.bukkit.event.AbstractDelegateEvent; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; @@ -30,14 +30,14 @@ import static com.google.common.base.Preconditions.checkNotNull; -abstract class AbstractBlockEvent extends AbstractInteractEvent { +abstract class AbstractBlockEvent extends AbstractDelegateEvent { private final Location target; @Nullable private final Block block; private final Material effectiveMaterial; - protected AbstractBlockEvent(Event originalEvent, Cause cause, Block block) { + protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { super(originalEvent, cause); checkNotNull(block); this.target = block.getLocation(); @@ -45,7 +45,7 @@ protected AbstractBlockEvent(Event originalEvent, Cause cause, Block block) { this.effectiveMaterial = block.getType(); } - protected AbstractBlockEvent(Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { + protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { super(originalEvent, cause); this.target = target; this.block = null; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java index e3647c62..097eeaef 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java @@ -26,15 +26,17 @@ import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import javax.annotation.Nullable; + public class BreakBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); - public BreakBlockEvent(Event originalEvent, Cause cause, Block block) { + public BreakBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { super(originalEvent, cause, block); } - public BreakBlockEvent(Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { + public BreakBlockEvent(@Nullable Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { super(originalEvent, cause, target, effectiveMaterial); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java index 7a02a876..c6dd711d 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java @@ -26,15 +26,17 @@ import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import javax.annotation.Nullable; + public class PlaceBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); - public PlaceBlockEvent(Event originalEvent, Cause cause, Block block) { + public PlaceBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { super(originalEvent, cause, block); } - public PlaceBlockEvent(Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { + public PlaceBlockEvent(@Nullable Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { super(originalEvent, cause, target, effectiveMaterial); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java index bd22dbf0..5c581196 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java @@ -26,6 +26,8 @@ import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import javax.annotation.Nullable; + /** * Fired when a block is interacted with. */ @@ -33,11 +35,11 @@ public class UseBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); - public UseBlockEvent(Event originalEvent, Cause cause, Block block) { + public UseBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { super(originalEvent, cause, block); } - public UseBlockEvent(Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { + public UseBlockEvent(@Nullable Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { super(originalEvent, cause, target, effectiveMaterial); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java index 093325e8..83743552 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/AbstractEntityEvent.java @@ -20,7 +20,7 @@ package com.sk89q.worldguard.bukkit.event.entity; import com.sk89q.worldguard.bukkit.cause.Cause; -import com.sk89q.worldguard.bukkit.event.AbstractInteractEvent; +import com.sk89q.worldguard.bukkit.event.AbstractDelegateEvent; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.event.Event; @@ -29,20 +29,20 @@ import static com.google.common.base.Preconditions.checkNotNull; -abstract class AbstractEntityEvent extends AbstractInteractEvent { +abstract class AbstractEntityEvent extends AbstractDelegateEvent { private final Location target; @Nullable private final Entity entity; - protected AbstractEntityEvent(Event originalEvent, Cause cause, Entity entity) { + protected AbstractEntityEvent(@Nullable Event originalEvent, Cause cause, Entity entity) { super(originalEvent, cause); checkNotNull(entity); this.target = entity.getLocation(); this.entity = entity; } - protected AbstractEntityEvent(Event originalEvent, Cause cause, Location target) { + protected AbstractEntityEvent(@Nullable Event originalEvent, Cause cause, Location target) { super(originalEvent, cause); checkNotNull(target); this.target = target; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java index db53d8eb..fd2d4056 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/DestroyEntityEvent.java @@ -25,6 +25,7 @@ import org.bukkit.event.HandlerList; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkNotNull; @@ -32,7 +33,7 @@ public class DestroyEntityEvent extends AbstractEntityEvent { private static final HandlerList handlers = new HandlerList(); - public DestroyEntityEvent(Event originalEvent, Cause cause, Entity target) { + public DestroyEntityEvent(@Nullable Event originalEvent, Cause cause, Entity target) { super(originalEvent, cause, checkNotNull(target)); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java index 05fee2f7..00817798 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/SpawnEntityEvent.java @@ -26,6 +26,8 @@ import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkNotNull; public class SpawnEntityEvent extends AbstractEntityEvent { @@ -33,12 +35,12 @@ public class SpawnEntityEvent extends AbstractEntityEvent { private static final HandlerList handlers = new HandlerList(); private final EntityType effectiveType; - public SpawnEntityEvent(Event originalEvent, Cause cause, Entity target) { + public SpawnEntityEvent(@Nullable Event originalEvent, Cause cause, Entity target) { super(originalEvent, cause, checkNotNull(target)); this.effectiveType = target.getType(); } - public SpawnEntityEvent(Event originalEvent, Cause cause, Location location, EntityType type) { + public SpawnEntityEvent(@Nullable Event originalEvent, Cause cause, Location location, EntityType type) { super(originalEvent, cause, location); checkNotNull(type); this.effectiveType = type; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java index 66b34282..9df27960 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/entity/UseEntityEvent.java @@ -25,6 +25,7 @@ import org.bukkit.event.HandlerList; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkNotNull; @@ -35,7 +36,7 @@ public class UseEntityEvent extends AbstractEntityEvent { private static final HandlerList handlers = new HandlerList(); - public UseEntityEvent(Event originalEvent, Cause cause, Entity target) { + public UseEntityEvent(@Nullable Event originalEvent, Cause cause, Entity target) { super(originalEvent, cause, checkNotNull(target)); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java index acd0185e..6dbb91ce 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/inventory/UseItemEvent.java @@ -20,24 +20,26 @@ package com.sk89q.worldguard.bukkit.event.inventory; import com.sk89q.worldguard.bukkit.cause.Cause; -import com.sk89q.worldguard.bukkit.event.AbstractInteractEvent; +import com.sk89q.worldguard.bukkit.event.AbstractDelegateEvent; import org.bukkit.World; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.bukkit.inventory.ItemStack; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkNotNull; /** * Fired when an item is interacted with. */ -public class UseItemEvent extends AbstractInteractEvent { +public class UseItemEvent extends AbstractDelegateEvent { private static final HandlerList handlers = new HandlerList(); private final World world; private final ItemStack itemStack; - public UseItemEvent(Event originalEvent, Cause cause, World world, ItemStack itemStack) { + public UseItemEvent(@Nullable Event originalEvent, Cause cause, World world, ItemStack itemStack) { super(originalEvent, cause); checkNotNull(world); checkNotNull(itemStack); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/FlagStateManager.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/FlagStateManager.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/bukkit/FlagStateManager.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/FlagStateManager.java index 9988a155..af6365c6 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/FlagStateManager.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/FlagStateManager.java @@ -1,252 +1,256 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; -import org.bukkit.GameMode; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import java.util.HashMap; -import java.util.Map; - -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - -/** - * This processes per-player state information and is also meant to be used - * as a scheduled task. - * - * @author sk89q - */ -public class FlagStateManager implements Runnable { - - public static final int RUN_DELAY = 20; - - private WorldGuardPlugin plugin; - private Map states; - - /** - * Construct the object. - * - * @param plugin The plugin instance - */ - public FlagStateManager(WorldGuardPlugin plugin) { - this.plugin = plugin; - - states = new HashMap(); - } - - /** - * Run the task. - */ - @Override - public void run() { - Player[] players = plugin.getServer().getOnlinePlayers(); - ConfigurationManager config = plugin.getGlobalStateManager(); - - for (Player player : players) { - WorldConfiguration worldConfig = config.get(player.getWorld()); - - if (!worldConfig.useRegions) { - continue; - } - - PlayerFlagState state; - - synchronized (this) { - state = states.get(player.getName()); - - if (state == null) { - state = new PlayerFlagState(); - states.put(player.getName(), state); - } - } - - Vector playerLocation = toVector(player.getLocation()); - RegionManager regionManager = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet applicable = regionManager.getApplicableRegions(playerLocation); - - if (!RegionQueryUtil.isInvincible(plugin, player, applicable) - && !plugin.getGlobalStateManager().hasGodMode(player) - && !(player.getGameMode() == GameMode.CREATIVE)) { - processHeal(applicable, player, state); - processFeed(applicable, player, state); - } - } - } - - /** - * Process healing for a player. - * - * @param applicable The set of applicable regions - * @param player The player to process healing flags on - * @param state The player's state - */ - private void processHeal(ApplicableRegionSet applicable, Player player, - PlayerFlagState state) { - - if (player.getHealth() <= 0) { - return; - } - - long now = System.currentTimeMillis(); - - Integer healAmount = applicable.getFlag(DefaultFlag.HEAL_AMOUNT); - Integer healDelay = applicable.getFlag(DefaultFlag.HEAL_DELAY); - Double minHealth = applicable.getFlag(DefaultFlag.MIN_HEAL); - Double maxHealth = applicable.getFlag(DefaultFlag.MAX_HEAL); - - if (healAmount == null || healDelay == null || healAmount == 0 || healDelay < 0) { - return; - } - if (minHealth == null) { - minHealth = 0.0; - } - if (maxHealth == null) { - maxHealth = player.getMaxHealth(); - } - - // Apply a cap to prevent possible exceptions - minHealth = Math.min(player.getMaxHealth(), minHealth); - maxHealth = Math.min(player.getMaxHealth(), maxHealth); - - if (player.getHealth() >= maxHealth && healAmount > 0) { - return; - } - - if (healDelay <= 0) { - player.setHealth(healAmount > 0 ? maxHealth : minHealth); // this will insta-kill if the flag is unset - state.lastHeal = now; - } else if (now - state.lastHeal > healDelay * 1000) { - // clamp health between minimum and maximum - player.setHealth(Math.min(maxHealth, Math.max(minHealth, player.getHealth() + healAmount))); - state.lastHeal = now; - } - } - - /** - * Process restoring hunger for a player. - * - * @param applicable The set of applicable regions - * @param player The player to process hunger flags on - * @param state The player's state - */ - private void processFeed(ApplicableRegionSet applicable, Player player, - PlayerFlagState state) { - - long now = System.currentTimeMillis(); - - Integer feedAmount = applicable.getFlag(DefaultFlag.FEED_AMOUNT); - Integer feedDelay = applicable.getFlag(DefaultFlag.FEED_DELAY); - Integer minHunger = applicable.getFlag(DefaultFlag.MIN_FOOD); - Integer maxHunger = applicable.getFlag(DefaultFlag.MAX_FOOD); - - if (feedAmount == null || feedDelay == null || feedAmount == 0 || feedDelay < 0) { - return; - } - if (minHunger == null) { - minHunger = 0; - } - if (maxHunger == null) { - maxHunger = 20; - } - - // Apply a cap to prevent possible exceptions - minHunger = Math.min(20, minHunger); - maxHunger = Math.min(20, maxHunger); - - if (player.getFoodLevel() >= maxHunger && feedAmount > 0) { - return; - } - - if (feedDelay <= 0) { - player.setFoodLevel(feedAmount > 0 ? maxHunger : minHunger); - player.setSaturation(player.getFoodLevel()); - state.lastFeed = now; - } else if (now - state.lastFeed > feedDelay * 1000) { - // clamp health between minimum and maximum - player.setFoodLevel(Math.min(maxHunger, Math.max(minHunger, player.getFoodLevel() + feedAmount))); - player.setSaturation(player.getFoodLevel()); - state.lastFeed = now; - } - } - - /** - * Forget a player. - * - * @param player The player to forget - */ - public synchronized void forget(Player player) { - states.remove(player.getName()); - } - - /** - * Forget all managed players. Use with caution. - */ - public synchronized void forgetAll() { - states.clear(); - } - - /** - * Get a player's flag state. A new state will be created if there is no existing - * state for the player. - * - * @param player The player to get a state for - * @return The {@code player}'s state - */ - public synchronized PlayerFlagState getState(Player player) { - PlayerFlagState state = states.get(player.getName()); - - if (state == null) { - state = new PlayerFlagState(); - states.put(player.getName(), state); - } - - return state; - } - - /** - * Keeps state per player. - */ - public static class PlayerFlagState { - public long lastHeal; - public long lastFeed; - public String lastGreeting; - public String lastFarewell; - public Boolean lastExitAllowed = null; - public Boolean notifiedForLeave = false; - public Boolean notifiedForEnter = false; - public GameMode lastGameMode; - public World lastWorld; - public int lastBlockX; - public int lastBlockY; - public int lastBlockZ; - - /* Used to cache invincibility status */ - public World lastInvincibleWorld; - public int lastInvincibleX; - public int lastInvincibleY; - public int lastInvincibleZ; - public boolean wasInvincible; - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.util.RegionQueryUtil; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.GameMode; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; + +/** + * This processes per-player state information and is also meant to be used + * as a scheduled task. + * + * @author sk89q + */ +public class FlagStateManager implements Runnable { + + public static final int RUN_DELAY = 20; + + private WorldGuardPlugin plugin; + private Map states; + + /** + * Construct the object. + * + * @param plugin The plugin instance + */ + public FlagStateManager(WorldGuardPlugin plugin) { + this.plugin = plugin; + + states = new HashMap(); + } + + /** + * Run the task. + */ + @Override + public void run() { + Player[] players = plugin.getServer().getOnlinePlayers(); + ConfigurationManager config = plugin.getGlobalStateManager(); + + for (Player player : players) { + WorldConfiguration worldConfig = config.get(player.getWorld()); + + if (!worldConfig.useRegions) { + continue; + } + + PlayerFlagState state; + + synchronized (this) { + state = states.get(player.getName()); + + if (state == null) { + state = new PlayerFlagState(); + states.put(player.getName(), state); + } + } + + Vector playerLocation = toVector(player.getLocation()); + RegionManager regionManager = plugin.getGlobalRegionManager().get(player.getWorld()); + ApplicableRegionSet applicable = regionManager.getApplicableRegions(playerLocation); + + if (!RegionQueryUtil.isInvincible(plugin, player, applicable) + && !plugin.getGlobalStateManager().hasGodMode(player) + && !(player.getGameMode() == GameMode.CREATIVE)) { + processHeal(applicable, player, state); + processFeed(applicable, player, state); + } + } + } + + /** + * Process healing for a player. + * + * @param applicable The set of applicable regions + * @param player The player to process healing flags on + * @param state The player's state + */ + private void processHeal(ApplicableRegionSet applicable, Player player, + PlayerFlagState state) { + + if (player.getHealth() <= 0) { + return; + } + + long now = System.currentTimeMillis(); + + Integer healAmount = applicable.getFlag(DefaultFlag.HEAL_AMOUNT); + Integer healDelay = applicable.getFlag(DefaultFlag.HEAL_DELAY); + Double minHealth = applicable.getFlag(DefaultFlag.MIN_HEAL); + Double maxHealth = applicable.getFlag(DefaultFlag.MAX_HEAL); + + if (healAmount == null || healDelay == null || healAmount == 0 || healDelay < 0) { + return; + } + if (minHealth == null) { + minHealth = 0.0; + } + if (maxHealth == null) { + maxHealth = player.getMaxHealth(); + } + + // Apply a cap to prevent possible exceptions + minHealth = Math.min(player.getMaxHealth(), minHealth); + maxHealth = Math.min(player.getMaxHealth(), maxHealth); + + if (player.getHealth() >= maxHealth && healAmount > 0) { + return; + } + + if (healDelay <= 0) { + player.setHealth(healAmount > 0 ? maxHealth : minHealth); // this will insta-kill if the flag is unset + state.lastHeal = now; + } else if (now - state.lastHeal > healDelay * 1000) { + // clamp health between minimum and maximum + player.setHealth(Math.min(maxHealth, Math.max(minHealth, player.getHealth() + healAmount))); + state.lastHeal = now; + } + } + + /** + * Process restoring hunger for a player. + * + * @param applicable The set of applicable regions + * @param player The player to process hunger flags on + * @param state The player's state + */ + private void processFeed(ApplicableRegionSet applicable, Player player, + PlayerFlagState state) { + + long now = System.currentTimeMillis(); + + Integer feedAmount = applicable.getFlag(DefaultFlag.FEED_AMOUNT); + Integer feedDelay = applicable.getFlag(DefaultFlag.FEED_DELAY); + Integer minHunger = applicable.getFlag(DefaultFlag.MIN_FOOD); + Integer maxHunger = applicable.getFlag(DefaultFlag.MAX_FOOD); + + if (feedAmount == null || feedDelay == null || feedAmount == 0 || feedDelay < 0) { + return; + } + if (minHunger == null) { + minHunger = 0; + } + if (maxHunger == null) { + maxHunger = 20; + } + + // Apply a cap to prevent possible exceptions + minHunger = Math.min(20, minHunger); + maxHunger = Math.min(20, maxHunger); + + if (player.getFoodLevel() >= maxHunger && feedAmount > 0) { + return; + } + + if (feedDelay <= 0) { + player.setFoodLevel(feedAmount > 0 ? maxHunger : minHunger); + player.setSaturation(player.getFoodLevel()); + state.lastFeed = now; + } else if (now - state.lastFeed > feedDelay * 1000) { + // clamp health between minimum and maximum + player.setFoodLevel(Math.min(maxHunger, Math.max(minHunger, player.getFoodLevel() + feedAmount))); + player.setSaturation(player.getFoodLevel()); + state.lastFeed = now; + } + } + + /** + * Forget a player. + * + * @param player The player to forget + */ + public synchronized void forget(Player player) { + states.remove(player.getName()); + } + + /** + * Forget all managed players. Use with caution. + */ + public synchronized void forgetAll() { + states.clear(); + } + + /** + * Get a player's flag state. A new state will be created if there is no existing + * state for the player. + * + * @param player The player to get a state for + * @return The {@code player}'s state + */ + public synchronized PlayerFlagState getState(Player player) { + PlayerFlagState state = states.get(player.getName()); + + if (state == null) { + state = new PlayerFlagState(); + states.put(player.getName(), state); + } + + return state; + } + + /** + * Keeps state per player. + */ + public static class PlayerFlagState { + public long lastHeal; + public long lastFeed; + public String lastGreeting; + public String lastFarewell; + public Boolean lastExitAllowed = null; + public Boolean notifiedForLeave = false; + public Boolean notifiedForEnter = false; + public GameMode lastGameMode; + public World lastWorld; + public int lastBlockX; + public int lastBlockY; + public int lastBlockZ; + + /* Used to cache invincibility status */ + public World lastInvincibleWorld; + public int lastInvincibleX; + public int lastInvincibleY; + public int lastInvincibleZ; + public boolean wasInvincible; + } +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java index 66300fe3..2eec00ed 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java @@ -19,7 +19,7 @@ package com.sk89q.worldguard.bukkit.listener; -import com.sk89q.worldedit.Vector2D; +import com.sk89q.worldguard.bukkit.RegionQuery; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; @@ -29,21 +29,14 @@ import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; import com.sk89q.worldguard.bukkit.util.Entities; import com.sk89q.worldguard.bukkit.util.Materials; -import com.sk89q.worldguard.bukkit.util.ProtectedRegionQuery; import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.ChatColor; -import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.command.CommandSender; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; -import org.bukkit.event.world.ChunkLoadEvent; -import org.bukkit.event.world.ChunkUnloadEvent; -import org.bukkit.event.world.WorldLoadEvent; -import org.bukkit.event.world.WorldUnloadEvent; /** * Handle events that need to be processed by region protection. @@ -63,34 +56,6 @@ private void tellErrorMessage(CommandSender sender, Object subject) { sender.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); } - @EventHandler - public void onWorldLoad(WorldLoadEvent event) { - getPlugin().getGlobalRegionManager().load(event.getWorld()); - } - - @EventHandler - public void onWorldUnload(WorldUnloadEvent event) { - getPlugin().getGlobalRegionManager().unload(event.getWorld()); - } - - @EventHandler - public void onChunkLoad(ChunkLoadEvent event) { - RegionManager manager = getPlugin().getGlobalRegionManager().get(event.getWorld()); - if (manager != null) { - Chunk chunk = event.getChunk(); - manager.loadChunk(new Vector2D(chunk.getX(), chunk.getZ())); - } - } - - @EventHandler - public void onChunkUnload(ChunkUnloadEvent event) { - RegionManager manager = getPlugin().getGlobalRegionManager().get(event.getWorld()); - if (manager != null) { - Chunk chunk = event.getChunk(); - manager.unloadChunk(new Vector2D(chunk.getX(), chunk.getZ())); - } - } - @EventHandler(ignoreCancelled = true) public void onPlaceBlock(PlaceBlockEvent event) { Player player = event.getCause().getPlayerRootCause(); @@ -98,15 +63,15 @@ public void onPlaceBlock(PlaceBlockEvent event) { Material type = event.getEffectiveMaterial(); if (player != null) { - ProtectedRegionQuery query = new ProtectedRegionQuery(getPlugin(), player); + RegionQuery query = getPlugin().getRegionContainer().createQuery(player); boolean canPlace; // Flint and steel, fire charge if (type == Material.FIRE) { - canPlace = query.allows(DefaultFlag.LIGHTER, target) || (query.canBuild(target) && query.canConstruct(target)); + canPlace = query.testPermission(target, DefaultFlag.LIGHTER); } else { - canPlace = query.canBuild(target) && query.canConstruct(target); + canPlace = query.testPermission(target); } if (!canPlace) { @@ -139,30 +104,24 @@ public void onUseBlock(UseBlockEvent event) { Material type = event.getEffectiveMaterial(); if (player != null) { - ProtectedRegionQuery query = new ProtectedRegionQuery(getPlugin(), player); + RegionQuery query = getPlugin().getRegionContainer().createQuery(player); boolean canUse; // Inventory blocks (CHEST_ACCESS) if (Materials.isInventoryBlock(type)) { - canUse = query.canBuild( target) - || query.allows(DefaultFlag.CHEST_ACCESS, target) - || query.allows(DefaultFlag.USE, target); + canUse = query.testPermission(target, DefaultFlag.USE, DefaultFlag.CHEST_ACCESS); // Beds (SLEEP) } else if (type == Material.BED) { - canUse = query.canBuild(target) - || query.allows(DefaultFlag.SLEEP, target) - || query.allows(DefaultFlag.USE, target); + canUse = query.testPermission(target, DefaultFlag.USE, DefaultFlag.SLEEP); // TNT (TNT) } else if (type == Material.TNT) { - canUse = query.canBuild(target) - || query.allows(DefaultFlag.TNT, target); + canUse = query.testPermission(target, DefaultFlag.TNT); // Everything else } else { - canUse = query.canBuild(target) - || query.allows(DefaultFlag.USE, target); + canUse = query.testPermission(target, DefaultFlag.USE); } if (!canUse) { @@ -179,13 +138,13 @@ public void onSpawnEntity(SpawnEntityEvent event) { EntityType type = event.getEffectiveType(); if (player != null) { - ProtectedRegionQuery query = new ProtectedRegionQuery(getPlugin(), player); + RegionQuery query = getPlugin().getRegionContainer().createQuery(player); boolean canSpawn; if (Entities.isVehicle(type)) { - canSpawn = query.canBuild(target) || query.allows(DefaultFlag.PLACE_VEHICLE, target); + canSpawn = query.testPermission(target, DefaultFlag.PLACE_VEHICLE); } else { - canSpawn = query.canBuild(target); + canSpawn = query.testPermission(target); } if (!canSpawn) { @@ -202,13 +161,13 @@ public void onDestroyEntity(DestroyEntityEvent event) { EntityType type = event.getEntity().getType(); if (player != null) { - ProtectedRegionQuery query = new ProtectedRegionQuery(getPlugin(), player); + RegionQuery query = getPlugin().getRegionContainer().createQuery(player); boolean canDestroy; if (Entities.isVehicle(type)) { - canDestroy = query.canBuild(target) || query.allows(DefaultFlag.DESTROY_VEHICLE, target); + canDestroy = query.testPermission(target, DefaultFlag.DESTROY_VEHICLE); } else { - canDestroy = query.canBuild(target); + canDestroy = query.testPermission(target); } if (!canDestroy) { @@ -224,8 +183,8 @@ public void onUseEntity(UseEntityEvent event) { Location target = event.getTarget(); if (player != null) { - ProtectedRegionQuery query = new ProtectedRegionQuery(getPlugin(), player); - boolean canUse = query.canBuild(target) || query.allows(DefaultFlag.USE, target); + RegionQuery query = getPlugin().getRegionContainer().createQuery(player); + boolean canUse = query.testPermission(target, DefaultFlag.USE); if (!canUse) { tellErrorMessage(player, target); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/SpongeUtil.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/SpongeUtil.java similarity index 96% rename from src/main/java/com/sk89q/worldguard/bukkit/SpongeUtil.java rename to src/main/java/com/sk89q/worldguard/bukkit/listener/SpongeUtil.java index c596630c..c5b52a67 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/SpongeUtil.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/SpongeUtil.java @@ -1,129 +1,132 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import org.bukkit.World; - -import static com.sk89q.worldguard.bukkit.BukkitUtil.isBlockWater; -import static com.sk89q.worldguard.bukkit.BukkitUtil.setBlockToWater; - -public final class SpongeUtil { - - private SpongeUtil() { - } - - /** - * Remove water around a sponge. - * - * @param plugin The plugin instace - * @param world The world the sponge isin - * @param ox The x coordinate of the 'sponge' block - * @param oy The y coordinate of the 'sponge' block - * @param oz The z coordinate of the 'sponge' block - */ - public static void clearSpongeWater(WorldGuardPlugin plugin, World world, int ox, int oy, int oz) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - for (int cx = -wcfg.spongeRadius; cx <= wcfg.spongeRadius; cx++) { - for (int cy = -wcfg.spongeRadius; cy <= wcfg.spongeRadius; cy++) { - for (int cz = -wcfg.spongeRadius; cz <= wcfg.spongeRadius; cz++) { - if (isBlockWater(world, ox + cx, oy + cy, oz + cz)) { - world.getBlockAt(ox + cx, oy + cy, oz + cz).setTypeId(0); - } - } - } - } - } - - /** - * Add water around a sponge. - * - * @param plugin The plugin instance - * @param world The world the sponge is located in - * @param ox The x coordinate of the 'sponge' block - * @param oy The y coordinate of the 'sponge' block - * @param oz The z coordinate of the 'sponge' block - */ - public static void addSpongeWater(WorldGuardPlugin plugin, World world, int ox, int oy, int oz) { - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(world); - - // The negative x edge - int cx = ox - wcfg.spongeRadius - 1; - for (int cy = oy - wcfg.spongeRadius - 1; cy <= oy + wcfg.spongeRadius + 1; cy++) { - for (int cz = oz - wcfg.spongeRadius - 1; cz <= oz + wcfg.spongeRadius + 1; cz++) { - if (isBlockWater(world, cx, cy, cz)) { - setBlockToWater(world, cx + 1, cy, cz); - } - } - } - - // The positive x edge - cx = ox + wcfg.spongeRadius + 1; - for (int cy = oy - wcfg.spongeRadius - 1; cy <= oy + wcfg.spongeRadius + 1; cy++) { - for (int cz = oz - wcfg.spongeRadius - 1; cz <= oz + wcfg.spongeRadius + 1; cz++) { - if (isBlockWater(world, cx, cy, cz)) { - setBlockToWater(world, cx - 1, cy, cz); - } - } - } - - // The negative y edge - int cy = oy - wcfg.spongeRadius - 1; - for (cx = ox - wcfg.spongeRadius - 1; cx <= ox + wcfg.spongeRadius + 1; cx++) { - for (int cz = oz - wcfg.spongeRadius - 1; cz <= oz + wcfg.spongeRadius + 1; cz++) { - if (isBlockWater(world, cx, cy, cz)) { - setBlockToWater(world, cx, cy + 1, cz); - } - } - } - - // The positive y edge - cy = oy + wcfg.spongeRadius + 1; - for (cx = ox - wcfg.spongeRadius - 1; cx <= ox + wcfg.spongeRadius + 1; cx++) { - for (int cz = oz - wcfg.spongeRadius - 1; cz <= oz + wcfg.spongeRadius + 1; cz++) { - if (isBlockWater(world, cx, cy, cz)) { - setBlockToWater(world, cx, cy - 1, cz); - } - } - } - - // The negative z edge - int cz = oz - wcfg.spongeRadius - 1; - for (cx = ox - wcfg.spongeRadius - 1; cx <= ox + wcfg.spongeRadius + 1; cx++) { - for (cy = oy - wcfg.spongeRadius - 1; cy <= oy + wcfg.spongeRadius + 1; cy++) { - if (isBlockWater(world, cx, cy, cz)) { - setBlockToWater(world, cx, cy, cz + 1); - } - } - } - - // The positive z edge - cz = oz + wcfg.spongeRadius + 1; - for (cx = ox - wcfg.spongeRadius - 1; cx <= ox + wcfg.spongeRadius + 1; cx++) { - for (cy = oy - wcfg.spongeRadius - 1; cy <= oy + wcfg.spongeRadius + 1; cy++) { - if (isBlockWater(world, cx, cy, cz)) { - setBlockToWater(world, cx, cy, cz - 1); - } - } - } - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.listener; + +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import org.bukkit.World; + +import static com.sk89q.worldguard.bukkit.BukkitUtil.isBlockWater; +import static com.sk89q.worldguard.bukkit.BukkitUtil.setBlockToWater; + +public final class SpongeUtil { + + private SpongeUtil() { + } + + /** + * Remove water around a sponge. + * + * @param plugin The plugin instace + * @param world The world the sponge isin + * @param ox The x coordinate of the 'sponge' block + * @param oy The y coordinate of the 'sponge' block + * @param oz The z coordinate of the 'sponge' block + */ + public static void clearSpongeWater(WorldGuardPlugin plugin, World world, int ox, int oy, int oz) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + for (int cx = -wcfg.spongeRadius; cx <= wcfg.spongeRadius; cx++) { + for (int cy = -wcfg.spongeRadius; cy <= wcfg.spongeRadius; cy++) { + for (int cz = -wcfg.spongeRadius; cz <= wcfg.spongeRadius; cz++) { + if (isBlockWater(world, ox + cx, oy + cy, oz + cz)) { + world.getBlockAt(ox + cx, oy + cy, oz + cz).setTypeId(0); + } + } + } + } + } + + /** + * Add water around a sponge. + * + * @param plugin The plugin instance + * @param world The world the sponge is located in + * @param ox The x coordinate of the 'sponge' block + * @param oy The y coordinate of the 'sponge' block + * @param oz The z coordinate of the 'sponge' block + */ + public static void addSpongeWater(WorldGuardPlugin plugin, World world, int ox, int oy, int oz) { + ConfigurationManager cfg = plugin.getGlobalStateManager(); + WorldConfiguration wcfg = cfg.get(world); + + // The negative x edge + int cx = ox - wcfg.spongeRadius - 1; + for (int cy = oy - wcfg.spongeRadius - 1; cy <= oy + wcfg.spongeRadius + 1; cy++) { + for (int cz = oz - wcfg.spongeRadius - 1; cz <= oz + wcfg.spongeRadius + 1; cz++) { + if (isBlockWater(world, cx, cy, cz)) { + setBlockToWater(world, cx + 1, cy, cz); + } + } + } + + // The positive x edge + cx = ox + wcfg.spongeRadius + 1; + for (int cy = oy - wcfg.spongeRadius - 1; cy <= oy + wcfg.spongeRadius + 1; cy++) { + for (int cz = oz - wcfg.spongeRadius - 1; cz <= oz + wcfg.spongeRadius + 1; cz++) { + if (isBlockWater(world, cx, cy, cz)) { + setBlockToWater(world, cx - 1, cy, cz); + } + } + } + + // The negative y edge + int cy = oy - wcfg.spongeRadius - 1; + for (cx = ox - wcfg.spongeRadius - 1; cx <= ox + wcfg.spongeRadius + 1; cx++) { + for (int cz = oz - wcfg.spongeRadius - 1; cz <= oz + wcfg.spongeRadius + 1; cz++) { + if (isBlockWater(world, cx, cy, cz)) { + setBlockToWater(world, cx, cy + 1, cz); + } + } + } + + // The positive y edge + cy = oy + wcfg.spongeRadius + 1; + for (cx = ox - wcfg.spongeRadius - 1; cx <= ox + wcfg.spongeRadius + 1; cx++) { + for (int cz = oz - wcfg.spongeRadius - 1; cz <= oz + wcfg.spongeRadius + 1; cz++) { + if (isBlockWater(world, cx, cy, cz)) { + setBlockToWater(world, cx, cy - 1, cz); + } + } + } + + // The negative z edge + int cz = oz - wcfg.spongeRadius - 1; + for (cx = ox - wcfg.spongeRadius - 1; cx <= ox + wcfg.spongeRadius + 1; cx++) { + for (cy = oy - wcfg.spongeRadius - 1; cy <= oy + wcfg.spongeRadius + 1; cy++) { + if (isBlockWater(world, cx, cy, cz)) { + setBlockToWater(world, cx, cy, cz + 1); + } + } + } + + // The positive z edge + cz = oz + wcfg.spongeRadius + 1; + for (cx = ox - wcfg.spongeRadius - 1; cx <= ox + wcfg.spongeRadius + 1; cx++) { + for (cy = oy - wcfg.spongeRadius - 1; cy <= oy + wcfg.spongeRadius + 1; cy++) { + if (isBlockWater(world, cx, cy, cz)) { + setBlockToWater(world, cx, cy, cz - 1); + } + } + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java index ef0c69cf..d29e8594 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java @@ -24,7 +24,6 @@ import com.sk89q.worldedit.blocks.BlockType; import com.sk89q.worldedit.blocks.ItemType; import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.SpongeUtil; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java index b052f72f..aa30e18a 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java @@ -24,7 +24,7 @@ import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.bukkit.BukkitUtil; import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.RegionQueryUtil; +import com.sk89q.worldguard.bukkit.util.RegionQueryUtil; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java index 6b4ce0eb..bdd08b3b 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java @@ -22,10 +22,9 @@ import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.blacklist.event.ItemUseBlacklistEvent; import com.sk89q.worldguard.bukkit.BukkitUtil; import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.FlagStateManager.PlayerFlagState; +import com.sk89q.worldguard.bukkit.listener.FlagStateManager.PlayerFlagState; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; @@ -50,7 +49,6 @@ import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerFishEvent; import org.bukkit.event.player.PlayerGameModeChangeEvent; -import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerJoinEvent; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/LoggerToChatHandler.java b/src/main/java/com/sk89q/worldguard/bukkit/util/LoggerToChatHandler.java similarity index 97% rename from src/main/java/com/sk89q/worldguard/bukkit/LoggerToChatHandler.java rename to src/main/java/com/sk89q/worldguard/bukkit/util/LoggerToChatHandler.java index 5bf5c60a..c735fc85 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/LoggerToChatHandler.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/LoggerToChatHandler.java @@ -17,14 +17,14 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.bukkit; - -import java.util.logging.Handler; -import java.util.logging.LogRecord; +package com.sk89q.worldguard.bukkit.util; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import java.util.logging.Handler; +import java.util.logging.LogRecord; + /** * Sends all logger messages to a player. * diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/ProtectedRegionQuery.java b/src/main/java/com/sk89q/worldguard/bukkit/util/ProtectedRegionQuery.java deleted file mode 100644 index 9d26992a..00000000 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/ProtectedRegionQuery.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit.util; - -import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.bukkit.BukkitUtil; -import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.WorldConfiguration; -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.GlobalRegionManager; -import com.sk89q.worldguard.protection.flags.StateFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import javax.annotation.Nullable; - -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - -public class ProtectedRegionQuery { - - private final ConfigurationManager config; - private final GlobalRegionManager globalManager; - @Nullable - private final LocalPlayer localPlayer; - - public ProtectedRegionQuery(WorldGuardPlugin plugin, @Nullable Player player) { - this(plugin, player != null ? plugin.wrapPlayer(player) : null); - } - - public ProtectedRegionQuery(WorldGuardPlugin plugin, @Nullable LocalPlayer player) { - this.config = plugin.getGlobalStateManager(); - this.globalManager = plugin.getGlobalRegionManager(); - this.localPlayer = player; - } - - public boolean canBuild(Location location) { - World world = location.getWorld(); - WorldConfiguration worldConfig = config.get(world); - - if (!worldConfig.useRegions) { - return true; - } - - if (localPlayer != null && globalManager.hasBypass(localPlayer, world)) { - return true; - } else { - RegionManager manager = globalManager.get(location.getWorld()); - return manager.getApplicableRegions(BukkitUtil.toVector(location)).canBuild(localPlayer); - } - } - - public boolean canConstruct(Location location) { - World world = location.getWorld(); - WorldConfiguration worldConfig = config.get(world); - - if (!worldConfig.useRegions) { - return true; - } - - if (localPlayer != null && globalManager.hasBypass(localPlayer, world)) { - return true; - } else { - RegionManager manager = globalManager.get(location.getWorld()); - ApplicableRegionSet result = manager.getApplicableRegions(BukkitUtil.toVector(location)); - return result.canBuild(localPlayer) && result.canConstruct(localPlayer); - } - } - - public boolean allows(StateFlag flag, Location location) { - World world = location.getWorld(); - WorldConfiguration worldConfig = config.get(world); - - if (!worldConfig.useRegions) { - return true; - } - - RegionManager manager = globalManager.get(location.getWorld()); - return manager.getApplicableRegions(toVector(location)).allows(flag, localPlayer); - } - -} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionQueryUtil.java b/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQueryUtil.java similarity index 95% rename from src/main/java/com/sk89q/worldguard/bukkit/RegionQueryUtil.java rename to src/main/java/com/sk89q/worldguard/bukkit/util/RegionQueryUtil.java index 5ddb3f3b..c60ea076 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionQueryUtil.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/RegionQueryUtil.java @@ -1,86 +1,88 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.entity.Player; - -import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.flags.StateFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; - -public final class RegionQueryUtil { - - private RegionQueryUtil() { - } - - public static boolean isInvincible(WorldGuardPlugin plugin, Player player) { - return isInvincible(plugin, player, null); - } - - public static boolean isInvincible(WorldGuardPlugin plugin, Player player, - ApplicableRegionSet set) { - Location loc = player.getLocation(); - World world = player.getWorld(); - - FlagStateManager.PlayerFlagState state = plugin.getFlagStateManager().getState(player); - - if (state.lastInvincibleWorld == null || - !state.lastInvincibleWorld.equals(world) || - state.lastInvincibleX != loc.getBlockX() || - state.lastInvincibleY != loc.getBlockY() || - state.lastInvincibleZ != loc.getBlockZ()) { - state.lastInvincibleX = loc.getBlockX(); - state.lastInvincibleY = loc.getBlockY(); - state.lastInvincibleZ = loc.getBlockZ(); - state.lastInvincibleWorld = world; - - if (set == null) { - Vector vec = new Vector(state.lastInvincibleX, - state.lastInvincibleY, state.lastInvincibleZ); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - set = mgr.getApplicableRegions(vec); - } - - state.wasInvincible = set.allows(DefaultFlag.INVINCIBILITY, plugin.wrapPlayer(player)); - } - - return state.wasInvincible; - } - - public static Boolean isAllowedInvinciblity(WorldGuardPlugin plugin, Player player) { - World world = player.getWorld(); - FlagStateManager.PlayerFlagState state = plugin.getFlagStateManager().getState(player); - Vector vec = new Vector(state.lastInvincibleX, state.lastInvincibleY, state.lastInvincibleZ); - - StateFlag.State regionState = plugin.getGlobalRegionManager().get(world). - getApplicableRegions(vec).getFlag(DefaultFlag.INVINCIBILITY, plugin.wrapPlayer(player)); - if (regionState == StateFlag.State.ALLOW) { - return Boolean.TRUE; - } else if (regionState == StateFlag.State.DENY) { - return Boolean.FALSE; - } else { - return null; - } - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.util; + +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.listener.FlagStateManager; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import com.sk89q.worldedit.Vector; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; + +public final class RegionQueryUtil { + + private RegionQueryUtil() { + } + + public static boolean isInvincible(WorldGuardPlugin plugin, Player player) { + return isInvincible(plugin, player, null); + } + + public static boolean isInvincible(WorldGuardPlugin plugin, Player player, + ApplicableRegionSet set) { + Location loc = player.getLocation(); + World world = player.getWorld(); + + FlagStateManager.PlayerFlagState state = plugin.getFlagStateManager().getState(player); + + if (state.lastInvincibleWorld == null || + !state.lastInvincibleWorld.equals(world) || + state.lastInvincibleX != loc.getBlockX() || + state.lastInvincibleY != loc.getBlockY() || + state.lastInvincibleZ != loc.getBlockZ()) { + state.lastInvincibleX = loc.getBlockX(); + state.lastInvincibleY = loc.getBlockY(); + state.lastInvincibleZ = loc.getBlockZ(); + state.lastInvincibleWorld = world; + + if (set == null) { + Vector vec = new Vector(state.lastInvincibleX, + state.lastInvincibleY, state.lastInvincibleZ); + RegionManager mgr = plugin.getGlobalRegionManager().get(world); + set = mgr.getApplicableRegions(vec); + } + + state.wasInvincible = set.allows(DefaultFlag.INVINCIBILITY, plugin.wrapPlayer(player)); + } + + return state.wasInvincible; + } + + public static Boolean isAllowedInvinciblity(WorldGuardPlugin plugin, Player player) { + World world = player.getWorld(); + FlagStateManager.PlayerFlagState state = plugin.getFlagStateManager().getState(player); + Vector vec = new Vector(state.lastInvincibleX, state.lastInvincibleY, state.lastInvincibleZ); + + StateFlag.State regionState = plugin.getGlobalRegionManager().get(world). + getApplicableRegions(vec).getFlag(DefaultFlag.INVINCIBILITY, plugin.wrapPlayer(player)); + if (regionState == StateFlag.State.ALLOW) { + return Boolean.TRUE; + } else if (regionState == StateFlag.State.DENY) { + return Boolean.FALSE; + } else { + return null; + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/ReportWriter.java b/src/main/java/com/sk89q/worldguard/bukkit/util/ReportWriter.java similarity index 97% rename from src/main/java/com/sk89q/worldguard/bukkit/ReportWriter.java rename to src/main/java/com/sk89q/worldguard/bukkit/util/ReportWriter.java index 82fa737b..54efe314 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/ReportWriter.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/ReportWriter.java @@ -1,343 +1,346 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.lang.reflect.Field; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.entity.Entity; -import org.bukkit.plugin.Plugin; - -import com.sk89q.worldguard.protection.GlobalRegionManager; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.flags.Flag; -import com.sk89q.worldguard.protection.flags.StateFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.util.LogListBlock; - -public class ReportWriter { - - private static final SimpleDateFormat dateFmt = - new SimpleDateFormat("yyyy-MM-dd kk:mm Z"); - - private Date date = new Date(); - private StringBuilder output = new StringBuilder(); - - public ReportWriter(WorldGuardPlugin plugin) { - appendReportHeader(plugin); - appendServerInformation(plugin.getServer()); - appendPluginInformation(plugin.getServer().getPluginManager().getPlugins()); - appendWorldInformation(plugin.getServer().getWorlds()); - appendGlobalConfiguration(plugin.getGlobalStateManager()); - appendWorldConfigurations(plugin, plugin.getServer().getWorlds(), - plugin.getGlobalRegionManager(), plugin.getGlobalStateManager()); - appendln("-------------"); - appendln("END OF REPORT"); - appendln(); - } - - protected static String repeat(String str, int n) { - if(str == null) { - return null; - } - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < n; i++) { - sb.append(str); - } - - return sb.toString(); - } - - protected void appendln(String text) { - output.append(text); - output.append("\r\n"); - } - - protected void appendln(String text, Object ... args) { - output.append(String.format(text, args)); - output.append("\r\n"); - } - - protected void append(LogListBlock log) { - output.append(log.toString()); - } - - protected void appendln() { - output.append("\r\n"); - } - - protected void appendHeader(String text) { - String rule = repeat("-", text.length()); - output.append(rule); - output.append("\r\n"); - appendln(text); - output.append(rule); - output.append("\r\n"); - appendln(); - } - - private void appendReportHeader(WorldGuardPlugin plugin) { - appendln("WorldGuard Configuration Report"); - appendln("Generated " + dateFmt.format(date)); - appendln(); - appendln("Version: " + plugin.getDescription().getVersion()); - appendln(); - } - - private void appendGlobalConfiguration(ConfigurationManager config) { - appendHeader("Global Configuration"); - - LogListBlock log = new LogListBlock(); - LogListBlock configLog = log.putChild("Configuration"); - - Class cls = config.getClass(); - for (Field field : cls.getFields()) { - try { - String name = field.getName(); - // store these elsewhere maybe? - if (name.equals("CONFIG_HEADER") - || name.equals("hostKeys") - || name.equals("sqlPassword")) { - continue; - } - Object val = field.get(config); - configLog.put(name, val); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (IllegalAccessException ignore) { - } - } - - append(log); - appendln(); - } - - private void appendServerInformation(Server server) { - appendHeader("Server Information"); - - LogListBlock log = new LogListBlock(); - - Runtime runtime = Runtime.getRuntime(); - - log.put("Java", "%s %s (%s)", - System.getProperty("java.vendor"), - System.getProperty("java.version"), - System.getProperty("java.vendor.url")); - log.put("Operating system", "%s %s (%s)", - System.getProperty("os.name"), - System.getProperty("os.version"), - System.getProperty("os.arch")); - log.put("Available processors", runtime.availableProcessors()); - log.put("Free memory", runtime.freeMemory() / 1024 / 1024 + " MB"); - log.put("Max memory", runtime.maxMemory() / 1024 / 1024 + " MB"); - log.put("Total memory", runtime.totalMemory() / 1024 / 1024 + " MB"); - log.put("Server ID", server.getServerId()); - log.put("Server name", server.getServerName()); - log.put("Implementation", server.getVersion()); - //log.put("Address", server.getIp(), server.getPort()); - log.put("Player count", "%d/%d", - server.getOnlinePlayers().length, server.getMaxPlayers()); - - append(log); - appendln(); - } - - private void appendPluginInformation(Plugin[] plugins) { - appendHeader("Plugins (" + plugins.length + ")"); - - LogListBlock log = new LogListBlock(); - - for (Plugin plugin : plugins) { - log.put(plugin.getDescription().getName(), plugin.getDescription().getVersion()); - } - - append(log); - appendln(); - - /*appendHeader("Plugin Information"); - - log = new LogListBlock(); - - for (Plugin plugin : plugins) { - log.putChild(plugin.getDescription().getName()) - .put("Data folder", plugin.getDataFolder()) - .put("Website", plugin.getDescription().getWebsite()) - .put("Entry point", plugin.getDescription().getMain()); - } - - append(log); - appendln();*/ - } - - private void appendWorldInformation(List worlds) { - appendHeader("Worlds"); - - LogListBlock log = new LogListBlock(); - - int i = 0; - for (World world : worlds) { - int loadedChunkCount = world.getLoadedChunks().length; - - LogListBlock worldLog = log.putChild(world.getName() + " (" + i + ")"); - LogListBlock infoLog = worldLog.putChild("Information"); - LogListBlock entitiesLog = worldLog.putChild("Entities"); - - infoLog.put("Seed", world.getSeed()); - infoLog.put("Environment", world.getEnvironment().toString()); - infoLog.put("Player count", world.getPlayers().size()); - infoLog.put("Entity count", world.getEntities().size()); - infoLog.put("Loaded chunk count", loadedChunkCount); - infoLog.put("Spawn location", world.getSpawnLocation()); - infoLog.put("Raw time", world.getFullTime()); - - Map, Integer> entityCounts = - new HashMap, Integer>(); - - // Collect entities - for (Entity entity : world.getEntities()) { - Class cls = entity.getClass(); - - if (entityCounts.containsKey(cls)) { - entityCounts.put(cls, entityCounts.get(cls) + 1); - } else { - entityCounts.put(cls, 1); - } - } - - // Print entities - for (Map.Entry, Integer> entry - : entityCounts.entrySet()) { - entitiesLog.put(entry.getKey().getSimpleName(), - "%d [%f/chunk]", - entry.getValue(), - (float) (entry.getValue() / (double) loadedChunkCount)); - } - - i++; - } - - append(log); - appendln(); - } - - private void appendWorldConfigurations(WorldGuardPlugin plugin, List worlds, - GlobalRegionManager regionMgr, ConfigurationManager mgr) { - appendHeader("World Configurations"); - - LogListBlock log = new LogListBlock(); - - int i = 0; - for (World world : worlds) { - LogListBlock worldLog = log.putChild(world.getName() + " (" + i + ")"); - LogListBlock infoLog = worldLog.putChild("Information"); - LogListBlock configLog = worldLog.putChild("Configuration"); - LogListBlock blacklistLog = worldLog.putChild("Blacklist"); - LogListBlock regionsLog = worldLog.putChild("Region manager"); - - infoLog.put("Configuration file", (new File(plugin.getDataFolder(), "worlds/" - + world.getName() + "/config.yml")).getAbsoluteFile()); - - infoLog.put("Blacklist file", (new File(plugin.getDataFolder(), "worlds/" - + world.getName() + "/blacklist.txt")).getAbsoluteFile()); - infoLog.put("Regions file", (new File(plugin.getDataFolder(), "worlds/" - + world.getName() + "/regions.yml")).getAbsoluteFile()); - - WorldConfiguration config = mgr.get(world); - - Class cls = config.getClass(); - for (Field field : cls.getFields()) { - try { - Object val = field.get(config); - configLog.put(field.getName(), String.valueOf(val)); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (IllegalAccessException ignore) { - } - } - - if (config.getBlacklist() == null) { - blacklistLog.put("State", "DISABLED"); - } else { - blacklistLog.put("State", "Enabled"); - blacklistLog.put("Number of items", - config.getBlacklist().getItemCount()); - blacklistLog.put("Is whitelist", - config.getBlacklist().isWhitelist()); - } - - RegionManager worldRegions = regionMgr.get(world); - - regionsLog.put("Type", worldRegions.getClass().getCanonicalName()); - regionsLog.put("Number of regions", worldRegions.getRegions().size()); - LogListBlock globalRegionLog = regionsLog.putChild("Global region"); - - ProtectedRegion globalRegion = worldRegions.matchRegion("__global__"); - if (globalRegion == null) { - globalRegionLog.put("Status", "UNDEFINED"); - } else { - for (Flag flag : DefaultFlag.getFlags()) { - if (flag instanceof StateFlag) { - globalRegionLog.put(flag.getName(), - globalRegion.getFlag(flag)); - } - } - } - } - - append(log); - appendln(); - } - - public void write(File file) throws IOException { - FileWriter writer = null; - BufferedWriter out; - - try { - writer = new FileWriter(file); - out = new BufferedWriter(writer); - out.write(output.toString()); - out.close(); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException ignore) { - } - } - } - } - - @Override - public String toString() { - return output.toString(); - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.util; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; + +import com.sk89q.worldguard.protection.GlobalRegionManager; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.util.LogListBlock; + +public class ReportWriter { + + private static final SimpleDateFormat dateFmt = + new SimpleDateFormat("yyyy-MM-dd kk:mm Z"); + + private Date date = new Date(); + private StringBuilder output = new StringBuilder(); + + public ReportWriter(WorldGuardPlugin plugin) { + appendReportHeader(plugin); + appendServerInformation(plugin.getServer()); + appendPluginInformation(plugin.getServer().getPluginManager().getPlugins()); + appendWorldInformation(plugin.getServer().getWorlds()); + appendGlobalConfiguration(plugin.getGlobalStateManager()); + appendWorldConfigurations(plugin, plugin.getServer().getWorlds(), + plugin.getGlobalRegionManager(), plugin.getGlobalStateManager()); + appendln("-------------"); + appendln("END OF REPORT"); + appendln(); + } + + protected static String repeat(String str, int n) { + if(str == null) { + return null; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < n; i++) { + sb.append(str); + } + + return sb.toString(); + } + + protected void appendln(String text) { + output.append(text); + output.append("\r\n"); + } + + protected void appendln(String text, Object ... args) { + output.append(String.format(text, args)); + output.append("\r\n"); + } + + protected void append(LogListBlock log) { + output.append(log.toString()); + } + + protected void appendln() { + output.append("\r\n"); + } + + protected void appendHeader(String text) { + String rule = repeat("-", text.length()); + output.append(rule); + output.append("\r\n"); + appendln(text); + output.append(rule); + output.append("\r\n"); + appendln(); + } + + private void appendReportHeader(WorldGuardPlugin plugin) { + appendln("WorldGuard Configuration Report"); + appendln("Generated " + dateFmt.format(date)); + appendln(); + appendln("Version: " + plugin.getDescription().getVersion()); + appendln(); + } + + private void appendGlobalConfiguration(ConfigurationManager config) { + appendHeader("Global Configuration"); + + LogListBlock log = new LogListBlock(); + LogListBlock configLog = log.putChild("Configuration"); + + Class cls = config.getClass(); + for (Field field : cls.getFields()) { + try { + String name = field.getName(); + // store these elsewhere maybe? + if (name.equals("CONFIG_HEADER") + || name.equals("hostKeys") + || name.equals("sqlPassword")) { + continue; + } + Object val = field.get(config); + configLog.put(name, val); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException ignore) { + } + } + + append(log); + appendln(); + } + + private void appendServerInformation(Server server) { + appendHeader("Server Information"); + + LogListBlock log = new LogListBlock(); + + Runtime runtime = Runtime.getRuntime(); + + log.put("Java", "%s %s (%s)", + System.getProperty("java.vendor"), + System.getProperty("java.version"), + System.getProperty("java.vendor.url")); + log.put("Operating system", "%s %s (%s)", + System.getProperty("os.name"), + System.getProperty("os.version"), + System.getProperty("os.arch")); + log.put("Available processors", runtime.availableProcessors()); + log.put("Free memory", runtime.freeMemory() / 1024 / 1024 + " MB"); + log.put("Max memory", runtime.maxMemory() / 1024 / 1024 + " MB"); + log.put("Total memory", runtime.totalMemory() / 1024 / 1024 + " MB"); + log.put("Server ID", server.getServerId()); + log.put("Server name", server.getServerName()); + log.put("Implementation", server.getVersion()); + //log.put("Address", server.getIp(), server.getPort()); + log.put("Player count", "%d/%d", + server.getOnlinePlayers().length, server.getMaxPlayers()); + + append(log); + appendln(); + } + + private void appendPluginInformation(Plugin[] plugins) { + appendHeader("Plugins (" + plugins.length + ")"); + + LogListBlock log = new LogListBlock(); + + for (Plugin plugin : plugins) { + log.put(plugin.getDescription().getName(), plugin.getDescription().getVersion()); + } + + append(log); + appendln(); + + /*appendHeader("Plugin Information"); + + log = new LogListBlock(); + + for (Plugin plugin : plugins) { + log.putChild(plugin.getDescription().getName()) + .put("Data folder", plugin.getDataFolder()) + .put("Website", plugin.getDescription().getWebsite()) + .put("Entry point", plugin.getDescription().getMain()); + } + + append(log); + appendln();*/ + } + + private void appendWorldInformation(List worlds) { + appendHeader("Worlds"); + + LogListBlock log = new LogListBlock(); + + int i = 0; + for (World world : worlds) { + int loadedChunkCount = world.getLoadedChunks().length; + + LogListBlock worldLog = log.putChild(world.getName() + " (" + i + ")"); + LogListBlock infoLog = worldLog.putChild("Information"); + LogListBlock entitiesLog = worldLog.putChild("Entities"); + + infoLog.put("Seed", world.getSeed()); + infoLog.put("Environment", world.getEnvironment().toString()); + infoLog.put("Player count", world.getPlayers().size()); + infoLog.put("Entity count", world.getEntities().size()); + infoLog.put("Loaded chunk count", loadedChunkCount); + infoLog.put("Spawn location", world.getSpawnLocation()); + infoLog.put("Raw time", world.getFullTime()); + + Map, Integer> entityCounts = + new HashMap, Integer>(); + + // Collect entities + for (Entity entity : world.getEntities()) { + Class cls = entity.getClass(); + + if (entityCounts.containsKey(cls)) { + entityCounts.put(cls, entityCounts.get(cls) + 1); + } else { + entityCounts.put(cls, 1); + } + } + + // Print entities + for (Map.Entry, Integer> entry + : entityCounts.entrySet()) { + entitiesLog.put(entry.getKey().getSimpleName(), + "%d [%f/chunk]", + entry.getValue(), + (float) (entry.getValue() / (double) loadedChunkCount)); + } + + i++; + } + + append(log); + appendln(); + } + + private void appendWorldConfigurations(WorldGuardPlugin plugin, List worlds, + GlobalRegionManager regionMgr, ConfigurationManager mgr) { + appendHeader("World Configurations"); + + LogListBlock log = new LogListBlock(); + + int i = 0; + for (World world : worlds) { + LogListBlock worldLog = log.putChild(world.getName() + " (" + i + ")"); + LogListBlock infoLog = worldLog.putChild("Information"); + LogListBlock configLog = worldLog.putChild("Configuration"); + LogListBlock blacklistLog = worldLog.putChild("Blacklist"); + LogListBlock regionsLog = worldLog.putChild("Region manager"); + + infoLog.put("Configuration file", (new File(plugin.getDataFolder(), "worlds/" + + world.getName() + "/config.yml")).getAbsoluteFile()); + + infoLog.put("Blacklist file", (new File(plugin.getDataFolder(), "worlds/" + + world.getName() + "/blacklist.txt")).getAbsoluteFile()); + infoLog.put("Regions file", (new File(plugin.getDataFolder(), "worlds/" + + world.getName() + "/regions.yml")).getAbsoluteFile()); + + WorldConfiguration config = mgr.get(world); + + Class cls = config.getClass(); + for (Field field : cls.getFields()) { + try { + Object val = field.get(config); + configLog.put(field.getName(), String.valueOf(val)); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException ignore) { + } + } + + if (config.getBlacklist() == null) { + blacklistLog.put("State", "DISABLED"); + } else { + blacklistLog.put("State", "Enabled"); + blacklistLog.put("Number of items", + config.getBlacklist().getItemCount()); + blacklistLog.put("Is whitelist", + config.getBlacklist().isWhitelist()); + } + + RegionManager worldRegions = regionMgr.get(world); + + regionsLog.put("Type", worldRegions.getClass().getCanonicalName()); + regionsLog.put("Number of regions", worldRegions.getRegions().size()); + LogListBlock globalRegionLog = regionsLog.putChild("Global region"); + + ProtectedRegion globalRegion = worldRegions.matchRegion("__global__"); + if (globalRegion == null) { + globalRegionLog.put("Status", "UNDEFINED"); + } else { + for (Flag flag : DefaultFlag.getFlags()) { + if (flag instanceof StateFlag) { + globalRegionLog.put(flag.getName(), + globalRegion.getFlag(flag)); + } + } + } + } + + append(log); + appendln(); + } + + public void write(File file) throws IOException { + FileWriter writer = null; + BufferedWriter out; + + try { + writer = new FileWriter(file); + out = new BufferedWriter(writer); + out.write(output.toString()); + out.close(); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException ignore) { + } + } + } + } + + @Override + public String toString() { + return output.toString(); + } +} diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index 651d6f9e..62c206a3 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -19,111 +19,45 @@ package com.sk89q.worldguard.protection; -import com.sk89q.worldedit.Vector2D; import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.bukkit.ConfigurationManager; +import com.sk89q.worldguard.bukkit.RegionContainer; +import com.sk89q.worldguard.bukkit.RegionQuery; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.bukkit.util.ProtectedRegionQuery; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.RegionManager; -import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; /** - * A global region manager loads, saves and caches region data for zero or - * more worlds at a time. + * This is the legacy class for accessing region data. * - *

This class is thread safe and its contents can be accessed from - * multiple concurrent threads.

+ * @deprecated use {@link WorldGuardPlugin#getRegionContainer()} */ +@Deprecated public class GlobalRegionManager { private final WorldGuardPlugin plugin; - private final ManagerContainer container; + private final RegionContainer container; /** * Create a new instance. * * @param plugin the plugin + * @param container the container */ - public GlobalRegionManager(WorldGuardPlugin plugin) { + public GlobalRegionManager(WorldGuardPlugin plugin, RegionContainer container) { + checkNotNull(plugin); + checkNotNull(container); this.plugin = plugin; - - ConfigurationManager config = plugin.getGlobalStateManager(); - container = new ManagerContainer(config); - } - - /** - * Load the region data for a world if it has not been loaded already. - * - *

This method is called by WorldGuard and should not be called - * by other plugins.

- * - * @param world the world - * @return a region manager, either returned from the cache or newly loaded - */ - @Nullable - public RegionManager load(World world) { - checkNotNull(world); - - RegionManager manager = container.load(world.getName()); - - if (manager != null) { - // Bias the region data for loaded chunks - List positions = new ArrayList(); - for (Chunk chunk : world.getLoadedChunks()) { - positions.add(new Vector2D(chunk.getX(), chunk.getZ())); - } - manager.loadChunks(positions); - } - - return manager; - } - - /** - * Load the region data for a list of worlds. - * - *

This method is called by WorldGuard and should not be called - * by other plugins.

- * - * @param worlds a list of worlds - */ - public void loadAll(Collection worlds) { - checkNotNull(worlds); - - for (World world : worlds) { - load(world); - } - } - - /** - * Unload the region data for a world. - * - * @param world a world - */ - public void unload(World world) { - checkNotNull(world); - - container.unload(world.getName()); - } - - /** - * Unload all region data for all worlds that region data has - * been loaded for. - */ - public void unloadAll() { - container.unloadAll(); + this.container = container; } /** @@ -138,7 +72,7 @@ public void unloadAll() { */ @Nullable public RegionManager get(World world) { - return container.get(world.getName()); + return container.get(world); } /** @@ -155,8 +89,8 @@ public List getLoaded() { * * @return a new query */ - public ProtectedRegionQuery createAnonymousQuery() { - return new ProtectedRegionQuery(plugin, (Player) null); + private RegionQuery createAnonymousQuery() { + return container.createAnonymousQuery(); } /** @@ -165,8 +99,8 @@ public ProtectedRegionQuery createAnonymousQuery() { * @param player a player, or {@code null} * @return a new query */ - public ProtectedRegionQuery createQuery(@Nullable Player player) { - return new ProtectedRegionQuery(plugin, player); + private RegionQuery createQuery(@Nullable Player player) { + return container.createQuery(player); } /** @@ -175,8 +109,8 @@ public ProtectedRegionQuery createQuery(@Nullable Player player) { * @param player a player, or {@code null} * @return a new query */ - private ProtectedRegionQuery createQuery(@Nullable LocalPlayer player) { - return new ProtectedRegionQuery(plugin, player); + private RegionQuery createQuery(@Nullable LocalPlayer player) { + return container.createQuery(player); } /** @@ -243,7 +177,7 @@ public boolean canBuild(Player player, Block block) { */ @Deprecated public boolean canBuild(Player player, Location location) { - return createQuery(player).canBuild(location); + return createQuery(player).testPermission(location); } /** @@ -252,12 +186,12 @@ public boolean canBuild(Player player, Location location) { * @param player the player * @param block the block * @return true if permitted - * @deprecated use {@link #createQuery(Player)} + * @deprecated the construct flag is being removed */ @Deprecated @SuppressWarnings("deprecation") public boolean canConstruct(Player player, Block block) { - return canConstruct(player, block.getLocation()); + return canBuild(player, block.getLocation()); } /** @@ -266,11 +200,11 @@ public boolean canConstruct(Player player, Block block) { * @param player the player * @param location the location * @return true if permitted - * @deprecated use {@link #createQuery(Player)} + * @deprecated the construct flag is being removed */ @Deprecated public boolean canConstruct(Player player, Location location) { - return createQuery(player).canConstruct(location); + return canBuild(player, location); } /** @@ -299,7 +233,7 @@ public boolean allows(StateFlag flag, Location location) { */ @Deprecated public boolean allows(StateFlag flag, Location location, @Nullable LocalPlayer player) { - return createQuery(player).allows(flag, location); + return createQuery(player).testEnabled(location, flag); } } From dc2652f87bda45ff8919ccc1f3a4dc76a6a9a679 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 02:33:57 -0700 Subject: [PATCH 034/133] Implement an ApplicableRegionSet cache. --- .../sk89q/worldguard/bukkit/QueryCache.java | 112 ++++++++++++++++++ .../worldguard/bukkit/RegionContainer.java | 19 ++- .../sk89q/worldguard/bukkit/RegionQuery.java | 19 +-- 3 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/QueryCache.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/QueryCache.java b/src/main/java/com/sk89q/worldguard/bukkit/QueryCache.java new file mode 100644 index 00000000..2f969deb --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/QueryCache.java @@ -0,0 +1,112 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit; + +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.managers.RegionManager; +import org.bukkit.Location; +import org.bukkit.World; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Keeps a cache of {@link ApplicableRegionSet}s. The contents of the cache + * must be externally invalidated occasionally (and frequently). + * + *

This class is fully concurrent.

+ */ +class QueryCache { + + private final ConcurrentMap cache = new ConcurrentHashMap(); + + /** + * Get from the cache a {@code ApplicableRegionSet} if an entry exists; + * otherwise, query the given manager for a result and cache it. + * + * @param manager the region manager + * @param location the location + * @return a result + */ + public ApplicableRegionSet queryContains(RegionManager manager, Location location) { + checkNotNull(manager); + checkNotNull(location); + + CacheKey key = new CacheKey(location); + ApplicableRegionSet result = cache.get(key); + if (result == null) { + result = manager.getApplicableRegions(location); + cache.put(key, result); + } + + return result; + } + + /** + * Invalidate the cache and clear its contents. + */ + public void invalidateAll() { + cache.clear(); + } + + /** + * Key object for the map. + */ + private static class CacheKey { + private final World world; + private final int x; + private final int y; + private final int z; + + private CacheKey(Location location) { + this.world = location.getWorld(); + this.x = location.getBlockX(); + this.y = location.getBlockY(); + this.z = location.getBlockZ(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CacheKey cacheKey = (CacheKey) o; + + if (x != cacheKey.x) return false; + if (y != cacheKey.y) return false; + if (z != cacheKey.z) return false; + if (!world.equals(cacheKey.world)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = world.hashCode(); + result = 31 * result + x; + result = 31 * result + y; + result = 31 * result + z; + return result; + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java index bd8f1348..42fc522e 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java @@ -53,9 +53,15 @@ */ public class RegionContainer { + /** + * Invalidation frequency in ticks. + */ + private static final int CACHE_INVALIDATION_INTERVAL = 2; + private final Object lock = new Object(); private final WorldGuardPlugin plugin; private final ManagerContainer container; + private final QueryCache cache = new QueryCache(); /** * Create a new instance. @@ -106,6 +112,13 @@ public void onChunkUnload(ChunkUnloadEvent event) { } } }, plugin); + + Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() { + @Override + public void run() { + cache.invalidateAll(); + } + }, CACHE_INVALIDATION_INTERVAL, CACHE_INVALIDATION_INTERVAL); } /** @@ -216,7 +229,7 @@ public List getLoaded() { * @return a new query */ public RegionQuery createAnonymousQuery() { - return new RegionQuery(plugin, (Player) null); + return new RegionQuery(plugin, cache, (Player) null); } /** @@ -226,7 +239,7 @@ public RegionQuery createAnonymousQuery() { * @return a new query */ public RegionQuery createQuery(@Nullable Player player) { - return new RegionQuery(plugin, player); + return new RegionQuery(plugin, cache, player); } /** @@ -236,7 +249,7 @@ public RegionQuery createQuery(@Nullable Player player) { * @return a new query */ public RegionQuery createQuery(@Nullable LocalPlayer player) { - return new RegionQuery(plugin, player); + return new RegionQuery(plugin, cache, player); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java index 56f18664..967397b5 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java @@ -42,6 +42,7 @@ public class RegionQuery { private final ConfigurationManager config; private final GlobalRegionManager globalManager; + private final QueryCache cache; @Nullable private final LocalPlayer localPlayer; @@ -49,22 +50,26 @@ public class RegionQuery { * Create a new instance. * * @param plugin the plugin + * @param cache the query cache * @param player an optional player */ - RegionQuery(WorldGuardPlugin plugin, @Nullable Player player) { - this(plugin, player != null ? plugin.wrapPlayer(player) : null); + RegionQuery(WorldGuardPlugin plugin, QueryCache cache, @Nullable Player player) { + this(plugin, cache, player != null ? plugin.wrapPlayer(player) : null); } /** * Create a new instance. * * @param plugin the plugin + * @param cache the query cache * @param player an optional player */ - RegionQuery(WorldGuardPlugin plugin, @Nullable LocalPlayer player) { + RegionQuery(WorldGuardPlugin plugin, QueryCache cache, @Nullable LocalPlayer player) { checkNotNull(plugin); + checkNotNull(cache); this.config = plugin.getGlobalStateManager(); + this.cache = cache; //noinspection deprecation this.globalManager = plugin.getGlobalRegionManager(); this.localPlayer = player; @@ -102,7 +107,7 @@ public boolean testPermission(Location location) { return true; } else { RegionManager manager = globalManager.get(location.getWorld()); - return manager == null || manager.getApplicableRegions(BukkitUtil.toVector(location)).canBuild(localPlayer); + return manager == null || cache.queryContains(manager, location).canBuild(localPlayer); } } @@ -141,7 +146,7 @@ public boolean testPermission(Location location, StateFlag... flags) { RegionManager manager = globalManager.get(location.getWorld()); if (manager != null) { - ApplicableRegionSet result = manager.getApplicableRegions(BukkitUtil.toVector(location)); + ApplicableRegionSet result = cache.queryContains(manager, location); if (result.canBuild(localPlayer)) { return true; @@ -185,11 +190,11 @@ public boolean testEnabled(Location location, StateFlag flag) { return true; } - if (globalManager.hasBypass(localPlayer, world)) { + if (localPlayer != null && globalManager.hasBypass(localPlayer, world)) { return true; } else { RegionManager manager = globalManager.get(location.getWorld()); - return manager == null || manager.getApplicableRegions(BukkitUtil.toVector(location)).allows(flag, localPlayer); + return manager == null || cache.queryContains(manager, location).allows(flag, localPlayer); } } From 19b87b628a610727429f1685062286bfa094ed16 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 02:39:12 -0700 Subject: [PATCH 035/133] Remove use of GlobalRegionManager in RegionProtectionListener. --- .../bukkit/listener/RegionProtectionListener.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java index 2eec00ed..fae511ba 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java @@ -87,10 +87,10 @@ public void onBreakBlock(BreakBlockEvent event) { Location target = event.getTarget(); if (player != null) { - if (!getPlugin().getGlobalRegionManager().canBuild(player, target)) { - tellErrorMessage(player, target); - event.setCancelled(true); - } else if (!getPlugin().getGlobalRegionManager().canConstruct(player, target)) { + RegionQuery query = getPlugin().getRegionContainer().createQuery(player); + boolean canBreak = query.testPermission(target); + + if (!canBreak) { tellErrorMessage(player, target); event.setCancelled(true); } From 98bbd900cb8643e0ba95f7a3c4ec529c08ccda60 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 02:39:42 -0700 Subject: [PATCH 036/133] Rename RegionQuery.testEnabled() -> testState(). --- src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java | 2 +- .../com/sk89q/worldguard/protection/GlobalRegionManager.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java index 967397b5..dda830d6 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java @@ -179,7 +179,7 @@ public boolean testPermission(Location location, StateFlag... flags) { * @param flag the flag * @return true if the flag evaluates to {@code ALLOW} */ - public boolean testEnabled(Location location, StateFlag flag) { + public boolean testState(Location location, StateFlag flag) { checkNotNull(location); checkNotNull(flag); diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index 62c206a3..e355601c 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -233,7 +233,7 @@ public boolean allows(StateFlag flag, Location location) { */ @Deprecated public boolean allows(StateFlag flag, Location location, @Nullable LocalPlayer player) { - return createQuery(player).testEnabled(location, flag); + return createQuery(player).testState(location, flag); } } From e24d7ccb1035016ae1a45707b8f6a4fada9a4576 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 02:44:42 -0700 Subject: [PATCH 037/133] Change "don't have permission" message for regions. --- .../worldguard/bukkit/listener/RegionProtectionListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java index fae511ba..9fd7b870 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java @@ -53,7 +53,7 @@ public RegionProtectionListener(WorldGuardPlugin plugin) { } private void tellErrorMessage(CommandSender sender, Object subject) { - sender.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area."); + sender.sendMessage(ChatColor.DARK_RED + "Sorry, but you are not allowed to do that here."); } @EventHandler(ignoreCancelled = true) From 709ce9eb08e32ee47d1029dcd6dd67fdfe3ae111 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 02:46:19 -0700 Subject: [PATCH 038/133] Fix a typo in some Javadocs. --- .../com/sk89q/worldguard/protection/GlobalRegionManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index e355601c..7a7c5a4d 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -68,7 +68,7 @@ public GlobalRegionManager(WorldGuardPlugin plugin, RegionContainer container) { * has been disabled.

* * @param world the world - * @return a region manager, or {@code null} if one is not availale + * @return a region manager, or {@code null} if one is not available */ @Nullable public RegionManager get(World world) { From cd62af698bb4457ace75801732153ff481487407 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 14:52:11 -0700 Subject: [PATCH 039/133] Change listeners to use the region query cache. --- .../sk89q/worldguard/bukkit/BukkitPlayer.java | 4 + .../worldguard/bukkit/RegionContainer.java | 24 +----- .../sk89q/worldguard/bukkit/RegionQuery.java | 79 ++++++++++++------- .../bukkit/listener/FlagStateManager.java | 10 +-- .../listener/RegionProtectionListener.java | 36 ++++----- .../listener/WorldGuardBlockListener.java | 13 +-- .../WorldGuardCommandBookListener.java | 3 +- .../listener/WorldGuardEntityListener.java | 71 ++++++----------- .../listener/WorldGuardPlayerListener.java | 59 +++----------- .../listener/WorldGuardWeatherListener.java | 12 +-- .../protection/ApplicableRegionSet.java | 13 +++ .../protection/GlobalRegionManager.java | 46 ++++------- 12 files changed, 145 insertions(+), 225 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java b/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java index 8bcd56f5..416317b6 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/BukkitPlayer.java @@ -89,4 +89,8 @@ public boolean hasPermission(String perm) { return plugin.hasPermission(player, perm); } + public Player getPlayer() { + return player; + } + } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java index 42fc522e..2c1b2c1d 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java @@ -223,33 +223,13 @@ public List getLoaded() { return Collections.unmodifiableList(container.getLoaded()); } - /** - * Create a new region query with no player. - * - * @return a new query - */ - public RegionQuery createAnonymousQuery() { - return new RegionQuery(plugin, cache, (Player) null); - } - /** * Create a new region query. * - * @param player a player, or {@code null} * @return a new query */ - public RegionQuery createQuery(@Nullable Player player) { - return new RegionQuery(plugin, cache, player); - } - - /** - * Create a new region query. - * - * @param player a player, or {@code null} - * @return a new query - */ - public RegionQuery createQuery(@Nullable LocalPlayer player) { - return new RegionQuery(plugin, cache, player); + public RegionQuery createQuery() { + return new RegionQuery(plugin, cache); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java index dda830d6..72d06bd4 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java @@ -40,45 +40,61 @@ */ public class RegionQuery { + private final WorldGuardPlugin plugin; private final ConfigurationManager config; private final GlobalRegionManager globalManager; private final QueryCache cache; - @Nullable - private final LocalPlayer localPlayer; /** * Create a new instance. * * @param plugin the plugin * @param cache the query cache - * @param player an optional player */ - RegionQuery(WorldGuardPlugin plugin, QueryCache cache, @Nullable Player player) { - this(plugin, cache, player != null ? plugin.wrapPlayer(player) : null); - } - - /** - * Create a new instance. - * - * @param plugin the plugin - * @param cache the query cache - * @param player an optional player - */ - RegionQuery(WorldGuardPlugin plugin, QueryCache cache, @Nullable LocalPlayer player) { + RegionQuery(WorldGuardPlugin plugin, QueryCache cache) { checkNotNull(plugin); checkNotNull(cache); + this.plugin = plugin; this.config = plugin.getGlobalStateManager(); this.cache = cache; //noinspection deprecation this.globalManager = plugin.getGlobalRegionManager(); - this.localPlayer = player; } /** - * Test whether the player (which must not be {@code null} can build at - * the given location, using only the membership information and the state - * of the {@link DefaultFlag#BUILD} flag to determine status. + * Query for regions containing the given location. + * + *

An instance of {@link ApplicableRegionSet} will always be returned, + * even if regions are disabled or region data failed to load. The most + * appropriate implementation will be returned in such a case + * (for example, if regions are disable, the returned implementation + * would permit all activities).

+ * + * @param location the location + * @return a region set + */ + public ApplicableRegionSet queryContains(Location location) { + checkNotNull(location); + + World world = location.getWorld(); + WorldConfiguration worldConfig = config.get(world); + + if (!worldConfig.useRegions) { + return ApplicableRegionSet.getEmpty(); + } + + RegionManager manager = globalManager.get(location.getWorld()); + if (manager != null) { + return cache.queryContains(manager, location); + } else { + return ApplicableRegionSet.getEmpty(); + } + } + + /** + * Test a the player can build at the given location, checking membership + * information and the state of the {@link DefaultFlag#BUILD} flag. * *

This method is used to check blocks and entities for which there * are no other related flags for (i.e. beds have the @@ -89,13 +105,15 @@ public class RegionQuery { * depending on the configuration.

* * @param location the location + * @param player the player * @return true if building is permitted * @throws NullPointerException if there is no player for this query */ - public boolean testPermission(Location location) { + public boolean testPermission(Location location, Player player) { checkNotNull(location); - checkNotNull(localPlayer, "testPermission() requires a player for the query"); - + checkNotNull(player); + + LocalPlayer localPlayer = plugin.wrapPlayer(player); World world = location.getWorld(); WorldConfiguration worldConfig = config.get(world); @@ -112,10 +130,9 @@ public boolean testPermission(Location location) { } /** - * Test whether the player (which must not be {@code null} can build at - * the given location, using the membership information, state - * of the {@link DefaultFlag#BUILD} flag, and the state of any passed - * flags. + * Test a the player can build at the given location, checking membership + * information, state of the {@link DefaultFlag#BUILD} flag, and the state + * of any passed flags. * *

This method is used to check blocks and entities for which there * are other related flags for (i.e. beds have the @@ -128,14 +145,16 @@ public boolean testPermission(Location location) { * depending on the configuration.

* * @param location the location to test + * @param player the player * @param flags an array of flags * @return true if the flag tests true */ - public boolean testPermission(Location location, StateFlag... flags) { + public boolean testPermission(Location location, Player player, StateFlag... flags) { checkNotNull(location); checkNotNull(flags); - checkNotNull(localPlayer, "testPermission() requires a player for the query"); + checkNotNull(player); + LocalPlayer localPlayer = plugin.wrapPlayer(player); World world = location.getWorld(); WorldConfiguration worldConfig = config.get(world); @@ -176,13 +195,15 @@ public boolean testPermission(Location location, StateFlag... flags) { * depending on the configuration.

* * @param location the location + * @param player the player (or null) * @param flag the flag * @return true if the flag evaluates to {@code ALLOW} */ - public boolean testState(Location location, StateFlag flag) { + public boolean testState(Location location, @Nullable Player player, StateFlag flag) { checkNotNull(location); checkNotNull(flag); + LocalPlayer localPlayer = player != null ? plugin.wrapPlayer(player) : null; World world = location.getWorld(); WorldConfiguration worldConfig = config.get(world); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/FlagStateManager.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/FlagStateManager.java index af6365c6..b8a7fbdb 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/FlagStateManager.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/FlagStateManager.java @@ -19,14 +19,12 @@ package com.sk89q.worldguard.bukkit.listener; -import com.sk89q.worldedit.Vector; import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.util.RegionQueryUtil; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.util.RegionQueryUtil; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.GameMode; import org.bukkit.World; import org.bukkit.entity.Player; @@ -34,8 +32,6 @@ import java.util.HashMap; import java.util.Map; -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - /** * This processes per-player state information and is also meant to be used * as a scheduled task. @@ -86,9 +82,7 @@ public void run() { } } - Vector playerLocation = toVector(player.getLocation()); - RegionManager regionManager = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet applicable = regionManager.getApplicableRegions(playerLocation); + ApplicableRegionSet applicable = plugin.getRegionContainer().createQuery().queryContains(player.getLocation()); if (!RegionQueryUtil.isInvincible(plugin, player, applicable) && !plugin.getGlobalStateManager().hasGodMode(player) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java index 9fd7b870..cd02b8ff 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java @@ -63,15 +63,15 @@ public void onPlaceBlock(PlaceBlockEvent event) { Material type = event.getEffectiveMaterial(); if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(player); + RegionQuery query = getPlugin().getRegionContainer().createQuery(); boolean canPlace; // Flint and steel, fire charge if (type == Material.FIRE) { - canPlace = query.testPermission(target, DefaultFlag.LIGHTER); + canPlace = query.testPermission(target, player, DefaultFlag.LIGHTER); } else { - canPlace = query.testPermission(target); + canPlace = query.testPermission(target, player); } if (!canPlace) { @@ -87,8 +87,8 @@ public void onBreakBlock(BreakBlockEvent event) { Location target = event.getTarget(); if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(player); - boolean canBreak = query.testPermission(target); + RegionQuery query = getPlugin().getRegionContainer().createQuery(); + boolean canBreak = query.testPermission(target, player); if (!canBreak) { tellErrorMessage(player, target); @@ -104,24 +104,24 @@ public void onUseBlock(UseBlockEvent event) { Material type = event.getEffectiveMaterial(); if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(player); + RegionQuery query = getPlugin().getRegionContainer().createQuery(); boolean canUse; // Inventory blocks (CHEST_ACCESS) if (Materials.isInventoryBlock(type)) { - canUse = query.testPermission(target, DefaultFlag.USE, DefaultFlag.CHEST_ACCESS); + canUse = query.testPermission(target, player, DefaultFlag.USE, DefaultFlag.CHEST_ACCESS); // Beds (SLEEP) } else if (type == Material.BED) { - canUse = query.testPermission(target, DefaultFlag.USE, DefaultFlag.SLEEP); + canUse = query.testPermission(target, player, DefaultFlag.USE, DefaultFlag.SLEEP); // TNT (TNT) } else if (type == Material.TNT) { - canUse = query.testPermission(target, DefaultFlag.TNT); + canUse = query.testPermission(target, player, DefaultFlag.TNT); // Everything else } else { - canUse = query.testPermission(target, DefaultFlag.USE); + canUse = query.testPermission(target, player, DefaultFlag.USE); } if (!canUse) { @@ -138,13 +138,13 @@ public void onSpawnEntity(SpawnEntityEvent event) { EntityType type = event.getEffectiveType(); if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(player); + RegionQuery query = getPlugin().getRegionContainer().createQuery(); boolean canSpawn; if (Entities.isVehicle(type)) { - canSpawn = query.testPermission(target, DefaultFlag.PLACE_VEHICLE); + canSpawn = query.testPermission(target, player, DefaultFlag.PLACE_VEHICLE); } else { - canSpawn = query.testPermission(target); + canSpawn = query.testPermission(target, player); } if (!canSpawn) { @@ -161,13 +161,13 @@ public void onDestroyEntity(DestroyEntityEvent event) { EntityType type = event.getEntity().getType(); if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(player); + RegionQuery query = getPlugin().getRegionContainer().createQuery(); boolean canDestroy; if (Entities.isVehicle(type)) { - canDestroy = query.testPermission(target, DefaultFlag.DESTROY_VEHICLE); + canDestroy = query.testPermission(target, player, DefaultFlag.DESTROY_VEHICLE); } else { - canDestroy = query.testPermission(target); + canDestroy = query.testPermission(target, player); } if (!canDestroy) { @@ -183,8 +183,8 @@ public void onUseEntity(UseEntityEvent event) { Location target = event.getTarget(); if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(player); - boolean canUse = query.testPermission(target, DefaultFlag.USE); + RegionQuery query = getPlugin().getRegionContainer().createQuery(); + boolean canUse = query.testPermission(target, player, DefaultFlag.USE); if (!canUse) { tellErrorMessage(player, target); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java index d29e8594..fba3b5a1 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardBlockListener.java @@ -19,7 +19,6 @@ package com.sk89q.worldguard.bukkit.listener; -import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldedit.blocks.BlockType; import com.sk89q.worldedit.blocks.ItemType; @@ -28,7 +27,6 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; @@ -55,8 +53,6 @@ import org.bukkit.event.block.LeavesDecayEvent; import org.bukkit.inventory.ItemStack; -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - /** * The listener for block events. * @@ -275,9 +271,7 @@ public void onBlockIgnite(BlockIgniteEvent event) { } if (wcfg.useRegions) { - Vector pt = toVector(block); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(block.getLocation()); if (wcfg.highFreqFlags && isFireSpread && !set.allows(DefaultFlag.FIRE_SPREAD)) { @@ -351,14 +345,11 @@ public void onBlockBurn(BlockBurnEvent event) { int x = block.getX(); int y = block.getY(); int z = block.getZ(); - Vector pt = toVector(block); - RegionManager mgr = plugin.getGlobalRegionManager().get(block.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(block.getLocation()); if (!set.allows(DefaultFlag.FIRE_SPREAD)) { checkAndDestroyAround(block.getWorld(), x, y, z, BlockID.FIRE); event.setCancelled(true); - return; } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardCommandBookListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardCommandBookListener.java index 40fa77eb..340d7e27 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardCommandBookListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardCommandBookListener.java @@ -44,8 +44,7 @@ public void onPlayerWhois(InfoComponent.PlayerWhoisEvent event) { Player player = (Player) event.getPlayer(); LocalPlayer localPlayer = plugin.wrapPlayer(player); if (plugin.getGlobalStateManager().get(player.getWorld()).useRegions) { - ApplicableRegionSet regions = plugin.getGlobalRegionManager() - .get(player.getWorld()).getApplicableRegions(player.getLocation()); + ApplicableRegionSet regions = plugin.getRegionContainer().createQuery().queryContains(player.getLocation()); // Current regions StringBuilder regionStr = new StringBuilder(); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java index aa30e18a..ad740a6e 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java @@ -19,14 +19,14 @@ package com.sk89q.worldguard.bukkit.listener; -import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.bukkit.BukkitUtil; import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.util.RegionQueryUtil; +import com.sk89q.worldguard.bukkit.RegionQuery; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.util.RegionQueryUtil; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.GlobalRegionManager; import com.sk89q.worldguard.protection.events.DisallowedPVPEvent; @@ -83,8 +83,6 @@ import java.util.Set; -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - /** * Listener for entity related events. * @@ -266,13 +264,11 @@ private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { if (attacker != null) { if (attacker instanceof Player) { if (wcfg.useRegions) { - Vector pt = toVector(defender.getLocation()); - Vector pt2 = toVector(attacker.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); + RegionQuery query = plugin.getRegionContainer().createQuery(); - if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer((Player) attacker))) { + if (!query.testState(attacker.getLocation(), (Player) attacker, DefaultFlag.PVP)) { tryCancelPVPEvent((Player) attacker, player, event, true); - } else if (!mgr.getApplicableRegions(pt).allows(DefaultFlag.PVP ,localPlayer)) { + } else if (!query.testState(defender.getLocation(), (Player) defender, DefaultFlag.PVP)) { tryCancelPVPEvent((Player) attacker, player, event, false); } } @@ -286,10 +282,8 @@ private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { return; } if (wcfg.useRegions && wcfg.explosionFlagCancellation) { - Vector pt = toVector(defender.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - if (!set.allows(DefaultFlag.TNT, localPlayer)) { + RegionQuery query = plugin.getRegionContainer().createQuery(); + if (!query.testState(defender.getLocation(), (Player) defender, DefaultFlag.TNT)) { event.setCancelled(true); return; } @@ -310,18 +304,16 @@ private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { } if (wcfg.useRegions) { Fireball fireball = (Fireball) attacker; - Vector pt = toVector(defender.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); + RegionQuery query = plugin.getRegionContainer().createQuery(); if (fireball.getShooter() instanceof Player) { - Vector pt2 = toVector(((Player) fireball.getShooter()).getLocation()); - if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer((Player) fireball.getShooter()))) { + Location pt2 = ((Player) fireball.getShooter()).getLocation(); + if (!query.testState(pt2, (Player) fireball.getShooter(), DefaultFlag.PVP)) { tryCancelPVPEvent((Player) fireball.getShooter(), player, event, true); - } else if (!set.allows(DefaultFlag.PVP, localPlayer)) { + } else if (!query.testState(defender.getLocation(), (Player) defender, DefaultFlag.PVP)) { tryCancelPVPEvent((Player) fireball.getShooter(), player, event, false); } } else { - if (!set.allows(DefaultFlag.GHAST_FIREBALL, localPlayer) && wcfg.explosionFlagCancellation) { + if (!query.testState(defender.getLocation(), (Player) defender, DefaultFlag.GHAST_FIREBALL) && wcfg.explosionFlagCancellation) { event.setCancelled(true); return; } @@ -342,9 +334,8 @@ private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { } if (wcfg.useRegions) { - Vector pt = toVector(defender.getLocation()); RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(defender.getLocation()); if (!set.allows(DefaultFlag.MOB_DAMAGE, localPlayer) && !(attacker instanceof Tameable)) { event.setCancelled(true); @@ -368,8 +359,7 @@ private void onEntityDamageByEntity(EntityDamageByEntityEvent event) { return; } Player beastMaster = (Player) ((Tameable) attacker).getOwner(); - Vector pt2 = toVector(attacker.getLocation()); - if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer(beastMaster))) { + if (!plugin.getRegionContainer().createQuery().queryContains(attacker.getLocation()).allows(DefaultFlag.PVP, plugin.wrapPlayer(beastMaster))) { tryCancelPVPEvent(beastMaster, player, event, true); } else if (!set.allows(DefaultFlag.PVP, localPlayer)) { tryCancelPVPEvent(beastMaster, player, event, false); @@ -411,10 +401,7 @@ private void onEntityDamageByProjectile(EntityDamageByEntityEvent event) { return; } if (wcfg.useRegions) { - Vector pt = toVector(defender.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - - if (!mgr.getApplicableRegions(pt).allows(DefaultFlag.MOB_DAMAGE, localPlayer)) { + if (!plugin.getRegionContainer().createQuery().queryContains(defender.getLocation()).allows(DefaultFlag.MOB_DAMAGE, localPlayer)) { event.setCancelled(true); return; } @@ -426,13 +413,9 @@ private void onEntityDamageByProjectile(EntityDamageByEntityEvent event) { if (attacker != null && attacker instanceof Player) { if (event.getDamager() instanceof EnderPearl && attacker == player) return; if (wcfg.useRegions) { - Vector pt = toVector(defender.getLocation()); - Vector pt2 = toVector(attacker.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - - if (!mgr.getApplicableRegions(pt2).allows(DefaultFlag.PVP, plugin.wrapPlayer((Player) attacker))) { + if (!plugin.getRegionContainer().createQuery().queryContains(attacker.getLocation()).allows(DefaultFlag.PVP, plugin.wrapPlayer((Player) attacker))) { tryCancelPVPEvent((Player) attacker, player, event, true); - } else if (!mgr.getApplicableRegions(pt).allows(DefaultFlag.PVP, localPlayer)) { + } else if (!plugin.getRegionContainer().createQuery().queryContains(defender.getLocation()).allows(DefaultFlag.PVP, localPlayer)) { tryCancelPVPEvent((Player) attacker, player, event, false); } } @@ -485,9 +468,7 @@ public void onEntityDamage(EntityDamageEvent event) { } if (wcfg.useRegions) { - Vector pt = toVector(defender.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(defender.getLocation()); if (!set.allows(DefaultFlag.MOB_DAMAGE, plugin.wrapPlayer(player))) { event.setCancelled(true); @@ -593,7 +574,7 @@ public void onEntityExplode(EntityExplodeEvent event) { RegionManager mgr = plugin.getGlobalRegionManager().get(world); for (Block block : event.blockList()) { - if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.CREEPER_EXPLOSION)) { + if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.CREEPER_EXPLOSION)) { event.blockList().clear(); if (wcfg.explosionFlagCancellation) event.setCancelled(true); return; @@ -610,7 +591,7 @@ public void onEntityExplode(EntityExplodeEvent event) { RegionManager mgr = plugin.getGlobalRegionManager().get(world); for (Block block : event.blockList()) { - if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.ENDERDRAGON_BLOCK_DAMAGE)) { + if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.ENDERDRAGON_BLOCK_DAMAGE)) { event.blockList().clear(); if (wcfg.explosionFlagCancellation) event.setCancelled(true); return; @@ -631,7 +612,7 @@ public void onEntityExplode(EntityExplodeEvent event) { RegionManager mgr = plugin.getGlobalRegionManager().get(world); for (Block block : event.blockList()) { - if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.TNT)) { + if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.TNT)) { event.blockList().clear(); if (wcfg.explosionFlagCancellation) event.setCancelled(true); return; @@ -663,7 +644,7 @@ public void onEntityExplode(EntityExplodeEvent event) { RegionManager mgr = plugin.getGlobalRegionManager().get(world); for (Block block : event.blockList()) { - if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.GHAST_FIREBALL)) { + if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.GHAST_FIREBALL)) { event.blockList().clear(); if (wcfg.explosionFlagCancellation) event.setCancelled(true); return; @@ -688,7 +669,7 @@ public void onEntityExplode(EntityExplodeEvent event) { if (wcfg.useRegions) { RegionManager mgr = plugin.getGlobalRegionManager().get(world); for (Block block : event.blockList()) { - if (!mgr.getApplicableRegions(toVector(block)).allows(DefaultFlag.OTHER_EXPLOSION)) { + if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.OTHER_EXPLOSION)) { event.blockList().clear(); if (wcfg.explosionFlagCancellation) event.setCancelled(true); return; @@ -785,11 +766,7 @@ public void onCreatureSpawn(CreatureSpawnEvent event) { Location eventLoc = event.getLocation(); if (wcfg.useRegions && cfg.useRegionsCreatureSpawnEvent) { - Vector pt = toVector(eventLoc); - RegionManager mgr = plugin.getGlobalRegionManager().get(eventLoc.getWorld()); - // @TODO get victims' stacktraces and find out why it's null anyway - if (mgr == null) return; - ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(eventLoc); if (!set.allows(DefaultFlag.MOB_SPAWNING)) { event.setCancelled(true); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java index bdd08b3b..6bb6a7a2 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardPlayerListener.java @@ -24,9 +24,9 @@ import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.bukkit.BukkitUtil; import com.sk89q.worldguard.bukkit.ConfigurationManager; -import com.sk89q.worldguard.bukkit.listener.FlagStateManager.PlayerFlagState; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.listener.FlagStateManager.PlayerFlagState; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.managers.RegionManager; @@ -44,7 +44,6 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.event.player.PlayerBedEnterEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerFishEvent; @@ -66,9 +65,6 @@ import java.util.logging.Level; import java.util.regex.Pattern; -import static com.sk89q.worldguard.bukkit.BukkitUtil.createTarget; -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - /** * Handles all events thrown in relation to a player. */ @@ -184,8 +180,7 @@ public static boolean checkMove(WorldGuardPlugin plugin, Player player, Location // Have to set this state if (state.lastExitAllowed == null) { - state.lastExitAllowed = plugin.getGlobalRegionManager().get(world) - .getApplicableRegions(toVector(from)) + state.lastExitAllowed = plugin.getRegionContainer().createQuery().queryContains(from) .allows(DefaultFlag.EXIT, localPlayer); } @@ -305,8 +300,7 @@ public void onPlayerGameModeChange(PlayerGameModeChangeEvent event) { Player player = event.getPlayer(); WorldConfiguration wcfg = plugin.getGlobalStateManager().get(player.getWorld()); if (wcfg.useRegions && !plugin.getGlobalRegionManager().hasBypass(player, player.getWorld())) { - GameMode gameMode = plugin.getGlobalRegionManager().get(player.getWorld()) - .getApplicableRegions(player.getLocation()).getFlag(DefaultFlag.GAME_MODE); + GameMode gameMode = plugin.getRegionContainer().createQuery().queryContains(player.getLocation()).getFlag(DefaultFlag.GAME_MODE); if (plugin.getFlagStateManager().getState(player).lastGameMode != null && gameMode != null && event.getNewGameMode() != gameMode) { event.setCancelled(true); @@ -435,10 +429,8 @@ public void onPlayerQuit(PlayerQuitEvent event) { if (state.lastWorld != null && !hasBypass) { LocalPlayer localPlayer = plugin.wrapPlayer(player); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); Location loc = player.getLocation(); - Vector pt = new Vector(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(loc); if (state.lastExitAllowed == null) { state.lastExitAllowed = set.allows(DefaultFlag.EXIT, localPlayer); @@ -523,11 +515,9 @@ private void handleBlockRightClick(PlayerInteractEvent event) { } if (wcfg.useRegions) { - Vector pt = toVector(block); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); Block placedIn = block.getRelative(event.getBlockFace()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - ApplicableRegionSet placedInSet = mgr.getApplicableRegions(placedIn.getLocation()); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(block.getLocation()); + ApplicableRegionSet placedInSet = plugin.getRegionContainer().createQuery().queryContains(placedIn.getLocation()); LocalPlayer localPlayer = plugin.wrapPlayer(player); if (item.getTypeId() == wcfg.regionWand && plugin.hasPermission(player, "worldguard.region.wand")) { @@ -609,9 +599,7 @@ public void onPlayerRespawn(PlayerRespawnEvent event) { WorldConfiguration wcfg = cfg.get(player.getWorld()); if (wcfg.useRegions) { - Vector pt = toVector(location); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(location); LocalPlayer localPlayer = plugin.wrapPlayer(player); com.sk89q.worldedit.Location spawn = set.getFlag(DefaultFlag.SPAWN_LOC, localPlayer); @@ -640,28 +628,6 @@ public void onItemHeldChange(PlayerItemHeldEvent event) { } } - @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) - public void onPlayerBedEnter(PlayerBedEnterEvent event) { - Player player = event.getPlayer(); - Location location = player.getLocation(); - - ConfigurationManager cfg = plugin.getGlobalStateManager(); - WorldConfiguration wcfg = cfg.get(player.getWorld()); - - if (wcfg.useRegions) { - Vector pt = toVector(location); - RegionManager mgr = plugin.getGlobalRegionManager().get(player.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - - if (!plugin.getGlobalRegionManager().hasBypass(player, player.getWorld()) - && !set.allows(DefaultFlag.SLEEP, plugin.wrapPlayer(player))) { - event.setCancelled(true); - player.sendMessage("This bed doesn't belong to you!"); - return; - } - } - } - @EventHandler(priority= EventPriority.LOW, ignoreCancelled = true) public void onPlayerTeleport(PlayerTeleportEvent event) { World world = event.getFrom().getWorld(); @@ -669,11 +635,8 @@ public void onPlayerTeleport(PlayerTeleportEvent event) { WorldConfiguration wcfg = cfg.get(world); if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(event.getFrom().getWorld()); - Vector pt = new Vector(event.getTo().getBlockX(), event.getTo().getBlockY(), event.getTo().getBlockZ()); - Vector ptFrom = new Vector(event.getFrom().getBlockX(), event.getFrom().getBlockY(), event.getFrom().getBlockZ()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); - ApplicableRegionSet setFrom = mgr.getApplicableRegions(ptFrom); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(event.getTo()); + ApplicableRegionSet setFrom = plugin.getRegionContainer().createQuery().queryContains(event.getFrom()); LocalPlayer localPlayer = plugin.wrapPlayer(event.getPlayer()); if (cfg.usePlayerTeleports) { @@ -705,9 +668,7 @@ public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { WorldConfiguration wcfg = cfg.get(world); if (wcfg.useRegions && !plugin.getGlobalRegionManager().hasBypass(player, world)) { - Vector pt = toVector(player.getLocation()); - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(player.getLocation()); Set allowedCommands = set.getFlag(DefaultFlag.ALLOWED_CMDS, localPlayer); Set blockedCommands = set.getFlag(DefaultFlag.BLOCKED_CMDS, localPlayer); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardWeatherListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardWeatherListener.java index 7fc1578e..66913ebe 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardWeatherListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardWeatherListener.java @@ -19,11 +19,11 @@ package com.sk89q.worldguard.bukkit.listener; -import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector; - import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.DefaultFlag; import org.bukkit.Location; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -31,10 +31,6 @@ import org.bukkit.event.weather.LightningStrikeEvent; import org.bukkit.event.weather.ThunderChangeEvent; import org.bukkit.event.weather.WeatherChangeEvent; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; public class WorldGuardWeatherListener implements Listener { @@ -102,9 +98,7 @@ public void onLightningStrike(LightningStrikeEvent event) { Location loc = event.getLightning().getLocation(); if (wcfg.useRegions) { - Vector pt = toVector(loc); - RegionManager mgr = plugin.getGlobalRegionManager().get(loc.getWorld()); - ApplicableRegionSet set = mgr.getApplicableRegions(pt); + ApplicableRegionSet set = plugin.getRegionContainer().createQuery().queryContains(loc); if (!set.allows(DefaultFlag.LIGHTNING)) { event.setCancelled(true); diff --git a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java index aac4e751..878aff2d 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java +++ b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java @@ -31,6 +31,7 @@ import javax.annotation.Nullable; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -50,6 +51,11 @@ */ public class ApplicableRegionSet implements Iterable { + /** + * A static instance of an empty set. + */ + private static final ApplicableRegionSet EMPTY = new ApplicableRegionSet(Collections.emptyList(), null); + private final SortedSet applicable; @Nullable private final ProtectedRegion globalRegion; @@ -434,4 +440,11 @@ public Iterator iterator() { return applicable.iterator(); } + /** + * Return an instance that contains no regions and has no global region. + */ + public static ApplicableRegionSet getEmpty() { + return EMPTY; + } + } diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index 7a7c5a4d..d55181cf 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -20,6 +20,7 @@ package com.sk89q.worldguard.protection; import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.bukkit.BukkitPlayer; import com.sk89q.worldguard.bukkit.RegionContainer; import com.sk89q.worldguard.bukkit.RegionQuery; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; @@ -84,33 +85,13 @@ public List getLoaded() { return Collections.unmodifiableList(container.getLoaded()); } - /** - * Create a new region query with no player. - * - * @return a new query - */ - private RegionQuery createAnonymousQuery() { - return container.createAnonymousQuery(); - } - /** * Create a new region query. * - * @param player a player, or {@code null} * @return a new query */ - private RegionQuery createQuery(@Nullable Player player) { - return container.createQuery(player); - } - - /** - * Create a new region query. - * - * @param player a player, or {@code null} - * @return a new query - */ - private RegionQuery createQuery(@Nullable LocalPlayer player) { - return container.createQuery(player); + private RegionQuery createQuery() { + return container.createQuery(); } /** @@ -119,7 +100,7 @@ private RegionQuery createQuery(@Nullable LocalPlayer player) { * @param player the player * @param world the world * @return true if a bypass is permitted - * @deprecated use {@link #createQuery(Player)} + * @deprecated use {@link RegionContainer#createQuery()} */ @Deprecated public boolean hasBypass(LocalPlayer player, World world) { @@ -132,7 +113,7 @@ public boolean hasBypass(LocalPlayer player, World world) { * @param player the player * @param world the world * @return true if a bypass is permitted - * @deprecated use {@link #createQuery(Player)} + * @deprecated use {@link RegionContainer#createQuery()} */ @Deprecated public boolean hasBypass(Player player, World world) { @@ -152,7 +133,7 @@ public boolean hasBypass(Player player, World world) { * @param player the player * @param block the block * @return true if a bypass is permitted - * @deprecated use {@link #createQuery(Player)} + * @deprecated use {@link RegionContainer#createQuery()} */ @SuppressWarnings("deprecation") @Deprecated @@ -173,11 +154,11 @@ public boolean canBuild(Player player, Block block) { * @param player the player * @param location the location * @return true if a bypass is permitted - * @deprecated use {@link #createQuery(Player)} + * @deprecated use {@link RegionContainer#createQuery()} */ @Deprecated public boolean canBuild(Player player, Location location) { - return createQuery(player).testPermission(location); + return createQuery().testPermission(location, player); } /** @@ -213,7 +194,7 @@ public boolean canConstruct(Player player, Location location) { * @param flag the flag * @param location the location * @return true if set to true - * @deprecated use {@link #createQuery(Player)} + * @deprecated use {@link RegionContainer#createQuery()} */ @Deprecated @SuppressWarnings("deprecation") @@ -229,11 +210,16 @@ public boolean allows(StateFlag flag, Location location) { * @param location the location * @param player the actor * @return true if set to true - * @deprecated use {@link #createQuery(Player)} + * @deprecated use {@link RegionContainer#createQuery()} */ @Deprecated public boolean allows(StateFlag flag, Location location, @Nullable LocalPlayer player) { - return createQuery(player).testState(location, flag); + if (player instanceof BukkitPlayer) { + Player p = ((BukkitPlayer) player).getPlayer(); + return createQuery().testState(location, p, flag); + } else { + throw new IllegalArgumentException("Can't take a non-Bukkit player"); + } } } From bf8ef398718888088a4b258d3ab680f24f6cd9b8 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 15:17:00 -0700 Subject: [PATCH 040/133] Handle null players in shim method of GlobalRegionManager. --- .../com/sk89q/worldguard/protection/GlobalRegionManager.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java index d55181cf..488edb94 100644 --- a/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/GlobalRegionManager.java @@ -214,7 +214,9 @@ public boolean allows(StateFlag flag, Location location) { */ @Deprecated public boolean allows(StateFlag flag, Location location, @Nullable LocalPlayer player) { - if (player instanceof BukkitPlayer) { + if (player == null) { + return createQuery().testState(location, null, flag); + } else if (player instanceof BukkitPlayer) { Player p = ((BukkitPlayer) player).getPlayer(); return createQuery().testState(location, p, flag); } else { From 7e25de38da28dd2e6aa92fd3069b010ad3781e3e Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 15:20:08 -0700 Subject: [PATCH 041/133] Update Javadocs in RegionContainer. --- .../com/sk89q/worldguard/bukkit/RegionContainer.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java index 2c1b2c1d..660882aa 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionContainer.java @@ -20,12 +20,10 @@ package com.sk89q.worldguard.bukkit; import com.sk89q.worldedit.Vector2D; -import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.protection.managers.RegionManager; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; -import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; @@ -198,13 +196,12 @@ void unload(World world) { /** * Get the region manager for a world if one exists. * + *

If you wish to make queries and performance is more important + * than accuracy, use {@link #createQuery()} instead.

+ * *

This method may return {@code null} if region data for the given * world has not been loaded, has failed to load, or support for regions - * has been disabled. If you merely want to query flags or a list of - * regions in a position, using {@link #createQuery(Player)} is much - * simpler and it will handle the case of a {@code null} - * {@code RegionManager}. That said, if you wish to make changes to - * regions, then you will have to get a region manager.

+ * has been disabled.

* * @param world the world * @return a region manager, or {@code null} if one is not available From 518d5958c979d5e8000530994643ea35fb8b58fb Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 16:21:10 -0700 Subject: [PATCH 042/133] Update some region commands to run in the background. --- .../bukkit/commands/RegionCommands.java | 296 ++++++++++-------- .../RegionManagerReload.java} | 8 +- .../RegionmanagerSave.java} | 8 +- .../protection/databases/RegionDBUtil.java | 109 ------- .../com/sk89q/worldguard/util/RegionUtil.java | 74 ----- 5 files changed, 174 insertions(+), 321 deletions(-) rename src/main/java/com/sk89q/worldguard/bukkit/commands/{RegionManagerLoad.java => task/RegionManagerReload.java} (84%) rename src/main/java/com/sk89q/worldguard/bukkit/commands/{RegionManagerSave.java => task/RegionmanagerSave.java} (85%) delete mode 100644 src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java delete mode 100644 src/main/java/com/sk89q/worldguard/util/RegionUtil.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java index e30423ea..265f448a 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java @@ -34,25 +34,28 @@ import com.sk89q.worldedit.bukkit.selections.Polygonal2DSelection; import com.sk89q.worldedit.bukkit.selections.Selection; import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.bukkit.util.LoggerToChatHandler; -import com.sk89q.worldguard.bukkit.permission.RegionPermissionModel; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.commands.task.RegionAdd; +import com.sk89q.worldguard.bukkit.commands.task.RegionManagerReload; +import com.sk89q.worldguard.bukkit.commands.task.RegionmanagerSave; +import com.sk89q.worldguard.bukkit.permission.RegionPermissionModel; +import com.sk89q.worldguard.bukkit.util.LoggerToChatHandler; import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.databases.RegionDBUtil; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; import com.sk89q.worldguard.protection.flags.RegionGroup; import com.sk89q.worldguard.protection.flags.RegionGroupFlag; import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.util.migrator.MigrationException; -import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator; import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; +import com.sk89q.worldguard.protection.util.DomainInputResolver.UserLocatorPolicy; +import com.sk89q.worldguard.protection.util.migrator.MigrationException; +import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; @@ -346,6 +349,7 @@ private static void setFlag(ProtectedRegion region, Flag flag, CommandSen */ @Command(aliases = {"define", "def", "d", "create"}, usage = " [ [ []]]", + flags = "n", desc = "Defines a region", min = 1) public void define(CommandContext args, CommandSender sender) throws CommandException { @@ -355,47 +359,56 @@ public void define(CommandContext args, CommandSender sender) throws CommandExce if (!getPermissionModel(sender).mayDefine()) { throw new CommandPermissionsException(); } - + // Get and validate the region ID String id = validateRegionId(args.getString(0), false); - + // Can't replace regions with this command - RegionManager regionManager = plugin.getGlobalRegionManager().get(player.getWorld()); - if (regionManager.hasRegion(id)) { - throw new CommandException( - "That region is already defined. To change the shape, use " + - "/region redefine " + id); - } + final RegionManager manager = plugin.getRegionContainer().get(player.getWorld()); - // Make a region from the user's selection - ProtectedRegion region = createRegionFromSelection(player, id); + if (manager != null) { + if (manager.hasRegion(id)) { + throw new CommandException("A region with that ID already exists. To change the shape, use /region redefine " + id); + } - // Get the list of region owners - if (args.argsLength() > 1) { - region.setOwners(RegionDBUtil.parseDomainString(args.getSlice(1), 1)); - } - - // Issue a warning about height - int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); - if (height <= 2) { - sender.sendMessage(ChatColor.GOLD + - "(Warning: The height of the region was " + (height + 1) + " block(s).)"); - } + // Make a region from the user's selection + final ProtectedRegion region = createRegionFromSelection(player, id); - // Hint - if (regionManager.getRegions().size() <= 2) { - sender.sendMessage(ChatColor.GRAY + - "(This region is NOW PROTECTED from modification from others. " + - "Don't want that? Use " + - ChatColor.AQUA + "/rg flag " + id + " passthrough allow" + - ChatColor.GRAY + ")"); - } - - // Tell the user - sender.sendMessage(ChatColor.YELLOW + "A new region has been made named '" + id + "'."); + // Issue a warning about height + int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); + if (height <= 2) { + sender.sendMessage(ChatColor.GRAY + "(Warning: The height of the region was " + (height + 1) + " block(s).)"); + } - // Add region - regionManager.addRegion(region); + // Hint + if (manager.getRegions().size() <= 2) { + sender.sendMessage(ChatColor.GRAY + + "(This region is NOW PROTECTED from modification from others. " + + "Don't want that? Use " + + ChatColor.AQUA + "/rg flag " + id + " passthrough allow" + + ChatColor.GRAY + ")"); + } + + RegionAdd task = new RegionAdd(plugin, manager, region); + + // Add the list of region owners + if (args.argsLength() > 1) { + task.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); + task.setOwnersInput(args.getSlice(2)); + } + + ListenableFuture future = plugin.getExecutorService().submit(task); + + AsyncCommandHelper.wrap(future, plugin, player) + .formatUsing(id) + .registerWithSupervisor("Adding the region '%s'...") + .sendMessageAfterDelay("(Please wait... adding '%s'...)") + .thenRespondWith( + "A new region has been made named '%s'.", + "Failed to add the region '%s'"); + } else { + throw new CommandException("Either region support is disabled or region data failed to load in the target world."); + } } /** @@ -417,38 +430,51 @@ public void redefine(CommandContext args, CommandSender sender) throws CommandEx String id = validateRegionId(args.getString(0), false); // Lookup the existing region - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion existing = findExistingRegion(regionManager, id, false); + RegionManager manager = plugin.getRegionContainer().get(world); - // Check permissions - if (!getPermissionModel(sender).mayRedefine(existing)) { - throw new CommandPermissionsException(); + if (manager != null) { + ProtectedRegion existing = findExistingRegion(manager, id, false); + + // Check permissions + if (!getPermissionModel(sender).mayRedefine(existing)) { + throw new CommandPermissionsException(); + } + + // Make a region from the user's selection + ProtectedRegion region = createRegionFromSelection(player, id); + + // Copy details from the old region to the new one + region.setMembers(existing.getMembers()); + region.setOwners(existing.getOwners()); + region.setFlags(existing.getFlags()); + region.setPriority(existing.getPriority()); + try { + region.setParent(existing.getParent()); + } catch (CircularInheritanceException ignore) { + // This should not be thrown + } + + // Issue a warning about height + int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); + if (height <= 2) { + sender.sendMessage(ChatColor.GOLD + + "(Warning: The height of the region was " + (height + 1) + " block(s).)"); + } + + RegionAdd task = new RegionAdd(plugin, manager, region); + + ListenableFuture future = plugin.getExecutorService().submit(task); + + AsyncCommandHelper.wrap(future, plugin, player) + .formatUsing(id) + .registerWithSupervisor("Updating the region '%s'...") + .sendMessageAfterDelay("(Please wait... updating '%s'...)") + .thenRespondWith( + "Region '%s' has been updated with a new area.", + "Failed to update the region '%s'"); + } else { + throw new CommandException("Either region support is disabled or region data failed to load in the target world."); } - - // Make a region from the user's selection - ProtectedRegion region = createRegionFromSelection(player, id); - - // Copy details from the old region to the new one - region.setMembers(existing.getMembers()); - region.setOwners(existing.getOwners()); - region.setFlags(existing.getFlags()); - region.setPriority(existing.getPriority()); - try { - region.setParent(existing.getParent()); - } catch (CircularInheritanceException ignore) { - // This should not be thrown - } - - // Issue a warning about height - int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); - if (height <= 2) { - sender.sendMessage(ChatColor.GOLD + - "(Warning: The height of the region was " + (height + 1) + " block(s).)"); - } - - sender.sendMessage(ChatColor.YELLOW + "Region '" + id + "' updated with new area."); - - regionManager.addRegion(region); // Replace region } /** @@ -479,73 +505,83 @@ public void claim(CommandContext args, CommandSender sender) throws CommandExcep String id = validateRegionId(args.getString(0), false); // Can't replace existing regions - RegionManager regionManager = plugin.getGlobalRegionManager().get(player.getWorld()); - if (regionManager.hasRegion(id)) { - throw new CommandException( - "That region already exists. Please choose a different name."); - } + RegionManager manager = plugin.getGlobalRegionManager().get(player.getWorld()); - // Make a region from the user's selection - ProtectedRegion region = createRegionFromSelection(player, id); - - // Get the list of region owners - if (args.argsLength() > 1) { - region.setOwners(RegionDBUtil.parseDomainString(args.getSlice(1), 1)); - } - - WorldConfiguration wcfg = plugin.getGlobalStateManager().get(player.getWorld()); - - // Check whether the player has created too many regions - if (!permModel.mayClaimRegionsUnbounded()) { - int maxRegionCount = wcfg.getMaxRegionCount(player); - if (maxRegionCount >= 0 - && regionManager.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) { - throw new CommandException( - "You own too many regions, delete one first to claim a new one."); + if (manager != null) { + if (manager.hasRegion(id)) { + throw new CommandException("That region already exists. Please choose a different name."); } - } - ProtectedRegion existing = regionManager.getRegion(id); + // Make a region from the user's selection + ProtectedRegion region = createRegionFromSelection(player, id); - // Check for an existing region - if (existing != null) { - if (!existing.getOwners().contains(localPlayer)) { - throw new CommandException( - "This region already exists and you don't own it."); + // Get the list of region owners + /*if (args.argsLength() > 1) { + region.setOwners(RegionDBUtil.parseDomainString(args.getSlice(1), 1)); + }*/ + + WorldConfiguration wcfg = plugin.getGlobalStateManager().get(player.getWorld()); + + // Check whether the player has created too many regions + if (!permModel.mayClaimRegionsUnbounded()) { + int maxRegionCount = wcfg.getMaxRegionCount(player); + if (maxRegionCount >= 0 + && manager.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) { + throw new CommandException( + "You own too many regions, delete one first to claim a new one."); + } } - } - // We have to check whether this region violates the space of any other reion - ApplicableRegionSet regions = regionManager.getApplicableRegions(region); + ProtectedRegion existing = manager.getRegion(id); - // Check if this region overlaps any other region - if (regions.size() > 0) { - if (!regions.isOwnerOfAll(localPlayer)) { - throw new CommandException("This region overlaps with someone else's region."); + // Check for an existing region + if (existing != null) { + if (!existing.getOwners().contains(localPlayer)) { + throw new CommandException( + "This region already exists and you don't own it."); + } } + + // We have to check whether this region violates the space of any other reion + ApplicableRegionSet regions = manager.getApplicableRegions(region); + + // Check if this region overlaps any other region + if (regions.size() > 0) { + if (!regions.isOwnerOfAll(localPlayer)) { + throw new CommandException("This region overlaps with someone else's region."); + } + } else { + if (wcfg.claimOnlyInsideExistingRegions) { + throw new CommandException("You may only claim regions inside " + + "existing regions that you or your group own."); + } + } + + // Check claim volume + if (!permModel.mayClaimRegionsUnbounded()) { + if (region.volume() > wcfg.maxClaimVolume) { + player.sendMessage(ChatColor.RED + "This region is too large to claim."); + player.sendMessage(ChatColor.RED + + "Max. volume: " + wcfg.maxClaimVolume + ", your volume: " + region.volume()); + return; + } + } + + region.getOwners().addPlayer(player.getName()); + + RegionAdd task = new RegionAdd(plugin, manager, region); + ListenableFuture future = plugin.getExecutorService().submit(task); + + AsyncCommandHelper.wrap(future, plugin, player) + .formatUsing(id) + .registerWithSupervisor("Claiming the region '%s'...") + .sendMessageAfterDelay("(Please wait... claiming '%s'...)") + .thenRespondWith( + "A new region has been claimed named '%s'.", + "Failed to claim the region '%s'"); } else { - if (wcfg.claimOnlyInsideExistingRegions) { - throw new CommandException("You may only claim regions inside " + - "existing regions that you or your group own."); - } + throw new CommandException("Either region support is disabled or region data failed to load in the target world."); } - - // Check claim volume - if (!permModel.mayClaimRegionsUnbounded()) { - if (region.volume() > wcfg.maxClaimVolume) { - player.sendMessage(ChatColor.RED + "This region is too large to claim."); - player.sendMessage(ChatColor.RED + - "Max. volume: " + wcfg.maxClaimVolume + ", your volume: " + region.volume()); - return; - } - } - - region.getOwners().addPlayer(player.getName()); - - sender.sendMessage(ChatColor.YELLOW + "Region '" + id + "' updated with new area."); - - // Replace region - regionManager.addRegion(region); } /** @@ -1024,7 +1060,7 @@ public void load(CommandContext args, final CommandSender sender) throws Command throw new CommandException("No region manager exists for world '" + world.getName() + "'."); } - ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerLoad(manager)); + ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerReload(manager)); AsyncCommandHelper.wrap(future, plugin, sender) .forRegionDataLoad(world, false); @@ -1039,7 +1075,7 @@ public void load(CommandContext args, final CommandSender sender) throws Command } } - ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerLoad(managers)); + ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerReload(managers)); AsyncCommandHelper.wrap(future, plugin, sender) .registerWithSupervisor("Loading regions for all worlds") @@ -1081,7 +1117,7 @@ public void save(CommandContext args, final CommandSender sender) throws Command throw new CommandException("No region manager exists for world '" + world.getName() + "'."); } - ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerSave(manager)); + ListenableFuture future = plugin.getExecutorService().submit(new RegionmanagerSave(manager)); AsyncCommandHelper.wrap(future, plugin, sender) .forRegionDataSave(world, false); @@ -1096,7 +1132,7 @@ public void save(CommandContext args, final CommandSender sender) throws Command } } - ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerSave(managers)); + ListenableFuture future = plugin.getExecutorService().submit(new RegionmanagerSave(managers)); AsyncCommandHelper.wrap(future, plugin, sender) .registerWithSupervisor("Saving regions for all worlds") diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerLoad.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerReload.java similarity index 84% rename from src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerLoad.java rename to src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerReload.java index c36de61c..15d3f0a4 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerLoad.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerReload.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.bukkit.commands; +package com.sk89q.worldguard.bukkit.commands.task; import com.sk89q.worldguard.protection.managers.RegionManager; @@ -28,16 +28,16 @@ import static com.google.common.base.Preconditions.checkNotNull; -public class RegionManagerLoad implements Callable> { +public class RegionManagerReload implements Callable> { private final Collection managers; - RegionManagerLoad(Collection managers) { + public RegionManagerReload(Collection managers) { checkNotNull(managers); this.managers = managers; } - RegionManagerLoad(RegionManager... manager) { + public RegionManagerReload(RegionManager... manager) { this(Arrays.asList(manager)); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerSave.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionmanagerSave.java similarity index 85% rename from src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerSave.java rename to src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionmanagerSave.java index 19fbf4d8..cd6606f5 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionManagerSave.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionmanagerSave.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.bukkit.commands; +package com.sk89q.worldguard.bukkit.commands.task; import com.sk89q.worldguard.protection.managers.RegionManager; @@ -28,16 +28,16 @@ import static com.google.common.base.Preconditions.checkNotNull; -class RegionManagerSave implements Callable> { +public class RegionmanagerSave implements Callable> { private final Collection managers; - RegionManagerSave(Collection managers) { + public RegionmanagerSave(Collection managers) { checkNotNull(managers); this.managers = managers; } - RegionManagerSave(RegionManager... manager) { + public RegionmanagerSave(RegionManager... manager) { this(Arrays.asList(manager)); } diff --git a/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java b/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java deleted file mode 100644 index 2fcf9d8b..00000000 --- a/src/main/java/com/sk89q/worldguard/protection/databases/RegionDBUtil.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.protection.databases; - -import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.util.DomainInputResolver; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Various utility functions for parsing region databases. - * - * @deprecated use {@link DomainInputResolver} - */ -@Deprecated -public final class RegionDBUtil { - - private static Pattern groupPattern = Pattern.compile("(?i)^[G]:(.+)$"); - - private RegionDBUtil() { - - } - - /** - * Add the given names to {@code domain} - * - * @param domain The domain to add to - * @param split The {@link String[]} containing names to add to {@code domain} - * @param startIndex The beginning index in the array - * @deprecated use {@link DomainInputResolver} - */ - @Deprecated - public static void addToDomain(DefaultDomain domain, String[] split, int startIndex) { - for (int i = startIndex; i < split.length; i++) { - String s = split[i]; - Matcher m = groupPattern.matcher(s); - if (m.matches()) { - domain.addGroup(m.group(1)); - } else { - domain.addPlayer(s); - } - } - } - - /** - * Remove the given names from {@code domain} - * - * @param domain The domain to remove from - * @param split The {@link String[]} containing names to remove from {@code domain} - * @param startIndex The beginning index in the array - * @deprecated use {@link DomainInputResolver} - */ - @Deprecated - public static void removeFromDomain(DefaultDomain domain, String[] split, int startIndex) { - for (int i = startIndex; i < split.length; i++) { - String s = split[i]; - Matcher m = groupPattern.matcher(s); - if (m.matches()) { - domain.removeGroup(m.group(1)); - } else { - domain.removePlayer(s); - } - } - } - - /** - * Parse a group/player DefaultDomain specification for areas. - * - * @param split The array of names to add - * @param startIndex The beginning index in the array - * @return The resulting DefaultDomain - * @deprecated use {@link DomainInputResolver} - */ - @Deprecated - public static DefaultDomain parseDomainString(String[] split, int startIndex) { - DefaultDomain domain = new DefaultDomain(); - - for (int i = startIndex; i < split.length; i++) { - String s = split[i]; - Matcher m = groupPattern.matcher(s); - if (m.matches()) { - domain.addGroup(m.group(1)); - } else { - domain.addPlayer(s); - } - } - - return domain; - } - -} diff --git a/src/main/java/com/sk89q/worldguard/util/RegionUtil.java b/src/main/java/com/sk89q/worldguard/util/RegionUtil.java deleted file mode 100644 index 332963bc..00000000 --- a/src/main/java/com/sk89q/worldguard/util/RegionUtil.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.util; - -import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.databases.RegionDBUtil; - -/** - * Various utility functions for regions. - */ -@Deprecated -public final class RegionUtil { - - private RegionUtil() { - } - - /** - * Parse a group/player DefaultDomain specification for areas. - * - * @param domain The domain - * @param split The arguments - * @param startIndex The index to start at - * @deprecated see {@link RegionDBUtil#addToDomain(com.sk89q.worldguard.domains.DefaultDomain, String[], int)} - */ - @Deprecated - public static void addToDomain(DefaultDomain domain, String[] split, - int startIndex) { - RegionDBUtil.addToDomain(domain, split, startIndex); - } - - /** - * Parse a group/player DefaultDomain specification for areas. - * - * @param domain The domain to add to - * @param split The arguments - * @param startIndex The index to start at - * @deprecated see {@link RegionDBUtil#removeFromDomain(com.sk89q.worldguard.domains.DefaultDomain, String[], int)} - */ - @Deprecated - public static void removeFromDomain(DefaultDomain domain, String[] split, - int startIndex) { - RegionDBUtil.removeFromDomain(domain, split, startIndex); - } - - /** - * Parse a group/player DefaultDomain specification for areas. - * - * @param split The arguments - * @param startIndex The index to start at - * @deprecated see {@link RegionDBUtil#parseDomainString(String[], int)} - * @return the parsed domain - */ - @Deprecated - public static DefaultDomain parseDomainString(String[] split, int startIndex) { - return RegionDBUtil.parseDomainString(split, startIndex); - } -} From 76f7ac8d8e9b7cfd4f10697d5b3c606dd6770aa7 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 21:22:15 -0700 Subject: [PATCH 043/133] Remove deprecation on getRegion() in RegionManager. --- .../com/sk89q/worldguard/protection/managers/RegionManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java index 122c4e35..413c0cb7 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/RegionManager.java @@ -219,7 +219,6 @@ public boolean hasRegion(String id) { * * @param id the name of the region * @return a region or {@code null} - * @deprecated use {@link #matchRegion(String)} */ @Nullable public ProtectedRegion getRegion(String id) { From 501a7aa77ad8676df1fe73409b745e8ad24d47ae Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 21:22:39 -0700 Subject: [PATCH 044/133] Add ProtectedRegion.copyFrom(). --- .../protection/regions/ProtectedRegion.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java index 01744050..d9aba90c 100644 --- a/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java +++ b/src/main/java/com/sk89q/worldguard/protection/regions/ProtectedRegion.java @@ -462,6 +462,24 @@ public void setFlags(Map, Object> flags) { this.flags = new ConcurrentHashMap, Object>(flags); } + /** + * Copy attributes from another region. + * + * @param other the other region + */ + public void copyFrom(ProtectedRegion other) { + checkNotNull(other); + setMembers(other.getMembers()); + setOwners(other.getOwners()); + setFlags(other.getFlags()); + setPriority(other.getPriority()); + try { + setParent(other.getParent()); + } catch (CircularInheritanceException ignore) { + // This should not be thrown + } + } + /** * Get points of the region projected onto the X-Z plane. * From e43ce420a3d7f0efc2a6fbc6d47f65f4367e49f0 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 21:23:26 -0700 Subject: [PATCH 045/133] Make UUID detection in DomainInputResolver reusable. --- .../protection/util/DomainInputResolver.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java b/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java index 57dc66b4..cf9e198a 100644 --- a/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java +++ b/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java @@ -99,10 +99,11 @@ public DefaultDomain call() throws UnresolvedNamesException { if (m.matches()) { domain.addGroup(m.group(1)); } else { - try { + UUID uuid = parseUUID(s); + if (uuid != null) { // Try to add any UUIDs given domain.addPlayer(UUID.fromString(UUIDs.addDashes(s.replaceAll("^uuid:", "")))); - } catch (IllegalArgumentException e) { + } else { switch (locatorPolicy) { case NAME_ONLY: domain.addPlayer(s); @@ -157,4 +158,21 @@ public DefaultDomain apply(@Nullable DefaultDomain domain) { } }; } + + /** + * Try to parse a UUID locator from input. + * + * @param input the input + * @return a UUID or {@code null} if the input is not a UUID + */ + @Nullable + public static UUID parseUUID(String input) { + checkNotNull(input); + + try { + return UUID.fromString(UUIDs.addDashes(input.replaceAll("^uuid:", ""))); + } catch (IllegalArgumentException e) { + return null; + } + } } From a35cb9277e5901f24ccb49070c7b1499927e36ac Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 21:24:02 -0700 Subject: [PATCH 046/133] Handle CommandException in WorldGuardPlugin.convertThrowable(). --- src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 82d4c088..5d526951 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -351,6 +351,8 @@ public String convertThrowable(@Nullable Throwable throwable) { return "WorldGuard: Task was interrupted"; } else if (throwable instanceof UnresolvedNamesException) { return throwable.getMessage(); + } else if (throwable instanceof CommandException) { + return throwable.getMessage(); } else { getLogger().log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable); return "WorldGuard: An unexpected error occurred! Please see the server console."; From 28c538e981aa6e05657551d0e7eeaf200439bd18 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 21:24:32 -0700 Subject: [PATCH 047/133] Refactor region commands and make the necessary ones run in the background. --- .../bukkit/commands/AsyncCommandHelper.java | 2 +- .../bukkit/commands/CommandUtils.java | 6 +- .../commands/FutureProgressListener.java | 4 +- .../commands/MessageFutureCallback.java | 6 +- .../bukkit/commands/MessageTimerTask.java | 2 +- .../bukkit/commands/ProtectionCommands.java | 4 +- .../bukkit/commands/RegionListEntry.java | 59 - .../MemberCommands.java} | 564 ++-- .../commands/{ => region}/RegionCommands.java | 2267 +++++++---------- .../{ => region}/RegionPrintoutBuilder.java | 6 +- ...Reload.java => RegionManagerReloader.java} | 6 +- ...nagerSave.java => RegionManagerSaver.java} | 6 +- .../permission/RegionPermissionModel.java | 16 + 13 files changed, 1228 insertions(+), 1720 deletions(-) delete mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/RegionListEntry.java rename src/main/java/com/sk89q/worldguard/bukkit/commands/{RegionMemberCommands.java => region/MemberCommands.java} (60%) rename src/main/java/com/sk89q/worldguard/bukkit/commands/{ => region}/RegionCommands.java (53%) rename src/main/java/com/sk89q/worldguard/bukkit/commands/{ => region}/RegionPrintoutBuilder.java (97%) rename src/main/java/com/sk89q/worldguard/bukkit/commands/task/{RegionManagerReload.java => RegionManagerReloader.java} (87%) rename src/main/java/com/sk89q/worldguard/bukkit/commands/task/{RegionmanagerSave.java => RegionManagerSaver.java} (87%) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java index a3ebaabc..a12862f3 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java @@ -30,7 +30,7 @@ import static com.google.common.base.Preconditions.checkNotNull; -class AsyncCommandHelper { +public class AsyncCommandHelper { private final ListenableFuture future; private final WorldGuardPlugin plugin; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java index 1d0f7080..77876e3a 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/CommandUtils.java @@ -30,7 +30,7 @@ /** * Command-related utility methods. */ -final class CommandUtils { +public final class CommandUtils { private CommandUtils() { } @@ -41,7 +41,7 @@ private CommandUtils() { * @param owner the owner object * @return a name */ - static String getOwnerName(@Nullable Object owner) { + public static String getOwnerName(@Nullable Object owner) { if (owner == null) { return "?"; } else if (owner instanceof Player) { @@ -62,7 +62,7 @@ static String getOwnerName(@Nullable Object owner) { * @param sender the sender * @return a function */ - static Function messageFunction(final CommandSender sender) { + public static Function messageFunction(final CommandSender sender) { return new Function() { @Override public Object apply(@Nullable String s) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java index 549aaa05..5680a6de 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/FutureProgressListener.java @@ -28,14 +28,14 @@ import static com.google.common.base.Preconditions.checkNotNull; -class FutureProgressListener implements Runnable { +public class FutureProgressListener implements Runnable { private static final Timer timer = new Timer(); private static final int MESSAGE_DELAY = 1000; private final MessageTimerTask task; - FutureProgressListener(CommandSender sender, String message) { + public FutureProgressListener(CommandSender sender, String message) { checkNotNull(sender); checkNotNull(message); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java index cb25126a..d07c04d8 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageFutureCallback.java @@ -28,7 +28,7 @@ import static com.google.common.base.Preconditions.checkNotNull; -class MessageFutureCallback implements FutureCallback { +public class MessageFutureCallback implements FutureCallback { private final WorldGuardPlugin plugin; private final CommandSender sender; @@ -57,7 +57,7 @@ public void onFailure(@Nullable Throwable throwable) { sender.sendMessage(ChatColor.RED + failure + ": " + plugin.convertThrowable(throwable)); } - static class Builder { + public static class Builder { private final WorldGuardPlugin plugin; private final CommandSender sender; @Nullable @@ -65,7 +65,7 @@ static class Builder { @Nullable private String failure; - Builder(WorldGuardPlugin plugin, CommandSender sender) { + public Builder(WorldGuardPlugin plugin, CommandSender sender) { checkNotNull(plugin); checkNotNull(sender); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageTimerTask.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageTimerTask.java index b4554c09..ad3f9a49 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageTimerTask.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/MessageTimerTask.java @@ -25,7 +25,7 @@ import static com.google.common.base.Preconditions.checkNotNull; -class MessageTimerTask extends TimerTask { +public class MessageTimerTask extends TimerTask { private final CommandSender sender; private final String message; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/ProtectionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/ProtectionCommands.java index 136a66b6..d0a6db7a 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/ProtectionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/ProtectionCommands.java @@ -19,6 +19,8 @@ package com.sk89q.worldguard.bukkit.commands; +import com.sk89q.worldguard.bukkit.commands.region.MemberCommands; +import com.sk89q.worldguard.bukkit.commands.region.RegionCommands; import org.bukkit.command.CommandSender; import com.sk89q.minecraft.util.commands.Command; @@ -35,7 +37,7 @@ public ProtectionCommands(WorldGuardPlugin plugin) { } @Command(aliases = {"region", "regions", "rg"}, desc = "Region management commands") - @NestedCommand({RegionCommands.class, RegionMemberCommands.class}) + @NestedCommand({RegionCommands.class, MemberCommands.class}) public void region(CommandContext args, CommandSender sender) {} @Command(aliases = {"worldguard", "wg"}, desc = "WorldGuard commands") diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionListEntry.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionListEntry.java deleted file mode 100644 index 2620938a..00000000 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionListEntry.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit.commands; - -/** - * Used for /rg list. - */ -class RegionListEntry implements Comparable { - - private final String id; - private final int index; - boolean isOwner; - boolean isMember; - - public RegionListEntry(String id, int index) { - this.id = id; - this.index = index; - } - - @Override - public int compareTo(RegionListEntry o) { - if (isOwner != o.isOwner) { - return isOwner ? 1 : -1; - } - if (isMember != o.isMember) { - return isMember ? 1 : -1; - } - return id.compareTo(o.id); - } - - @Override - public String toString() { - if (isOwner) { - return (index + 1) + ". +" + id; - } else if (isMember) { - return (index + 1) + ". -" + id; - } else { - return (index + 1) + ". " + id; - } - } - -} \ No newline at end of file diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/MemberCommands.java similarity index 60% rename from src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java rename to src/main/java/com/sk89q/worldguard/bukkit/commands/region/MemberCommands.java index f9732f02..3ea62fc6 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionMemberCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/MemberCommands.java @@ -1,330 +1,234 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit.commands; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.domains.DefaultDomain; -import com.sk89q.worldguard.protection.util.DomainInputResolver; -import com.sk89q.worldguard.protection.util.DomainInputResolver.UserLocatorPolicy; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -// @TODO: A lot of code duplication here! Need to fix. - -public class RegionMemberCommands { - - private final WorldGuardPlugin plugin; - - public RegionMemberCommands(WorldGuardPlugin plugin) { - this.plugin = plugin; - } - - @Command(aliases = {"addmember", "addmember", "addmem", "am"}, - usage = " ", - flags = "nw:", - desc = "Add a member to a region", - min = 2) - public void addMember(CommandContext args, CommandSender sender) throws CommandException { - final World world; - Player player = null; - LocalPlayer localPlayer = null; - if (sender instanceof Player) { - player = (Player) sender; - localPlayer = plugin.wrapPlayer(player); - } - if (args.hasFlag('w')) { - world = plugin.matchWorld(sender, args.getFlag('w')); - } else { - if (player != null) { - world = player.getWorld(); - } else { - throw new CommandException("No world specified. Use -w ."); - } - } - - String id = args.getString(0); - - RegionManager manager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = manager.matchRegion(id); - - if (region == null) { - throw new CommandException("Could not find a region by that ID."); - } - - id = region.getId(); - - if (localPlayer != null) { - if (region.isOwner(localPlayer)) { - plugin.checkPermission(sender, "worldguard.region.addmember.own." + id.toLowerCase()); - } else if (region.isMember(localPlayer)) { - plugin.checkPermission(sender, "worldguard.region.addmember.member." + id.toLowerCase()); - } else { - plugin.checkPermission(sender, "worldguard.region.addmember." + id.toLowerCase()); - } - } - - // Resolve members asynchronously - DomainInputResolver resolver = new DomainInputResolver( - plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); - resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); - - // Then add it to the members - ListenableFuture future = Futures.transform( - plugin.getExecutorService().submit(resolver), - resolver.createAddAllFunction(region.getMembers())); - - AsyncCommandHelper.wrap(future, plugin, sender) - .formatUsing(region.getId(), world.getName()) - .registerWithSupervisor("Adding members to the region '%s' on '%s'") - .sendMessageAfterDelay("(Please wait... querying player names...)") - .thenRespondWith("Region '%s' updated with new members.", "Failed to add new members"); - } - - @Command(aliases = {"addowner", "addowner", "ao"}, - usage = " ", - flags = "nw:", - desc = "Add an owner to a region", - min = 2) - public void addOwner(CommandContext args, CommandSender sender) throws CommandException { - final World world; - Player player = null; - LocalPlayer localPlayer = null; - if (sender instanceof Player) { - player = (Player) sender; - localPlayer = plugin.wrapPlayer(player); - } - if (args.hasFlag('w')) { - world = plugin.matchWorld(sender, args.getFlag('w')); - } else { - if (player != null) { - world = player.getWorld(); - } else { - throw new CommandException("No world specified. Use -w ."); - } - } - - String id = args.getString(0); - - RegionManager manager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = manager.matchRegion(id); - - if (region == null) { - throw new CommandException("Could not find a region by that ID."); - } - - id = region.getId(); - - Boolean flag = region.getFlag(DefaultFlag.BUYABLE); - DefaultDomain owners = region.getOwners(); - if (localPlayer != null) { - if (flag != null && flag && owners != null && owners.size() == 0) { - if (!plugin.hasPermission(player, "worldguard.region.unlimited")) { - int maxRegionCount = plugin.getGlobalStateManager().get(world).getMaxRegionCount(player); - if (maxRegionCount >= 0 && manager.getRegionCountOfPlayer(localPlayer) - >= maxRegionCount) { - throw new CommandException("You already own the maximum allowed amount of regions."); - } - } - plugin.checkPermission(sender, "worldguard.region.addowner.unclaimed." + id.toLowerCase()); - } else { - if (region.isOwner(localPlayer)) { - plugin.checkPermission(sender, "worldguard.region.addowner.own." + id.toLowerCase()); - } else if (region.isMember(localPlayer)) { - plugin.checkPermission(sender, "worldguard.region.addowner.member." + id.toLowerCase()); - } else { - plugin.checkPermission(sender, "worldguard.region.addowner." + id.toLowerCase()); - } - } - } - - // Resolve owners asynchronously - DomainInputResolver resolver = new DomainInputResolver( - plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); - resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); - - // Then add it to the owners - ListenableFuture future = Futures.transform( - plugin.getExecutorService().submit(resolver), - resolver.createAddAllFunction(region.getOwners())); - - AsyncCommandHelper.wrap(future, plugin, sender) - .formatUsing(region.getId(), world.getName()) - .registerWithSupervisor("Adding owners to the region '%s' on '%s'") - .sendMessageAfterDelay("(Please wait... querying player names...)") - .thenRespondWith("Region '%s' updated with new owners.", "Failed to add new owners"); - } - - @Command(aliases = {"removemember", "remmember", "removemem", "remmem", "rm"}, - usage = " ", - flags = "naw:", - desc = "Remove an owner to a region", - min = 1) - public void removeMember(CommandContext args, CommandSender sender) throws CommandException { - final World world; - Player player = null; - LocalPlayer localPlayer = null; - if (sender instanceof Player) { - player = (Player) sender; - localPlayer = plugin.wrapPlayer(player); - } - if (args.hasFlag('w')) { - world = plugin.matchWorld(sender, args.getFlag('w')); - } else { - if (player != null) { - world = player.getWorld(); - } else { - throw new CommandException("No world specified. Use -w ."); - } - } - - String id = args.getString(0); - - RegionManager manager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = manager.matchRegion(id); - - if (region == null) { - throw new CommandException("Could not find a region by that ID."); - } - - id = region.getId(); - - if (localPlayer != null) { - if (region.isOwner(localPlayer)) { - plugin.checkPermission(sender, "worldguard.region.removemember.own." + id.toLowerCase()); - } else if (region.isMember(localPlayer)) { - plugin.checkPermission(sender, "worldguard.region.removemember.member." + id.toLowerCase()); - } else { - plugin.checkPermission(sender, "worldguard.region.removemember." + id.toLowerCase()); - } - } - - ListenableFuture future; - - if (args.hasFlag('a')) { - region.getMembers().removeAll(); - - future = Futures.immediateFuture(null); - } else { - if (args.argsLength() < 2) { - throw new CommandException("List some names to remove, or use -a to remove all."); - } - - // Resolve members asynchronously - DomainInputResolver resolver = new DomainInputResolver( - plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); - resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME); - - // Then remove it from the members - future = Futures.transform( - plugin.getExecutorService().submit(resolver), - resolver.createRemoveAllFunction(region.getMembers())); - } - - AsyncCommandHelper.wrap(future, plugin, sender) - .formatUsing(region.getId(), world.getName()) - .registerWithSupervisor("Removing members from the region '%s' on '%s'") - .sendMessageAfterDelay("(Please wait... querying player names...)") - .thenRespondWith("Region '%s' updated with members removed.", "Failed to remove members"); - } - - @Command(aliases = {"removeowner", "remowner", "ro"}, - usage = " ", - flags = "naw:", - desc = "Remove an owner to a region", - min = 1) - public void removeOwner(CommandContext args, - CommandSender sender) throws CommandException { - final World world; - Player player = null; - LocalPlayer localPlayer = null; - if (sender instanceof Player) { - player = (Player) sender; - localPlayer = plugin.wrapPlayer(player); - } - if (args.hasFlag('w')) { - world = plugin.matchWorld(sender, args.getFlag('w')); - } else { - if (player != null) { - world = player.getWorld(); - } else { - throw new CommandException("No world specified. Use -w ."); - } - } - - String id = args.getString(0); - - RegionManager manager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion region = manager.matchRegion(id); - - if (region == null) { - throw new CommandException("Could not find a region by that ID."); - } - - id = region.getId(); - - if (localPlayer != null) { - if (region.isOwner(localPlayer)) { - plugin.checkPermission(sender, "worldguard.region.removeowner.own." + id.toLowerCase()); - } else if (region.isMember(localPlayer)) { - plugin.checkPermission(sender, "worldguard.region.removeowner.member." + id.toLowerCase()); - } else { - plugin.checkPermission(sender, "worldguard.region.removeowner." + id.toLowerCase()); - } - } - - ListenableFuture future; - - if (args.hasFlag('a')) { - region.getOwners().removeAll(); - - future = Futures.immediateFuture(null); - } else { - if (args.argsLength() < 2) { - throw new CommandException("List some names to remove, or use -a to remove all."); - } - - // Resolve owners asynchronously - DomainInputResolver resolver = new DomainInputResolver( - plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); - resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME); - - // Then remove it from the owners - future = Futures.transform( - plugin.getExecutorService().submit(resolver), - resolver.createRemoveAllFunction(region.getOwners())); - } - - AsyncCommandHelper.wrap(future, plugin, sender) - .formatUsing(region.getId(), world.getName()) - .registerWithSupervisor("Removing owners from the region '%s' on '%s'") - .sendMessageAfterDelay("(Please wait... querying player names...)") - .thenRespondWith("Region '%s' updated with owners removed.", "Failed to remove owners"); - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands.region; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandPermissionsException; +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.commands.AsyncCommandHelper; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.util.DomainInputResolver; +import com.sk89q.worldguard.protection.util.DomainInputResolver.UserLocatorPolicy; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class MemberCommands extends RegionCommandsBase { + + private final WorldGuardPlugin plugin; + + public MemberCommands(WorldGuardPlugin plugin) { + this.plugin = plugin; + } + + @Command(aliases = {"addmember", "addmember", "addmem", "am"}, + usage = " ", + flags = "nw:", + desc = "Add a member to a region", + min = 2) + public void addMember(CommandContext args, CommandSender sender) throws CommandException { + World world = checkWorld(args, sender, 'w'); // Get the world + String id = args.getString(0); + RegionManager manager = checkRegionManager(plugin, world); + ProtectedRegion region = checkExistingRegion(manager, id, true); + + id = region.getId(); + + // Check permissions + if (!getPermissionModel(sender).mayAddMembers(region)) { + throw new CommandPermissionsException(); + } + + // Resolve members asynchronously + DomainInputResolver resolver = new DomainInputResolver( + plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); + resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); + + // Then add it to the members + ListenableFuture future = Futures.transform( + plugin.getExecutorService().submit(resolver), + resolver.createAddAllFunction(region.getMembers())); + + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Adding members to the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with new members.", "Failed to add new members"); + } + + @Command(aliases = {"addowner", "addowner", "ao"}, + usage = " ", + flags = "nw:", + desc = "Add an owner to a region", + min = 2) + public void addOwner(CommandContext args, CommandSender sender) throws CommandException { + World world = checkWorld(args, sender, 'w'); // Get the world + + Player player = null; + LocalPlayer localPlayer = null; + if (sender instanceof Player) { + player = (Player) sender; + localPlayer = plugin.wrapPlayer(player); + } + + String id = args.getString(0); + + RegionManager manager = checkRegionManager(plugin, world); + ProtectedRegion region = checkExistingRegion(manager, id, true); + + id = region.getId(); + + Boolean flag = region.getFlag(DefaultFlag.BUYABLE); + DefaultDomain owners = region.getOwners(); + + if (localPlayer != null) { + if (flag != null && flag && owners != null && owners.size() == 0) { + // TODO: Move this to an event + if (!plugin.hasPermission(player, "worldguard.region.unlimited")) { + int maxRegionCount = plugin.getGlobalStateManager().get(world).getMaxRegionCount(player); + if (maxRegionCount >= 0 && manager.getRegionCountOfPlayer(localPlayer) + >= maxRegionCount) { + throw new CommandException("You already own the maximum allowed amount of regions."); + } + } + plugin.checkPermission(sender, "worldguard.region.addowner.unclaimed." + id.toLowerCase()); + } else { + // Check permissions + if (!getPermissionModel(sender).mayAddOwners(region)) { + throw new CommandPermissionsException(); + } + } + } + + // Resolve owners asynchronously + DomainInputResolver resolver = new DomainInputResolver( + plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); + resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); + + // Then add it to the owners + ListenableFuture future = Futures.transform( + plugin.getExecutorService().submit(resolver), + resolver.createAddAllFunction(region.getOwners())); + + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Adding owners to the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with new owners.", "Failed to add new owners"); + } + + @Command(aliases = {"removemember", "remmember", "removemem", "remmem", "rm"}, + usage = " ", + flags = "naw:", + desc = "Remove an owner to a region", + min = 1) + public void removeMember(CommandContext args, CommandSender sender) throws CommandException { + World world = checkWorld(args, sender, 'w'); // Get the world + String id = args.getString(0); + RegionManager manager = checkRegionManager(plugin, world); + ProtectedRegion region = checkExistingRegion(manager, id, true); + + // Check permissions + if (!getPermissionModel(sender).mayRemoveMembers(region)) { + throw new CommandPermissionsException(); + } + + ListenableFuture future; + + if (args.hasFlag('a')) { + region.getMembers().removeAll(); + + future = Futures.immediateFuture(null); + } else { + if (args.argsLength() < 2) { + throw new CommandException("List some names to remove, or use -a to remove all."); + } + + // Resolve members asynchronously + DomainInputResolver resolver = new DomainInputResolver( + plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); + resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME); + + // Then remove it from the members + future = Futures.transform( + plugin.getExecutorService().submit(resolver), + resolver.createRemoveAllFunction(region.getMembers())); + } + + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Removing members from the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with members removed.", "Failed to remove members"); + } + + @Command(aliases = {"removeowner", "remowner", "ro"}, + usage = " ", + flags = "naw:", + desc = "Remove an owner to a region", + min = 1) + public void removeOwner(CommandContext args, CommandSender sender) throws CommandException { + World world = checkWorld(args, sender, 'w'); // Get the world + String id = args.getString(0); + RegionManager manager = checkRegionManager(plugin, world); + ProtectedRegion region = checkExistingRegion(manager, id, true); + + // Check permissions + if (!getPermissionModel(sender).mayRemoveOwners(region)) { + throw new CommandPermissionsException(); + } + + ListenableFuture future; + + if (args.hasFlag('a')) { + region.getOwners().removeAll(); + + future = Futures.immediateFuture(null); + } else { + if (args.argsLength() < 2) { + throw new CommandException("List some names to remove, or use -a to remove all."); + } + + // Resolve owners asynchronously + DomainInputResolver resolver = new DomainInputResolver( + plugin.getProfileService(), args.getParsedPaddedSlice(1, 0)); + resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME); + + // Then remove it from the owners + future = Futures.transform( + plugin.getExecutorService().submit(resolver), + resolver.createRemoveAllFunction(region.getOwners())); + } + + AsyncCommandHelper.wrap(future, plugin, sender) + .formatUsing(region.getId(), world.getName()) + .registerWithSupervisor("Removing owners from the region '%s' on '%s'") + .sendMessageAfterDelay("(Please wait... querying player names...)") + .thenRespondWith("Region '%s' updated with owners removed.", "Failed to remove owners"); + } +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java similarity index 53% rename from src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java rename to src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java index 265f448a..3003196c 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java @@ -1,1311 +1,956 @@ -/* - * WorldGuard, a suite of tools for Minecraft - * Copyright (C) sk89q - * Copyright (C) WorldGuard team and contributors - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldguard.bukkit.commands; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandPermissionsException; -import com.sk89q.worldedit.BlockVector; -import com.sk89q.worldedit.Location; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.bukkit.BukkitUtil; -import com.sk89q.worldedit.bukkit.WorldEditPlugin; -import com.sk89q.worldedit.bukkit.selections.CuboidSelection; -import com.sk89q.worldedit.bukkit.selections.Polygonal2DSelection; -import com.sk89q.worldedit.bukkit.selections.Selection; -import com.sk89q.worldguard.LocalPlayer; -import com.sk89q.worldguard.bukkit.WorldConfiguration; -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; -import com.sk89q.worldguard.bukkit.commands.task.RegionAdd; -import com.sk89q.worldguard.bukkit.commands.task.RegionManagerReload; -import com.sk89q.worldguard.bukkit.commands.task.RegionmanagerSave; -import com.sk89q.worldguard.bukkit.permission.RegionPermissionModel; -import com.sk89q.worldguard.bukkit.util.LoggerToChatHandler; -import com.sk89q.worldguard.protection.ApplicableRegionSet; -import com.sk89q.worldguard.protection.flags.DefaultFlag; -import com.sk89q.worldguard.protection.flags.Flag; -import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; -import com.sk89q.worldguard.protection.flags.RegionGroup; -import com.sk89q.worldguard.protection.flags.RegionGroupFlag; -import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; -import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion; -import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; -import com.sk89q.worldguard.protection.util.DomainInputResolver.UserLocatorPolicy; -import com.sk89q.worldguard.protection.util.migrator.MigrationException; -import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.World; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Implements the /region commands for WorldGuard. - */ -public final class RegionCommands { - - private final WorldGuardPlugin plugin; - - public RegionCommands(WorldGuardPlugin plugin) { - this.plugin = plugin; - } - - /** - * Get the permission model to lookup permissions. - * - * @param sender the sender - * @return the permission model - */ - private static RegionPermissionModel getPermissionModel(CommandSender sender) { - return new RegionPermissionModel(WorldGuardPlugin.inst(), sender); - } - - /** - * Gets the world from the given flag, or falling back to the the current player - * if the sender is a player, otherwise reporting an error. - * - * @param args the arguments - * @param sender the sender - * @param flag the flag (such as 'w') - * @return a world - * @throws CommandException on error - */ - private static World getWorld(CommandContext args, CommandSender sender, char flag) throws CommandException { - if (args.hasFlag(flag)) { - return WorldGuardPlugin.inst().matchWorld(sender, args.getFlag(flag)); - } else { - if (sender instanceof Player) { - return WorldGuardPlugin.inst().checkPlayer(sender).getWorld(); - } else { - throw new CommandException("Please specify " + - "the world with -" + flag + " world_name."); - } - } - } - - /** - * Validate a region ID. - * - * @param id the id - * @param allowGlobal whether __global__ is allowed - * @return the id given - * @throws CommandException thrown on an error - */ - private static String validateRegionId(String id, boolean allowGlobal) - throws CommandException { - if (!ProtectedRegion.isValidId(id)) { - throw new CommandException( - "The region name of '" + id + "' contains characters that are not allowed."); - } - - if (!allowGlobal && id.equalsIgnoreCase("__global__")) { // Sorry, no global - throw new CommandException( - "Sorry, you can't use __global__ here."); - } - - return id; - } - - /** - * Get a protected region by a given name, otherwise throw a - * {@link CommandException}. - * - *

This also validates the region ID.

- * - * @param regionManager the region manager - * @param id the name to search - * @param allowGlobal true to allow selecting __global__ - * @throws CommandException thrown if no region is found by the given name - */ - private static ProtectedRegion findExistingRegion(RegionManager regionManager, String id, boolean allowGlobal) throws CommandException { - // Validate the id - validateRegionId(id, allowGlobal); - - ProtectedRegion region = regionManager.getRegion(id); - - // No region found! - if (region == null) { - // But we want a __global__, so let's create one - if (id.equalsIgnoreCase("__global__")) { - region = new GlobalProtectedRegion(id); - regionManager.addRegion(region); - return region; - } - - throw new CommandException( - "No region could be found with the name of '" + id + "'."); - } - - return region; - } - - - /** - * Get the region at the player's location, if possible. - * - *

If the player is standing in several regions, an error will be raised - * and a list of regions will be provided.

- * - * @param regionManager the region manager - * @param player the player - * @return a region - * @throws CommandException thrown if no region was found - */ - private static ProtectedRegion findRegionStandingIn(RegionManager regionManager, Player player) throws CommandException { - return findRegionStandingIn(regionManager, player, false); - } - - /** - * Get the region at the player's location, if possible. - * - *

If the player is standing in several regions, an error will be raised - * and a list of regions will be provided.

- * - *

If the player is not standing in any regions, the global region will - * returned if allowGlobal is true and it exists.

- * - * @param regionManager the region manager - * @param player the player - * @param allowGlobal whether to search for a global region if no others are found - * @return a region - * @throws CommandException thrown if no region was found - */ - private static ProtectedRegion findRegionStandingIn(RegionManager regionManager, Player player, boolean allowGlobal) throws CommandException { - ApplicableRegionSet set = regionManager.getApplicableRegions( - player.getLocation()); - - if (set.size() == 0) { - if (allowGlobal) { - ProtectedRegion global = findExistingRegion(regionManager, "__global__", true); - player.sendMessage(ChatColor.GRAY + "You're not standing in any " + - "regions. Using the global region for this world instead."); - return global; - } - throw new CommandException( - "You're not standing in a region." + - "Specify an ID if you want to select a specific region."); - } else if (set.size() > 1) { - StringBuilder builder = new StringBuilder(); - boolean first = true; - - for (ProtectedRegion region : set) { - if (!first) { - builder.append(", "); - } - first = false; - builder.append(region.getId()); - } - - throw new CommandException( - "You're standing in several regions, and " + - "WorldGuard is not sure what you want.\nYou're in: " + - builder.toString()); - } - - return set.iterator().next(); - } - - /** - * Get a WorldEdit selection for a player, or emit an exception if there is none - * available. - * - * @param player the player - * @return the selection - * @throws CommandException thrown on an error - */ - private static Selection getSelection(Player player) throws CommandException { - WorldEditPlugin worldEdit = WorldGuardPlugin.inst().getWorldEdit(); - Selection selection = worldEdit.getSelection(player); - - if (selection == null) { - throw new CommandException( - "Please select an area first. " + - "Use WorldEdit to make a selection! " + - "(wiki: http://wiki.sk89q.com/wiki/WorldEdit)."); - } - - return selection; - } - - /** - * Create a {@link ProtectedRegion} from the player's selection. - * - * @param player the player - * @param id the ID of the new region - * @return a new region - * @throws CommandException thrown on an error - */ - private static ProtectedRegion createRegionFromSelection(Player player, String id) throws CommandException { - Selection selection = getSelection(player); - - // Detect the type of region from WorldEdit - if (selection instanceof Polygonal2DSelection) { - Polygonal2DSelection polySel = (Polygonal2DSelection) selection; - int minY = polySel.getNativeMinimumPoint().getBlockY(); - int maxY = polySel.getNativeMaximumPoint().getBlockY(); - return new ProtectedPolygonalRegion(id, polySel.getNativePoints(), minY, maxY); - } else if (selection instanceof CuboidSelection) { - BlockVector min = selection.getNativeMinimumPoint().toBlockVector(); - BlockVector max = selection.getNativeMaximumPoint().toBlockVector(); - return new ProtectedCuboidRegion(id, min, max); - } else { - throw new CommandException( - "Sorry, you can only use cuboids and polygons for WorldGuard regions."); - } - } - - /** - * Set a player's selection to a given region. - * - * @param player the player - * @param region the region - * @throws CommandException thrown on a command error - */ - private static void setPlayerSelection(Player player, ProtectedRegion region) - throws CommandException { - WorldEditPlugin worldEdit = WorldGuardPlugin.inst().getWorldEdit(); - - World world = player.getWorld(); - - // Set selection - if (region instanceof ProtectedCuboidRegion) { - ProtectedCuboidRegion cuboid = (ProtectedCuboidRegion) region; - Vector pt1 = cuboid.getMinimumPoint(); - Vector pt2 = cuboid.getMaximumPoint(); - CuboidSelection selection = new CuboidSelection(world, pt1, pt2); - worldEdit.setSelection(player, selection); - player.sendMessage(ChatColor.YELLOW + "Region selected as a cuboid."); - - } else if (region instanceof ProtectedPolygonalRegion) { - ProtectedPolygonalRegion poly2d = (ProtectedPolygonalRegion) region; - Polygonal2DSelection selection = new Polygonal2DSelection( - world, poly2d.getPoints(), - poly2d.getMinimumPoint().getBlockY(), - poly2d.getMaximumPoint().getBlockY() ); - worldEdit.setSelection(player, selection); - player.sendMessage(ChatColor.YELLOW + "Region selected as a polygon."); - - } else if (region instanceof GlobalProtectedRegion) { - throw new CommandException( - "Can't select global regions! " + - "That would cover the entire world."); - - } else { - throw new CommandException("Unknown region type: " + - region.getClass().getCanonicalName()); - } - } - - /** - * Utility method to set a flag. - * - * @param region the region - * @param flag the flag - * @param sender the sender - * @param value the value - * @throws InvalidFlagFormat thrown if the value is invalid - */ - private static void setFlag(ProtectedRegion region, Flag flag, CommandSender sender, String value) throws InvalidFlagFormat { - region.setFlag(flag, flag.parseInput(WorldGuardPlugin.inst(), sender, value)); - } - - /** - * Defines a new region. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"define", "def", "d", "create"}, - usage = " [ [ []]]", - flags = "n", - desc = "Defines a region", - min = 1) - public void define(CommandContext args, CommandSender sender) throws CommandException { - Player player = plugin.checkPlayer(sender); - - // Check permissions - if (!getPermissionModel(sender).mayDefine()) { - throw new CommandPermissionsException(); - } - - // Get and validate the region ID - String id = validateRegionId(args.getString(0), false); - - // Can't replace regions with this command - final RegionManager manager = plugin.getRegionContainer().get(player.getWorld()); - - if (manager != null) { - if (manager.hasRegion(id)) { - throw new CommandException("A region with that ID already exists. To change the shape, use /region redefine " + id); - } - - // Make a region from the user's selection - final ProtectedRegion region = createRegionFromSelection(player, id); - - // Issue a warning about height - int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); - if (height <= 2) { - sender.sendMessage(ChatColor.GRAY + "(Warning: The height of the region was " + (height + 1) + " block(s).)"); - } - - // Hint - if (manager.getRegions().size() <= 2) { - sender.sendMessage(ChatColor.GRAY + - "(This region is NOW PROTECTED from modification from others. " + - "Don't want that? Use " + - ChatColor.AQUA + "/rg flag " + id + " passthrough allow" + - ChatColor.GRAY + ")"); - } - - RegionAdd task = new RegionAdd(plugin, manager, region); - - // Add the list of region owners - if (args.argsLength() > 1) { - task.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); - task.setOwnersInput(args.getSlice(2)); - } - - ListenableFuture future = plugin.getExecutorService().submit(task); - - AsyncCommandHelper.wrap(future, plugin, player) - .formatUsing(id) - .registerWithSupervisor("Adding the region '%s'...") - .sendMessageAfterDelay("(Please wait... adding '%s'...)") - .thenRespondWith( - "A new region has been made named '%s'.", - "Failed to add the region '%s'"); - } else { - throw new CommandException("Either region support is disabled or region data failed to load in the target world."); - } - } - - /** - * Re-defines a region with a new selection. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"redefine", "update", "move"}, - usage = "", - desc = "Re-defines the shape of a region", - min = 1, max = 1) - public void redefine(CommandContext args, CommandSender sender) throws CommandException { - Player player = plugin.checkPlayer(sender); - World world = player.getWorld(); - - // Get and validate the region ID - String id = validateRegionId(args.getString(0), false); - - // Lookup the existing region - RegionManager manager = plugin.getRegionContainer().get(world); - - if (manager != null) { - ProtectedRegion existing = findExistingRegion(manager, id, false); - - // Check permissions - if (!getPermissionModel(sender).mayRedefine(existing)) { - throw new CommandPermissionsException(); - } - - // Make a region from the user's selection - ProtectedRegion region = createRegionFromSelection(player, id); - - // Copy details from the old region to the new one - region.setMembers(existing.getMembers()); - region.setOwners(existing.getOwners()); - region.setFlags(existing.getFlags()); - region.setPriority(existing.getPriority()); - try { - region.setParent(existing.getParent()); - } catch (CircularInheritanceException ignore) { - // This should not be thrown - } - - // Issue a warning about height - int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); - if (height <= 2) { - sender.sendMessage(ChatColor.GOLD + - "(Warning: The height of the region was " + (height + 1) + " block(s).)"); - } - - RegionAdd task = new RegionAdd(plugin, manager, region); - - ListenableFuture future = plugin.getExecutorService().submit(task); - - AsyncCommandHelper.wrap(future, plugin, player) - .formatUsing(id) - .registerWithSupervisor("Updating the region '%s'...") - .sendMessageAfterDelay("(Please wait... updating '%s'...)") - .thenRespondWith( - "Region '%s' has been updated with a new area.", - "Failed to update the region '%s'"); - } else { - throw new CommandException("Either region support is disabled or region data failed to load in the target world."); - } - } - - /** - * Claiming command for users. - * - *

This command is a joke and it needs to be rewritten. It was contributed - * code :(

- * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"claim"}, - usage = " [ [ []]]", - desc = "Claim a region", - min = 1) - public void claim(CommandContext args, CommandSender sender) throws CommandException { - Player player = plugin.checkPlayer(sender); - LocalPlayer localPlayer = plugin.wrapPlayer(player); - RegionPermissionModel permModel = getPermissionModel(sender); - - // Check permissions - if (!permModel.mayClaim()) { - throw new CommandPermissionsException(); - } - - // Get and validate the region ID - String id = validateRegionId(args.getString(0), false); - - // Can't replace existing regions - RegionManager manager = plugin.getGlobalRegionManager().get(player.getWorld()); - - if (manager != null) { - if (manager.hasRegion(id)) { - throw new CommandException("That region already exists. Please choose a different name."); - } - - // Make a region from the user's selection - ProtectedRegion region = createRegionFromSelection(player, id); - - // Get the list of region owners - /*if (args.argsLength() > 1) { - region.setOwners(RegionDBUtil.parseDomainString(args.getSlice(1), 1)); - }*/ - - WorldConfiguration wcfg = plugin.getGlobalStateManager().get(player.getWorld()); - - // Check whether the player has created too many regions - if (!permModel.mayClaimRegionsUnbounded()) { - int maxRegionCount = wcfg.getMaxRegionCount(player); - if (maxRegionCount >= 0 - && manager.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) { - throw new CommandException( - "You own too many regions, delete one first to claim a new one."); - } - } - - ProtectedRegion existing = manager.getRegion(id); - - // Check for an existing region - if (existing != null) { - if (!existing.getOwners().contains(localPlayer)) { - throw new CommandException( - "This region already exists and you don't own it."); - } - } - - // We have to check whether this region violates the space of any other reion - ApplicableRegionSet regions = manager.getApplicableRegions(region); - - // Check if this region overlaps any other region - if (regions.size() > 0) { - if (!regions.isOwnerOfAll(localPlayer)) { - throw new CommandException("This region overlaps with someone else's region."); - } - } else { - if (wcfg.claimOnlyInsideExistingRegions) { - throw new CommandException("You may only claim regions inside " + - "existing regions that you or your group own."); - } - } - - // Check claim volume - if (!permModel.mayClaimRegionsUnbounded()) { - if (region.volume() > wcfg.maxClaimVolume) { - player.sendMessage(ChatColor.RED + "This region is too large to claim."); - player.sendMessage(ChatColor.RED + - "Max. volume: " + wcfg.maxClaimVolume + ", your volume: " + region.volume()); - return; - } - } - - region.getOwners().addPlayer(player.getName()); - - RegionAdd task = new RegionAdd(plugin, manager, region); - ListenableFuture future = plugin.getExecutorService().submit(task); - - AsyncCommandHelper.wrap(future, plugin, player) - .formatUsing(id) - .registerWithSupervisor("Claiming the region '%s'...") - .sendMessageAfterDelay("(Please wait... claiming '%s'...)") - .thenRespondWith( - "A new region has been claimed named '%s'.", - "Failed to claim the region '%s'"); - } else { - throw new CommandException("Either region support is disabled or region data failed to load in the target world."); - } - } - - /** - * Get a WorldEdit selection from a region. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"select", "sel", "s"}, - usage = "[id]", - desc = "Load a region as a WorldEdit selection", - min = 0, max = 1) - public void select(CommandContext args, CommandSender sender) throws CommandException { - Player player = plugin.checkPlayer(sender); - World world = player.getWorld(); - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion existing; - - // If no arguments were given, get the region that the player is inside - if (args.argsLength() == 0) { - existing = findRegionStandingIn(regionManager, player); - } else { - existing = findExistingRegion(regionManager, args.getString(0), false); - } - - // Check permissions - if (!getPermissionModel(sender).maySelect(existing)) { - throw new CommandPermissionsException(); - } - - // Select - setPlayerSelection(player, existing); - } - - /** - * Get information about a region. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"info", "i"}, - usage = "[id]", - flags = "usw:", - desc = "Get information about a region", - min = 0, max = 1) - public void info(CommandContext args, CommandSender sender) throws CommandException { - World world = getWorld(args, sender, 'w'); // Get the world - RegionPermissionModel permModel = getPermissionModel(sender); - - // Lookup the existing region - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion existing; - - if (args.argsLength() == 0) { // Get region from where the player is - if (!(sender instanceof Player)) { - throw new CommandException("Please specify " + - "the region with /region info -w world_name region_name."); - } - - existing = findRegionStandingIn(regionManager, (Player) sender, true); - } else { // Get region from the ID - existing = findExistingRegion(regionManager, args.getString(0), true); - } - - // Check permissions - if (!permModel.mayLookup(existing)) { - throw new CommandPermissionsException(); - } - - // Let the player select the region - if (args.hasFlag('s')) { - // Check permissions - if (!permModel.maySelect(existing)) { - throw new CommandPermissionsException(); - } - - setPlayerSelection(plugin.checkPlayer(sender), existing); - } - - // Print region information - RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, args.hasFlag('u') ? null : plugin.getProfileCache()); - ListenableFuture future = Futures.transform( - plugin.getExecutorService().submit(printout), - CommandUtils.messageFunction(sender)); - - // If it takes too long... - FutureProgressListener.addProgressListener( - future, sender, "(Please wait... fetching region information...)"); - - // Send a response message - Futures.addCallback(future, - new MessageFutureCallback.Builder(plugin, sender) - .onFailure("Failed to fetch region information") - .build()); - } - - /** - * List regions. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"list"}, - usage = "[page]", - desc = "Get a list of regions", - flags = "p:w:", - max = 1) - public void list(CommandContext args, CommandSender sender) throws CommandException { - World world = getWorld(args, sender, 'w'); // Get the world - String ownedBy; - - // Get page - int page = args.getInteger(0, 1) - 1; - if (page < 0) { - page = 0; - } - - // -p flag to lookup a player's regions - if (args.hasFlag('p')) { - ownedBy = args.getFlag('p'); - } else { - ownedBy = null; // List all regions - } - - // Check permissions - if (!getPermissionModel(sender).mayList(ownedBy)) { - ownedBy = sender.getName(); // assume they only want their own - if (!getPermissionModel(sender).mayList(ownedBy)) { - throw new CommandPermissionsException(); - } - } - - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - Map regions = mgr.getRegions(); - - // Build a list of regions to show - List entries = new ArrayList(); - - int index = 0; - for (String id : regions.keySet()) { - RegionListEntry entry = new RegionListEntry(id, index++); - - // Filtering by owner? - if (ownedBy != null) { - entry.isOwner = regions.get(id).isOwner(ownedBy); - entry.isMember = regions.get(id).isMember(ownedBy); - - if (!entry.isOwner && !entry.isMember) { - continue; // Skip - } - } - - entries.add(entry); - } - - Collections.sort(entries); - - final int totalSize = entries.size(); - final int pageSize = 10; - final int pages = (int) Math.ceil(totalSize / (float) pageSize); - - sender.sendMessage(ChatColor.RED - + (ownedBy == null ? "Regions (page " : "Regions for " + ownedBy + " (page ") - + (page + 1) + " of " + pages + "):"); - - if (page < pages) { - // Print - for (int i = page * pageSize; i < page * pageSize + pageSize; i++) { - if (i >= totalSize) { - break; - } - - sender.sendMessage(ChatColor.YELLOW.toString() + entries.get(i)); - } - } - } - - /** - * Set a flag. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"flag", "f"}, - usage = " [-w world] [-g group] [value]", - flags = "g:w:", - desc = "Set flags", - min = 2) - public void flag(CommandContext args, CommandSender sender) throws CommandException { - World world = getWorld(args, sender, 'w'); // Get the world - String flagName = args.getString(1); - String value = args.argsLength() >= 3 ? args.getJoinedStrings(2) : null; - RegionGroup groupValue = null; - RegionPermissionModel permModel = getPermissionModel(sender); - - // Lookup the existing region - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion existing = findExistingRegion(regionManager, - args.getString(0), true); - - // Check permissions - if (!permModel.maySetFlag(existing)) { - throw new CommandPermissionsException(); - } - - Flag foundFlag = DefaultFlag.fuzzyMatchFlag(flagName); - - // We didn't find the flag, so let's print a list of flags that the user - // can use, and do nothing afterwards - if (foundFlag == null) { - StringBuilder list = new StringBuilder(); - - // Need to build a list - for (Flag flag : DefaultFlag.getFlags()) { - // Can the user set this flag? - if (!permModel.maySetFlag(existing, flag)) { - continue; - } - - if (list.length() > 0) { - list.append(", "); - } - - list.append(flag.getName()); - } - - sender.sendMessage(ChatColor.RED + "Unknown flag specified: " + flagName); - sender.sendMessage(ChatColor.RED + "Available " + - "flags: " + list); - - return; - } - - // Also make sure that we can use this flag - // This permission is confusing and probably should be replaced, but - // but not here -- in the model - if (!permModel.maySetFlag(existing, foundFlag)) { - throw new CommandPermissionsException(); - } - - // -g for group flag - if (args.hasFlag('g')) { - String group = args.getFlag('g'); - RegionGroupFlag groupFlag = foundFlag.getRegionGroupFlag(); - - if (groupFlag == null) { - throw new CommandException("Region flag '" + foundFlag.getName() - + "' does not have a group flag!"); - } - - // Parse the [-g group] separately so entire command can abort if parsing - // the [value] part throws an error. - try { - groupValue = groupFlag.parseInput(plugin, sender, group); - } catch (InvalidFlagFormat e) { - throw new CommandException(e.getMessage()); - } - - } - - // Set the flag value if a value was set - if (value != null) { - // Set the flag if [value] was given even if [-g group] was given as well - try { - setFlag(existing, foundFlag, sender, value); - } catch (InvalidFlagFormat e) { - throw new CommandException(e.getMessage()); - } - - sender.sendMessage(ChatColor.YELLOW - + "Region flag " + foundFlag.getName() + " set on '" + - existing.getId() + "' to '" + value + "'."); - - // No value? Clear the flag, if -g isn't specified - } else if (!args.hasFlag('g')) { - // Clear the flag only if neither [value] nor [-g group] was given - existing.setFlag(foundFlag, null); - - // Also clear the associated group flag if one exists - RegionGroupFlag groupFlag = foundFlag.getRegionGroupFlag(); - if (groupFlag != null) { - existing.setFlag(groupFlag, null); - } - - sender.sendMessage(ChatColor.YELLOW - + "Region flag " + foundFlag.getName() + " removed from '" + - existing.getId() + "'. (Any -g(roups) were also removed.)"); - } - - // Now set the group - if (groupValue != null) { - RegionGroupFlag groupFlag = foundFlag.getRegionGroupFlag(); - - // If group set to the default, then clear the group flag - if (groupValue == groupFlag.getDefault()) { - existing.setFlag(groupFlag, null); - sender.sendMessage(ChatColor.YELLOW - + "Region group flag for '" + foundFlag.getName() + "' reset to " + - "default."); - } else { - existing.setFlag(groupFlag, groupValue); - sender.sendMessage(ChatColor.YELLOW - + "Region group flag for '" + foundFlag.getName() + "' set."); - } - } - - // Print region information - RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, null); - printout.append(ChatColor.GRAY); - printout.append("(Current flags: "); - printout.appendFlagsList(false); - printout.append(")"); - printout.send(sender); - } - - /** - * Set the priority of a region. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"setpriority", "priority", "pri"}, - usage = " ", - flags = "w:", - desc = "Set the priority of a region", - min = 2, max = 2) - public void setPriority(CommandContext args, CommandSender sender) - throws CommandException { - World world = getWorld(args, sender, 'w'); // Get the world - int priority = args.getInteger(1); - - // Lookup the existing region - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion existing = findExistingRegion(regionManager, - args.getString(0), false); - - // Check permissions - if (!getPermissionModel(sender).maySetPriority(existing)) { - throw new CommandPermissionsException(); - } - - existing.setPriority(priority); - - sender.sendMessage(ChatColor.YELLOW - + "Priority of '" + existing.getId() + "' set to " - + priority + " (higher numbers override)."); - } - - /** - * Set the parent of a region. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"setparent", "parent", "par"}, - usage = " [parent-id]", - flags = "w:", - desc = "Set the parent of a region", - min = 1, max = 2) - public void setParent(CommandContext args, CommandSender sender) throws CommandException { - World world = getWorld(args, sender, 'w'); // Get the world - ProtectedRegion parent; - ProtectedRegion child; - - // Lookup the existing region - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - - // Get parent and child - child = findExistingRegion(regionManager, args.getString(0), false); - if (args.argsLength() == 2) { - parent = findExistingRegion(regionManager, args.getString(1), false); - } else { - parent = null; - } - - // Check permissions - if (!getPermissionModel(sender).maySetParent(child, parent)) { - throw new CommandPermissionsException(); - } - - try { - child.setParent(parent); - } catch (CircularInheritanceException e) { - // Tell the user what's wrong - RegionPrintoutBuilder printout = new RegionPrintoutBuilder(parent, null); - printout.append(ChatColor.RED); - printout.append("Uh oh! Setting '" + parent.getId() + "' to be the parent " + - "of '" + child.getId() + "' would cause circular inheritance.\n"); - printout.append(ChatColor.GRAY); - printout.append("(Current inheritance on '" + parent.getId() + "':\n"); - printout.appendParentTree(true); - printout.append(ChatColor.GRAY); - printout.append(")"); - printout.send(sender); - return; - } - - // Tell the user the current inheritance - RegionPrintoutBuilder printout = new RegionPrintoutBuilder(child, null); - printout.append(ChatColor.YELLOW); - printout.append("Inheritance set for region '" + child.getId() + "'.\n"); - if (parent != null) { - printout.append(ChatColor.GRAY); - printout.append("(Current inheritance:\n"); - printout.appendParentTree(true); - printout.append(ChatColor.GRAY); - printout.append(")"); - } - printout.send(sender); - } - - /** - * Remove a region. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"remove", "delete", "del", "rem"}, - usage = "", - flags = "w:", - desc = "Remove a region", - min = 1, max = 1) - public void remove(CommandContext args, CommandSender sender) throws CommandException { - World world = getWorld(args, sender, 'w'); // Get the world - - // Lookup the existing region - RegionManager regionManager = plugin.getGlobalRegionManager().get(world); - ProtectedRegion existing = findExistingRegion(regionManager, - args.getString(0), true); - - // Check permissions - if (!getPermissionModel(sender).mayDelete(existing)) { - throw new CommandPermissionsException(); - } - - regionManager.removeRegion(existing.getId()); - - sender.sendMessage(ChatColor.YELLOW + "Region '" + existing.getId() + "' removed."); - } - - /** - * Reload the region database. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"load", "reload"}, - usage = "[world]", - desc = "Reload regions from file", - flags = "w:") - public void load(CommandContext args, final CommandSender sender) throws CommandException { - World world = null; - try { - world = getWorld(args, sender, 'w'); // Get the world - } catch (CommandException e) { - // assume the user wants to reload all worlds - } - - // Check permissions - if (!getPermissionModel(sender).mayForceLoadRegions()) { - throw new CommandPermissionsException(); - } - - if (world != null) { - RegionManager manager = plugin.getGlobalRegionManager().get(world); - - if (manager == null) { - throw new CommandException("No region manager exists for world '" + world.getName() + "'."); - } - - ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerReload(manager)); - - AsyncCommandHelper.wrap(future, plugin, sender) - .forRegionDataLoad(world, false); - } else { - // Load regions for all worlds - List managers = new ArrayList(); - - for (World w : Bukkit.getServer().getWorlds()) { - RegionManager manager = plugin.getGlobalRegionManager().get(w); - if (manager != null) { - managers.add(manager); - } - } - - ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerReload(managers)); - - AsyncCommandHelper.wrap(future, plugin, sender) - .registerWithSupervisor("Loading regions for all worlds") - .sendMessageAfterDelay("(Please wait... loading region data for all worlds...)") - .thenRespondWith( - "Successfully load the region data for all worlds.", - "Failed to load regions for all worlds"); - } - } - - /** - * Re-save the region database. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"save", "write"}, - usage = "[world]", - desc = "Re-save regions to file", - flags = "w:") - public void save(CommandContext args, final CommandSender sender) throws CommandException { - World world = null; - try { - world = getWorld(args, sender, 'w'); // Get the world - } catch (CommandException e) { - // assume user wants to save all worlds - } - - // Check permissions - if (!getPermissionModel(sender).mayForceSaveRegions()) { - throw new CommandPermissionsException(); - } - - if (world != null) { - RegionManager manager = plugin.getGlobalRegionManager().get(world); - - if (manager == null) { - throw new CommandException("No region manager exists for world '" + world.getName() + "'."); - } - - ListenableFuture future = plugin.getExecutorService().submit(new RegionmanagerSave(manager)); - - AsyncCommandHelper.wrap(future, plugin, sender) - .forRegionDataSave(world, false); - } else { - // Save for all worlds - List managers = new ArrayList(); - - for (World w : Bukkit.getServer().getWorlds()) { - RegionManager manager = plugin.getGlobalRegionManager().get(w); - if (manager != null) { - managers.add(manager); - } - } - - ListenableFuture future = plugin.getExecutorService().submit(new RegionmanagerSave(managers)); - - AsyncCommandHelper.wrap(future, plugin, sender) - .registerWithSupervisor("Saving regions for all worlds") - .sendMessageAfterDelay("(Please wait... saving region data for all worlds...)") - .thenRespondWith( - "Successfully saved the region data for all worlds.", - "Failed to save regions for all worlds"); - } - } - - /** - * Migrate the region database. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"migratedb"}, usage = " ", - desc = "Migrate from one Protection Database to another.", min = 2, max = 2) - public void migrateDB(CommandContext args, CommandSender sender) throws CommandException { - // Check permissions - if (!getPermissionModel(sender).mayMigrateRegionStore()) { - throw new CommandPermissionsException(); - } - - /* - String from = args.getString(0).toLowerCase().trim(); - String to = args.getString(1).toLowerCase().trim(); - - if (from.equals(to)) { - throw new CommandException("Will not migrate with common source and target."); - } - - Map> migrators = - AbstractDatabaseMigrator.getMigrators(); - MigratorKey key = new MigratorKey(from,to); - - if (!migrators.containsKey(key)) { - throw new CommandException("No migrator found for that combination and direction."); - } - - long lastRequest = 10000000; - if (this.migrateDBRequestDate != null) { - lastRequest = new Date().getTime() - this.migrateDBRequestDate.getTime(); - } - if (this.migrateDBRequest == null || lastRequest > 60000) { - this.migrateDBRequest = key; - this.migrateDBRequestDate = new Date(); - - throw new CommandException("This command is potentially dangerous.\n" + - "Please ensure you have made a backup of your data, and then re-enter the command exactly to procede."); - } - - Class cls = migrators.get(key); - - try { - AbstractDatabaseMigrator migrator = cls.getConstructor(WorldGuardPlugin.class).newInstance(plugin); - - migrator.migrate(); - } catch (IllegalArgumentException ignore) { - } catch (SecurityException ignore) { - } catch (InstantiationException ignore) { - } catch (IllegalAccessException ignore) { - } catch (InvocationTargetException ignore) { - } catch (NoSuchMethodException ignore) { - } catch (MigrationException e) { - throw new CommandException("Error migrating database: " + e.getMessage()); - } - - sender.sendMessage(ChatColor.YELLOW + "Regions have been migrated successfully.\n" + - "If you wish to use the destination format as your new backend, please update your config and reload WorldGuard."); - */ - } - - /** - * Migrate the region databases to use UUIDs rather than name. - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"migrateuuid"}, - desc = "Migrate loaded databases to use UUIDs", max = 0) - public void migrateUuid(CommandContext args, CommandSender sender) throws CommandException { - // Check permissions - if (!getPermissionModel(sender).mayMigrateRegionNames()) { - throw new CommandPermissionsException(); - } - - LoggerToChatHandler handler = null; - Logger minecraftLogger = null; - - if (sender instanceof Player) { - handler = new LoggerToChatHandler(sender); - handler.setLevel(Level.ALL); - minecraftLogger = Logger.getLogger("Minecraft"); - minecraftLogger.addHandler(handler); - } - - try { - UUIDMigrator migrator = new UUIDMigrator(plugin.getProfileService(), plugin.getLogger()); - migrator.readConfiguration(plugin.getGlobalStateManager()); - List managers = plugin.getGlobalRegionManager().getLoaded(); - - // Try migration - if (migrator.migrate(managers)) { - sender.sendMessage(ChatColor.YELLOW + "Now saving regions... this may take a while."); - - for (RegionManager manager : managers) { - manager.save(); - } - - sender.sendMessage(ChatColor.YELLOW + "Migration complete!"); - } else { - sender.sendMessage(ChatColor.YELLOW + "There were no names to migrate."); - } - } catch (IOException e) { - plugin.getLogger().log(Level.WARNING, "Failed to save", e); - throw new CommandException("Error encountered while saving: " + e.getMessage()); - } catch (MigrationException e) { - plugin.getLogger().log(Level.WARNING, "Failed to migrate", e); - throw new CommandException("Error encountered while migrating: " + e.getMessage()); - } finally { - if (minecraftLogger != null) { - minecraftLogger.removeHandler(handler); - } - } - } - - /** - * Teleport to a region - * - * @param args the arguments - * @param sender the sender - * @throws CommandException any error - */ - @Command(aliases = {"teleport", "tp"}, - usage = "", - flags = "s", - desc = "Teleports you to the location associated with the region.", - min = 1, max = 1) - public void teleport(CommandContext args, CommandSender sender) throws CommandException { - Player player = plugin.checkPlayer(sender); - Location teleportLocation; - - // Lookup the existing region - RegionManager regionManager = plugin.getGlobalRegionManager().get(player.getWorld()); - ProtectedRegion existing = findExistingRegion(regionManager, - args.getString(0), false); - - // Check permissions - if (!getPermissionModel(sender).mayTeleportTo(existing)) { - throw new CommandPermissionsException(); - } - - // -s for spawn location - if (args.hasFlag('s')) { - teleportLocation = existing.getFlag(DefaultFlag.SPAWN_LOC); - - if (teleportLocation == null) { - throw new CommandException( - "The region has no spawn point associated."); - } - } else { - teleportLocation = existing.getFlag(DefaultFlag.TELE_LOC); - - if (teleportLocation == null) { - throw new CommandException( - "The region has no teleport point associated."); - } - } - - player.teleport(BukkitUtil.toLocation(teleportLocation)); - sender.sendMessage("Teleported you to the region '" + existing.getId() + "'."); - } -} +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands.region; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandPermissionsException; +import com.sk89q.worldedit.Location; +import com.sk89q.worldedit.bukkit.BukkitUtil; +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.bukkit.WorldConfiguration; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.commands.AsyncCommandHelper; +import com.sk89q.worldguard.bukkit.commands.CommandUtils; +import com.sk89q.worldguard.bukkit.commands.FutureProgressListener; +import com.sk89q.worldguard.bukkit.commands.MessageFutureCallback.Builder; +import com.sk89q.worldguard.bukkit.commands.task.RegionAdder; +import com.sk89q.worldguard.bukkit.commands.task.RegionLister; +import com.sk89q.worldguard.bukkit.commands.task.RegionManagerReloader; +import com.sk89q.worldguard.bukkit.commands.task.RegionManagerSaver; +import com.sk89q.worldguard.bukkit.commands.task.RegionRemover; +import com.sk89q.worldguard.bukkit.permission.RegionPermissionModel; +import com.sk89q.worldguard.bukkit.util.LoggerToChatHandler; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; +import com.sk89q.worldguard.protection.flags.RegionGroup; +import com.sk89q.worldguard.protection.flags.RegionGroupFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; +import com.sk89q.worldguard.protection.util.migrator.MigrationException; +import com.sk89q.worldguard.protection.util.migrator.UUIDMigrator; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Implements the /region commands for WorldGuard. + */ +public final class RegionCommands extends RegionCommandsBase { + + private final WorldGuardPlugin plugin; + + public RegionCommands(WorldGuardPlugin plugin) { + checkNotNull(plugin); + this.plugin = plugin; + } + + /** + * Defines a new region. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"define", "def", "d", "create"}, + usage = " [ [ []]]", + flags = "n", + desc = "Defines a region", + min = 1) + public void define(CommandContext args, CommandSender sender) throws CommandException { + Player player = plugin.checkPlayer(sender); + + // Check permissions + if (!getPermissionModel(sender).mayDefine()) { + throw new CommandPermissionsException(); + } + + String id = checkRegionId(args.getString(0), false); + + RegionManager manager = checkRegionManager(plugin, player.getWorld()); + + checkRegionDoesNotExist(manager, id, true); + + ProtectedRegion region = checkRegionFromSelection(player, id); + warnAboutDimensions(player, region); + informNewUser(player, manager, region); + + RegionAdder task = new RegionAdder(plugin, manager, region); + task.addOwnersFromCommand(args, 2); + ListenableFuture future = plugin.getExecutorService().submit(task); + + AsyncCommandHelper.wrap(future, plugin, player) + .formatUsing(id) + .registerWithSupervisor("Adding the region '%s'...") + .sendMessageAfterDelay("(Please wait... adding '%s'...)") + .thenRespondWith( + "A new region has been made named '%s'.", + "Failed to add the region '%s'"); + } + + /** + * Re-defines a region with a new selection. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"redefine", "update", "move"}, + usage = "", + desc = "Re-defines the shape of a region", + min = 1, max = 1) + public void redefine(CommandContext args, CommandSender sender) throws CommandException { + Player player = plugin.checkPlayer(sender); + World world = player.getWorld(); + + String id = checkRegionId(args.getString(0), false); + + RegionManager manager = checkRegionManager(plugin, world); + + ProtectedRegion existing = checkExistingRegion(manager, id, false); + + // Check permissions + if (!getPermissionModel(sender).mayRedefine(existing)) { + throw new CommandPermissionsException(); + } + + ProtectedRegion region = checkRegionFromSelection(player, id); + region.copyFrom(existing); + + warnAboutDimensions(sender, region); + + RegionAdder task = new RegionAdder(plugin, manager, region); + ListenableFuture future = plugin.getExecutorService().submit(task); + + AsyncCommandHelper.wrap(future, plugin, player) + .formatUsing(id) + .registerWithSupervisor("Updating the region '%s'...") + .sendMessageAfterDelay("(Please wait... updating '%s'...)") + .thenRespondWith( + "Region '%s' has been updated with a new area.", + "Failed to update the region '%s'"); + } + + /** + * Claiming command for users. + * + *

This command is a joke and it needs to be rewritten. It was contributed + * code :(

+ * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"claim"}, + usage = " [ [ []]]", + desc = "Claim a region", + min = 1) + public void claim(CommandContext args, CommandSender sender) throws CommandException { + Player player = plugin.checkPlayer(sender); + LocalPlayer localPlayer = plugin.wrapPlayer(player); + RegionPermissionModel permModel = getPermissionModel(sender); + + // Check permissions + if (!permModel.mayClaim()) { + throw new CommandPermissionsException(); + } + + String id = checkRegionId(args.getString(0), false); + + RegionManager manager = checkRegionManager(plugin, player.getWorld()); + + checkRegionDoesNotExist(manager, id, false); + ProtectedRegion region = checkRegionFromSelection(player, id); + + WorldConfiguration wcfg = plugin.getGlobalStateManager().get(player.getWorld()); + + // Check whether the player has created too many regions + if (!permModel.mayClaimRegionsUnbounded()) { + int maxRegionCount = wcfg.getMaxRegionCount(player); + if (maxRegionCount >= 0 + && manager.getRegionCountOfPlayer(localPlayer) >= maxRegionCount) { + throw new CommandException( + "You own too many regions, delete one first to claim a new one."); + } + } + + ProtectedRegion existing = manager.getRegion(id); + + // Check for an existing region + if (existing != null) { + if (!existing.getOwners().contains(localPlayer)) { + throw new CommandException( + "This region already exists and you don't own it."); + } + } + + // We have to check whether this region violates the space of any other reion + ApplicableRegionSet regions = manager.getApplicableRegions(region); + + // Check if this region overlaps any other region + if (regions.size() > 0) { + if (!regions.isOwnerOfAll(localPlayer)) { + throw new CommandException("This region overlaps with someone else's region."); + } + } else { + if (wcfg.claimOnlyInsideExistingRegions) { + throw new CommandException("You may only claim regions inside " + + "existing regions that you or your group own."); + } + } + + // Check claim volume + if (!permModel.mayClaimRegionsUnbounded()) { + if (region.volume() > wcfg.maxClaimVolume) { + player.sendMessage(ChatColor.RED + "This region is too large to claim."); + player.sendMessage(ChatColor.RED + + "Max. volume: " + wcfg.maxClaimVolume + ", your volume: " + region.volume()); + return; + } + } + + region.getOwners().addPlayer(player.getName()); + + RegionAdder task = new RegionAdder(plugin, manager, region); + ListenableFuture future = plugin.getExecutorService().submit(task); + + AsyncCommandHelper.wrap(future, plugin, player) + .formatUsing(id) + .registerWithSupervisor("Claiming the region '%s'...") + .sendMessageAfterDelay("(Please wait... claiming '%s'...)") + .thenRespondWith( + "A new region has been claimed named '%s'.", + "Failed to claim the region '%s'"); + } + + /** + * Get a WorldEdit selection from a region. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"select", "sel", "s"}, + usage = "[id]", + desc = "Load a region as a WorldEdit selection", + min = 0, max = 1) + public void select(CommandContext args, CommandSender sender) throws CommandException { + Player player = plugin.checkPlayer(sender); + World world = player.getWorld(); + RegionManager manager = checkRegionManager(plugin, world); + ProtectedRegion existing; + + // If no arguments were given, get the region that the player is inside + if (args.argsLength() == 0) { + existing = checkRegionStandingIn(manager, player); + } else { + existing = checkExistingRegion(manager, args.getString(0), false); + } + + // Check permissions + if (!getPermissionModel(sender).maySelect(existing)) { + throw new CommandPermissionsException(); + } + + // Select + setPlayerSelection(player, existing); + } + + /** + * Get information about a region. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"info", "i"}, + usage = "[id]", + flags = "usw:", + desc = "Get information about a region", + min = 0, max = 1) + public void info(CommandContext args, CommandSender sender) throws CommandException { + World world = checkWorld(args, sender, 'w'); // Get the world + RegionPermissionModel permModel = getPermissionModel(sender); + + // Lookup the existing region + RegionManager manager = checkRegionManager(plugin, world); + ProtectedRegion existing; + + if (args.argsLength() == 0) { // Get region from where the player is + if (!(sender instanceof Player)) { + throw new CommandException("Please specify " + + "the region with /region info -w world_name region_name."); + } + + existing = checkRegionStandingIn(manager, (Player) sender, true); + } else { // Get region from the ID + existing = checkExistingRegion(manager, args.getString(0), true); + } + + // Check permissions + if (!permModel.mayLookup(existing)) { + throw new CommandPermissionsException(); + } + + // Let the player select the region + if (args.hasFlag('s')) { + // Check permissions + if (!permModel.maySelect(existing)) { + throw new CommandPermissionsException(); + } + + setPlayerSelection(plugin.checkPlayer(sender), existing); + } + + // Print region information + RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, args.hasFlag('u') ? null : plugin.getProfileCache()); + ListenableFuture future = Futures.transform( + plugin.getExecutorService().submit(printout), + CommandUtils.messageFunction(sender)); + + // If it takes too long... + FutureProgressListener.addProgressListener( + future, sender, "(Please wait... fetching region information...)"); + + // Send a response message + Futures.addCallback(future, + new Builder(plugin, sender) + .onFailure("Failed to fetch region information") + .build()); + } + + /** + * List regions. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"list"}, + usage = "[page]", + desc = "Get a list of regions", + flags = "np:w:", + max = 1) + public void list(CommandContext args, CommandSender sender) throws CommandException { + World world = checkWorld(args, sender, 'w'); // Get the world + String ownedBy; + + // Get page + int page = args.getInteger(0, 1) - 1; + if (page < 0) { + page = 0; + } + + // -p flag to lookup a player's regions + if (args.hasFlag('p')) { + ownedBy = args.getFlag('p'); + } else { + ownedBy = null; // List all regions + } + + // Check permissions + if (!getPermissionModel(sender).mayList(ownedBy)) { + ownedBy = sender.getName(); // assume they only want their own + if (!getPermissionModel(sender).mayList(ownedBy)) { + throw new CommandPermissionsException(); + } + } + + RegionManager manager = checkRegionManager(plugin, world); + + RegionLister task = new RegionLister(plugin, manager, sender); + task.setPage(page); + if (ownedBy != null) { + task.filterOwnedByName(ownedBy, args.hasFlag('n')); + } + + ListenableFuture future = plugin.getExecutorService().submit(task); + + AsyncCommandHelper.wrap(future, plugin, sender) + .registerWithSupervisor("Getting list of regions...") + .sendMessageAfterDelay("(Please wait... fetching region list...)") + .thenTellErrorsOnly("Failed to fetch region list"); + } + + /** + * Set a flag. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"flag", "f"}, + usage = " [-w world] [-g group] [value]", + flags = "g:w:", + desc = "Set flags", + min = 2) + public void flag(CommandContext args, CommandSender sender) throws CommandException { + World world = checkWorld(args, sender, 'w'); // Get the world + String flagName = args.getString(1); + String value = args.argsLength() >= 3 ? args.getJoinedStrings(2) : null; + RegionGroup groupValue = null; + RegionPermissionModel permModel = getPermissionModel(sender); + + // Lookup the existing region + RegionManager manager = checkRegionManager(plugin, world); + ProtectedRegion existing = checkExistingRegion(manager, args.getString(0), true); + + // Check permissions + if (!permModel.maySetFlag(existing)) { + throw new CommandPermissionsException(); + } + + Flag foundFlag = DefaultFlag.fuzzyMatchFlag(flagName); + + // We didn't find the flag, so let's print a list of flags that the user + // can use, and do nothing afterwards + if (foundFlag == null) { + StringBuilder list = new StringBuilder(); + + // Need to build a list + for (Flag flag : DefaultFlag.getFlags()) { + // Can the user set this flag? + if (!permModel.maySetFlag(existing, flag)) { + continue; + } + + if (list.length() > 0) { + list.append(", "); + } + + list.append(flag.getName()); + } + + sender.sendMessage(ChatColor.RED + "Unknown flag specified: " + flagName); + sender.sendMessage(ChatColor.RED + "Available " + + "flags: " + list); + + return; + } + + // Also make sure that we can use this flag + // This permission is confusing and probably should be replaced, but + // but not here -- in the model + if (!permModel.maySetFlag(existing, foundFlag)) { + throw new CommandPermissionsException(); + } + + // -g for group flag + if (args.hasFlag('g')) { + String group = args.getFlag('g'); + RegionGroupFlag groupFlag = foundFlag.getRegionGroupFlag(); + + if (groupFlag == null) { + throw new CommandException("Region flag '" + foundFlag.getName() + + "' does not have a group flag!"); + } + + // Parse the [-g group] separately so entire command can abort if parsing + // the [value] part throws an error. + try { + groupValue = groupFlag.parseInput(plugin, sender, group); + } catch (InvalidFlagFormat e) { + throw new CommandException(e.getMessage()); + } + + } + + // Set the flag value if a value was set + if (value != null) { + // Set the flag if [value] was given even if [-g group] was given as well + try { + setFlag(existing, foundFlag, sender, value); + } catch (InvalidFlagFormat e) { + throw new CommandException(e.getMessage()); + } + + sender.sendMessage(ChatColor.YELLOW + + "Region flag " + foundFlag.getName() + " set on '" + + existing.getId() + "' to '" + value + "'."); + + // No value? Clear the flag, if -g isn't specified + } else if (!args.hasFlag('g')) { + // Clear the flag only if neither [value] nor [-g group] was given + existing.setFlag(foundFlag, null); + + // Also clear the associated group flag if one exists + RegionGroupFlag groupFlag = foundFlag.getRegionGroupFlag(); + if (groupFlag != null) { + existing.setFlag(groupFlag, null); + } + + sender.sendMessage(ChatColor.YELLOW + + "Region flag " + foundFlag.getName() + " removed from '" + + existing.getId() + "'. (Any -g(roups) were also removed.)"); + } + + // Now set the group + if (groupValue != null) { + RegionGroupFlag groupFlag = foundFlag.getRegionGroupFlag(); + + // If group set to the default, then clear the group flag + if (groupValue == groupFlag.getDefault()) { + existing.setFlag(groupFlag, null); + sender.sendMessage(ChatColor.YELLOW + + "Region group flag for '" + foundFlag.getName() + "' reset to " + + "default."); + } else { + existing.setFlag(groupFlag, groupValue); + sender.sendMessage(ChatColor.YELLOW + + "Region group flag for '" + foundFlag.getName() + "' set."); + } + } + + // Print region information + RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, null); + printout.append(ChatColor.GRAY); + printout.append("(Current flags: "); + printout.appendFlagsList(false); + printout.append(")"); + printout.send(sender); + } + + /** + * Set the priority of a region. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"setpriority", "priority", "pri"}, + usage = " ", + flags = "w:", + desc = "Set the priority of a region", + min = 2, max = 2) + public void setPriority(CommandContext args, CommandSender sender) throws CommandException { + World world = checkWorld(args, sender, 'w'); // Get the world + int priority = args.getInteger(1); + + // Lookup the existing region + RegionManager manager = checkRegionManager(plugin, world); + ProtectedRegion existing = checkExistingRegion(manager, args.getString(0), false); + + // Check permissions + if (!getPermissionModel(sender).maySetPriority(existing)) { + throw new CommandPermissionsException(); + } + + existing.setPriority(priority); + + sender.sendMessage(ChatColor.YELLOW + + "Priority of '" + existing.getId() + "' set to " + + priority + " (higher numbers override)."); + } + + /** + * Set the parent of a region. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"setparent", "parent", "par"}, + usage = " [parent-id]", + flags = "w:", + desc = "Set the parent of a region", + min = 1, max = 2) + public void setParent(CommandContext args, CommandSender sender) throws CommandException { + World world = checkWorld(args, sender, 'w'); // Get the world + ProtectedRegion parent; + ProtectedRegion child; + + // Lookup the existing region + RegionManager manager = checkRegionManager(plugin, world); + + // Get parent and child + child = checkExistingRegion(manager, args.getString(0), false); + if (args.argsLength() == 2) { + parent = checkExistingRegion(manager, args.getString(1), false); + } else { + parent = null; + } + + // Check permissions + if (!getPermissionModel(sender).maySetParent(child, parent)) { + throw new CommandPermissionsException(); + } + + try { + child.setParent(parent); + } catch (CircularInheritanceException e) { + // Tell the user what's wrong + RegionPrintoutBuilder printout = new RegionPrintoutBuilder(parent, null); + printout.append(ChatColor.RED); + assert parent != null; + printout.append("Uh oh! Setting '" + parent.getId() + "' to be the parent " + + "of '" + child.getId() + "' would cause circular inheritance.\n"); + printout.append(ChatColor.GRAY); + printout.append("(Current inheritance on '" + parent.getId() + "':\n"); + printout.appendParentTree(true); + printout.append(ChatColor.GRAY); + printout.append(")"); + printout.send(sender); + return; + } + + // Tell the user the current inheritance + RegionPrintoutBuilder printout = new RegionPrintoutBuilder(child, null); + printout.append(ChatColor.YELLOW); + printout.append("Inheritance set for region '" + child.getId() + "'.\n"); + if (parent != null) { + printout.append(ChatColor.GRAY); + printout.append("(Current inheritance:\n"); + printout.appendParentTree(true); + printout.append(ChatColor.GRAY); + printout.append(")"); + } + printout.send(sender); + } + + /** + * Remove a region. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"remove", "delete", "del", "rem"}, + usage = "", + flags = "w:", + desc = "Remove a region", + min = 1, max = 1) + public void remove(CommandContext args, CommandSender sender) throws CommandException { + World world = checkWorld(args, sender, 'w'); // Get the world + + // Lookup the existing region + RegionManager manager = checkRegionManager(plugin, world); + ProtectedRegion existing = checkExistingRegion(manager, args.getString(0), true); + + // Check permissions + if (!getPermissionModel(sender).mayDelete(existing)) { + throw new CommandPermissionsException(); + } + + RegionRemover task = new RegionRemover(manager, existing); + + AsyncCommandHelper.wrap(plugin.getExecutorService().submit(task), plugin, sender) + .formatUsing(existing.getId()) + .registerWithSupervisor("Removing the region '%s'...") + .sendMessageAfterDelay("(Please wait... removing '%s'...)") + .thenRespondWith( + "The region named '%s' has been removed.", + "Failed to remove the region '%s'"); + } + + /** + * Reload the region database. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"load", "reload"}, + usage = "[world]", + desc = "Reload regions from file", + flags = "w:") + public void load(CommandContext args, final CommandSender sender) throws CommandException { + World world = null; + try { + world = checkWorld(args, sender, 'w'); // Get the world + } catch (CommandException e) { + // assume the user wants to reload all worlds + } + + // Check permissions + if (!getPermissionModel(sender).mayForceLoadRegions()) { + throw new CommandPermissionsException(); + } + + if (world != null) { + RegionManager manager = checkRegionManager(plugin, world); + + if (manager == null) { + throw new CommandException("No region manager exists for world '" + world.getName() + "'."); + } + + ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerReloader(manager)); + + AsyncCommandHelper.wrap(future, plugin, sender) + .forRegionDataLoad(world, false); + } else { + // Load regions for all worlds + List managers = new ArrayList(); + + for (World w : Bukkit.getServer().getWorlds()) { + RegionManager manager = plugin.getRegionContainer().get(w); + if (manager != null) { + managers.add(manager); + } + } + + ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerReloader(managers)); + + AsyncCommandHelper.wrap(future, plugin, sender) + .registerWithSupervisor("Loading regions for all worlds") + .sendMessageAfterDelay("(Please wait... loading region data for all worlds...)") + .thenRespondWith( + "Successfully load the region data for all worlds.", + "Failed to load regions for all worlds"); + } + } + + /** + * Re-save the region database. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"save", "write"}, + usage = "[world]", + desc = "Re-save regions to file", + flags = "w:") + public void save(CommandContext args, final CommandSender sender) throws CommandException { + World world = null; + try { + world = checkWorld(args, sender, 'w'); // Get the world + } catch (CommandException e) { + // assume user wants to save all worlds + } + + // Check permissions + if (!getPermissionModel(sender).mayForceSaveRegions()) { + throw new CommandPermissionsException(); + } + + if (world != null) { + RegionManager manager = checkRegionManager(plugin, world); + + if (manager == null) { + throw new CommandException("No region manager exists for world '" + world.getName() + "'."); + } + + ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerSaver(manager)); + + AsyncCommandHelper.wrap(future, plugin, sender) + .forRegionDataSave(world, false); + } else { + // Save for all worlds + List managers = new ArrayList(); + + for (World w : Bukkit.getServer().getWorlds()) { + RegionManager manager = plugin.getRegionContainer().get(w); + if (manager != null) { + managers.add(manager); + } + } + + ListenableFuture future = plugin.getExecutorService().submit(new RegionManagerSaver(managers)); + + AsyncCommandHelper.wrap(future, plugin, sender) + .registerWithSupervisor("Saving regions for all worlds") + .sendMessageAfterDelay("(Please wait... saving region data for all worlds...)") + .thenRespondWith( + "Successfully saved the region data for all worlds.", + "Failed to save regions for all worlds"); + } + } + + /** + * Migrate the region database. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"migratedb"}, usage = " ", + desc = "Migrate from one Protection Database to another.", min = 2, max = 2) + public void migrateDB(CommandContext args, CommandSender sender) throws CommandException { + // Check permissions + if (!getPermissionModel(sender).mayMigrateRegionStore()) { + throw new CommandPermissionsException(); + } + + /* + String from = args.getString(0).toLowerCase().trim(); + String to = args.getString(1).toLowerCase().trim(); + + if (from.equals(to)) { + throw new CommandException("Will not migrate with common source and target."); + } + + Map> migrators = + AbstractDatabaseMigrator.getMigrators(); + MigratorKey key = new MigratorKey(from,to); + + if (!migrators.containsKey(key)) { + throw new CommandException("No migrator found for that combination and direction."); + } + + long lastRequest = 10000000; + if (this.migrateDBRequestDate != null) { + lastRequest = new Date().getTime() - this.migrateDBRequestDate.getTime(); + } + if (this.migrateDBRequest == null || lastRequest > 60000) { + this.migrateDBRequest = key; + this.migrateDBRequestDate = new Date(); + + throw new CommandException("This command is potentially dangerous.\n" + + "Please ensure you have made a backup of your data, and then re-enter the command exactly to procede."); + } + + Class cls = migrators.get(key); + + try { + AbstractDatabaseMigrator migrator = cls.getConstructor(WorldGuardPlugin.class).newInstance(plugin); + + migrator.migrate(); + } catch (IllegalArgumentException ignore) { + } catch (SecurityException ignore) { + } catch (InstantiationException ignore) { + } catch (IllegalAccessException ignore) { + } catch (InvocationTargetException ignore) { + } catch (NoSuchMethodException ignore) { + } catch (MigrationException e) { + throw new CommandException("Error migrating database: " + e.getMessage()); + } + + sender.sendMessage(ChatColor.YELLOW + "Regions have been migrated successfully.\n" + + "If you wish to use the destination format as your new backend, please update your config and reload WorldGuard."); + */ + } + + /** + * Migrate the region databases to use UUIDs rather than name. + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"migrateuuid"}, + desc = "Migrate loaded databases to use UUIDs", max = 0) + public void migrateUuid(CommandContext args, CommandSender sender) throws CommandException { + // Check permissions + if (!getPermissionModel(sender).mayMigrateRegionNames()) { + throw new CommandPermissionsException(); + } + + LoggerToChatHandler handler = null; + Logger minecraftLogger = null; + + if (sender instanceof Player) { + handler = new LoggerToChatHandler(sender); + handler.setLevel(Level.ALL); + minecraftLogger = Logger.getLogger("Minecraft"); + minecraftLogger.addHandler(handler); + } + + try { + UUIDMigrator migrator = new UUIDMigrator(plugin.getProfileService(), plugin.getLogger()); + migrator.readConfiguration(plugin.getGlobalStateManager()); + List managers = plugin.getRegionContainer().getLoaded(); + + // Try migration + if (migrator.migrate(managers)) { + sender.sendMessage(ChatColor.YELLOW + "Now saving regions... this may take a while."); + + for (RegionManager manager : managers) { + manager.save(); + } + + sender.sendMessage(ChatColor.YELLOW + "Migration complete!"); + } else { + sender.sendMessage(ChatColor.YELLOW + "There were no names to migrate."); + } + } catch (IOException e) { + plugin.getLogger().log(Level.WARNING, "Failed to save", e); + throw new CommandException("Error encountered while saving: " + e.getMessage()); + } catch (MigrationException e) { + plugin.getLogger().log(Level.WARNING, "Failed to migrate", e); + throw new CommandException("Error encountered while migrating: " + e.getMessage()); + } finally { + if (minecraftLogger != null) { + minecraftLogger.removeHandler(handler); + } + } + } + + /** + * Teleport to a region + * + * @param args the arguments + * @param sender the sender + * @throws CommandException any error + */ + @Command(aliases = {"teleport", "tp"}, + usage = "", + flags = "s", + desc = "Teleports you to the location associated with the region.", + min = 1, max = 1) + public void teleport(CommandContext args, CommandSender sender) throws CommandException { + Player player = plugin.checkPlayer(sender); + Location teleportLocation; + + // Lookup the existing region + RegionManager regionManager = checkRegionManager(plugin, player.getWorld()); + ProtectedRegion existing = checkExistingRegion(regionManager, args.getString(0), false); + + // Check permissions + if (!getPermissionModel(sender).mayTeleportTo(existing)) { + throw new CommandPermissionsException(); + } + + // -s for spawn location + if (args.hasFlag('s')) { + teleportLocation = existing.getFlag(DefaultFlag.SPAWN_LOC); + + if (teleportLocation == null) { + throw new CommandException( + "The region has no spawn point associated."); + } + } else { + teleportLocation = existing.getFlag(DefaultFlag.TELE_LOC); + + if (teleportLocation == null) { + throw new CommandException( + "The region has no teleport point associated."); + } + } + + player.teleport(BukkitUtil.toLocation(teleportLocation)); + sender.sendMessage("Teleported you to the region '" + existing.getId() + "'."); + } +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionPrintoutBuilder.java similarity index 97% rename from src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java rename to src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionPrintoutBuilder.java index 97eda335..a03c2745 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/RegionPrintoutBuilder.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionPrintoutBuilder.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.sk89q.worldguard.bukkit.commands; +package com.sk89q.worldguard.bukkit.commands.region; import com.sk89q.squirrelid.cache.ProfileCache; import com.sk89q.worldedit.BlockVector; @@ -39,7 +39,7 @@ * Create a region printout, as used in /region info to show information about * a region. */ -class RegionPrintoutBuilder implements Callable { +public class RegionPrintoutBuilder implements Callable { private final ProtectedRegion region; @Nullable @@ -52,7 +52,7 @@ class RegionPrintoutBuilder implements Callable { * @param region the region * @param cache a profile cache, or {@code null} */ - RegionPrintoutBuilder(ProtectedRegion region, @Nullable ProfileCache cache) { + public RegionPrintoutBuilder(ProtectedRegion region, @Nullable ProfileCache cache) { this.region = region; this.cache = cache; } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerReload.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerReloader.java similarity index 87% rename from src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerReload.java rename to src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerReloader.java index 15d3f0a4..372e7dd8 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerReload.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerReloader.java @@ -28,16 +28,16 @@ import static com.google.common.base.Preconditions.checkNotNull; -public class RegionManagerReload implements Callable> { +public class RegionManagerReloader implements Callable> { private final Collection managers; - public RegionManagerReload(Collection managers) { + public RegionManagerReloader(Collection managers) { checkNotNull(managers); this.managers = managers; } - public RegionManagerReload(RegionManager... manager) { + public RegionManagerReloader(RegionManager... manager) { this(Arrays.asList(manager)); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionmanagerSave.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerSaver.java similarity index 87% rename from src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionmanagerSave.java rename to src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerSaver.java index cd6606f5..f55b16e0 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionmanagerSave.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionManagerSaver.java @@ -28,16 +28,16 @@ import static com.google.common.base.Preconditions.checkNotNull; -public class RegionmanagerSave implements Callable> { +public class RegionManagerSaver implements Callable> { private final Collection managers; - public RegionmanagerSave(Collection managers) { + public RegionManagerSaver(Collection managers) { checkNotNull(managers); this.managers = managers; } - public RegionmanagerSave(RegionManager... manager) { + public RegionManagerSaver(RegionManager... manager) { this(Arrays.asList(manager)); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/permission/RegionPermissionModel.java b/src/main/java/com/sk89q/worldguard/bukkit/permission/RegionPermissionModel.java index 3807468b..16570f43 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/permission/RegionPermissionModel.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/permission/RegionPermissionModel.java @@ -123,6 +123,22 @@ public boolean maySetFlag(ProtectedRegion region, Flag flag) { return hasPatternPermission( "flag.flags." + flag.getName().toLowerCase(), region); } + + public boolean mayAddMembers(ProtectedRegion region) { + return hasPatternPermission("addmember", region); + } + + public boolean mayAddOwners(ProtectedRegion region) { + return hasPatternPermission("addowner", region); + } + + public boolean mayRemoveMembers(ProtectedRegion region) { + return hasPatternPermission("removemember", region); + } + + public boolean mayRemoveOwners(ProtectedRegion region) { + return hasPatternPermission("removeowner", region); + } /** * Checks to see if the given sender has permission to modify the given region From 5d6dad4c6d0e152ff357fe108e15e54ec327b6c6 Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 21:41:44 -0700 Subject: [PATCH 048/133] Don't remove child regions without warning. Also add some missing files. Fixes WORLDGUARD-2311. --- .../commands/region/RegionCommands.java | 13 +- .../commands/region/RegionCommandsBase.java | 358 ++++++++++++++++++ .../bukkit/commands/task/RegionAdder.java | 108 ++++++ .../bukkit/commands/task/RegionLister.java | 232 ++++++++++++ .../bukkit/commands/task/RegionRemover.java | 93 +++++ 5 files changed, 803 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommandsBase.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionAdder.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionLister.java create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionRemover.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java index 3003196c..ba04eda7 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommands.java @@ -48,6 +48,7 @@ import com.sk89q.worldguard.protection.flags.RegionGroup; import com.sk89q.worldguard.protection.flags.RegionGroupFlag; import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.RemovalStrategy; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException; import com.sk89q.worldguard.protection.util.migrator.MigrationException; @@ -649,11 +650,13 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE */ @Command(aliases = {"remove", "delete", "del", "rem"}, usage = "", - flags = "w:", + flags = "fuw:", desc = "Remove a region", min = 1, max = 1) public void remove(CommandContext args, CommandSender sender) throws CommandException { World world = checkWorld(args, sender, 'w'); // Get the world + boolean removeChildren = args.hasFlag('f'); + boolean unsetParent = args.hasFlag('u'); // Lookup the existing region RegionManager manager = checkRegionManager(plugin, world); @@ -666,6 +669,14 @@ public void remove(CommandContext args, CommandSender sender) throws CommandExce RegionRemover task = new RegionRemover(manager, existing); + if (removeChildren && unsetParent) { + throw new CommandException("You cannot use both -u (unset parent) and -f (remove children) together."); + } else if (removeChildren) { + task.setRemovalStrategy(RemovalStrategy.REMOVE_CHILDREN); + } else if (unsetParent) { + task.setRemovalStrategy(RemovalStrategy.UNSET_PARENT_IN_CHILDREN); + } + AsyncCommandHelper.wrap(plugin.getExecutorService().submit(task), plugin, sender) .formatUsing(existing.getId()) .registerWithSupervisor("Removing the region '%s'...") diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommandsBase.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommandsBase.java new file mode 100644 index 00000000..ce28601c --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommandsBase.java @@ -0,0 +1,358 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands.region; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.worldedit.BlockVector; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.selections.CuboidSelection; +import com.sk89q.worldedit.bukkit.selections.Polygonal2DSelection; +import com.sk89q.worldedit.bukkit.selections.Selection; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.permission.RegionPermissionModel; +import com.sk89q.worldguard.protection.ApplicableRegionSet; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +class RegionCommandsBase { + + /** + * Get the permission model to lookup permissions. + * + * @param sender the sender + * @return the permission model + */ + protected static RegionPermissionModel getPermissionModel(CommandSender sender) { + return new RegionPermissionModel(WorldGuardPlugin.inst(), sender); + } + + /** + * Gets the world from the given flag, or falling back to the the current player + * if the sender is a player, otherwise reporting an error. + * + * @param args the arguments + * @param sender the sender + * @param flag the flag (such as 'w') + * @return a world + * @throws CommandException on error + */ + protected static World checkWorld(CommandContext args, CommandSender sender, char flag) throws CommandException { + if (args.hasFlag(flag)) { + return WorldGuardPlugin.inst().matchWorld(sender, args.getFlag(flag)); + } else { + if (sender instanceof Player) { + return WorldGuardPlugin.inst().checkPlayer(sender).getWorld(); + } else { + throw new CommandException("Please specify " + "the world with -" + flag + " world_name."); + } + } + } + + /** + * Validate a region ID. + * + * @param id the id + * @param allowGlobal whether __global__ is allowed + * @return the id given + * @throws CommandException thrown on an error + */ + protected static String checkRegionId(String id, boolean allowGlobal) throws CommandException { + if (!ProtectedRegion.isValidId(id)) { + throw new CommandException( + "The region name of '" + id + "' contains characters that are not allowed."); + } + + if (!allowGlobal && id.equalsIgnoreCase("__global__")) { // Sorry, no global + throw new CommandException( + "Sorry, you can't use __global__ here."); + } + + return id; + } + + /** + * Get a protected region by a given name, otherwise throw a + * {@link CommandException}. + * + *

This also validates the region ID.

+ * + * @param regionManager the region manager + * @param id the name to search + * @param allowGlobal true to allow selecting __global__ + * @throws CommandException thrown if no region is found by the given name + */ + protected static ProtectedRegion checkExistingRegion(RegionManager regionManager, String id, boolean allowGlobal) throws CommandException { + // Validate the id + checkRegionId(id, allowGlobal); + + ProtectedRegion region = regionManager.getRegion(id); + + // No region found! + if (region == null) { + // But we want a __global__, so let's create one + if (id.equalsIgnoreCase("__global__")) { + region = new GlobalProtectedRegion(id); + regionManager.addRegion(region); + return region; + } + + throw new CommandException( + "No region could be found with the name of '" + id + "'."); + } + + return region; + } + + + /** + * Get the region at the player's location, if possible. + * + *

If the player is standing in several regions, an error will be raised + * and a list of regions will be provided.

+ * + * @param regionManager the region manager + * @param player the player + * @return a region + * @throws CommandException thrown if no region was found + */ + protected static ProtectedRegion checkRegionStandingIn(RegionManager regionManager, Player player) throws CommandException { + return checkRegionStandingIn(regionManager, player, false); + } + + /** + * Get the region at the player's location, if possible. + * + *

If the player is standing in several regions, an error will be raised + * and a list of regions will be provided.

+ * + *

If the player is not standing in any regions, the global region will + * returned if allowGlobal is true and it exists.

+ * + * @param regionManager the region manager + * @param player the player + * @param allowGlobal whether to search for a global region if no others are found + * @return a region + * @throws CommandException thrown if no region was found + */ + protected static ProtectedRegion checkRegionStandingIn(RegionManager regionManager, Player player, boolean allowGlobal) throws CommandException { + ApplicableRegionSet set = regionManager.getApplicableRegions(player.getLocation()); + + if (set.size() == 0) { + if (allowGlobal) { + ProtectedRegion global = checkExistingRegion(regionManager, "__global__", true); + player.sendMessage(ChatColor.GRAY + "You're not standing in any " + + "regions. Using the global region for this world instead."); + return global; + } + throw new CommandException( + "You're not standing in a region." + + "Specify an ID if you want to select a specific region."); + } else if (set.size() > 1) { + StringBuilder builder = new StringBuilder(); + boolean first = true; + + for (ProtectedRegion region : set) { + if (!first) { + builder.append(", "); + } + first = false; + builder.append(region.getId()); + } + + throw new CommandException( + "You're standing in several regions (please pick one).\nYou're in: " + builder.toString()); + } + + return set.iterator().next(); + } + + /** + * Get a WorldEdit selection for a player, or emit an exception if there is none + * available. + * + * @param player the player + * @return the selection + * @throws CommandException thrown on an error + */ + protected static Selection checkSelection(Player player) throws CommandException { + WorldEditPlugin worldEdit = WorldGuardPlugin.inst().getWorldEdit(); + Selection selection = worldEdit.getSelection(player); + + if (selection == null) { + throw new CommandException( + "Please select an area first. " + + "Use WorldEdit to make a selection! " + + "(wiki: http://wiki.sk89q.com/wiki/WorldEdit)."); + } + + return selection; + } + + /** + * Check that a region with the given ID does not already exist. + * + * @param manager the manager + * @param id the ID + * @throws CommandException thrown if the ID already exists + */ + protected static void checkRegionDoesNotExist(RegionManager manager, String id, boolean mayRedefine) throws CommandException { + if (manager.hasRegion(id)) { + throw new CommandException("A region with that name already exists. Please choose another name." + + (mayRedefine ? " To change the shape, use /region redefine " + id + "." : "")); + } + } + + /** + * Check that the given region manager is not null. + * + * @param plugin the plugin + * @param world the world + * @throws CommandException thrown if the manager is null + */ + protected static RegionManager checkRegionManager(WorldGuardPlugin plugin, World world) throws CommandException { + RegionManager manager = plugin.getRegionContainer().get(world); + if (manager == null) { + throw new CommandException("Either region support is disabled or region data failed to load in the target world."); + } + return manager; + } + + /** + * Create a {@link ProtectedRegion} from the player's selection. + * + * @param player the player + * @param id the ID of the new region + * @return a new region + * @throws CommandException thrown on an error + */ + protected static ProtectedRegion checkRegionFromSelection(Player player, String id) throws CommandException { + Selection selection = checkSelection(player); + + // Detect the type of region from WorldEdit + if (selection instanceof Polygonal2DSelection) { + Polygonal2DSelection polySel = (Polygonal2DSelection) selection; + int minY = polySel.getNativeMinimumPoint().getBlockY(); + int maxY = polySel.getNativeMaximumPoint().getBlockY(); + return new ProtectedPolygonalRegion(id, polySel.getNativePoints(), minY, maxY); + } else if (selection instanceof CuboidSelection) { + BlockVector min = selection.getNativeMinimumPoint().toBlockVector(); + BlockVector max = selection.getNativeMaximumPoint().toBlockVector(); + return new ProtectedCuboidRegion(id, min, max); + } else { + throw new CommandException("Sorry, you can only use cuboids and polygons for WorldGuard regions."); + } + } + + /** + * Warn the sender if the dimensions of the given region are worrying. + * + * @param sender the sender to send the message to + * @param region the region + */ + protected static void warnAboutDimensions(CommandSender sender, ProtectedRegion region) { + int height = region.getMaximumPoint().getBlockY() - region.getMinimumPoint().getBlockY(); + if (height <= 2) { + sender.sendMessage(ChatColor.GRAY + "(Warning: The height of the region was " + (height + 1) + " block(s).)"); + } + } + + /** + * Inform a new user about automatic protection. + * + * @param sender the sender to send the message to + * @param manager the region manager + * @param region the region + */ + protected static void informNewUser(CommandSender sender, RegionManager manager, ProtectedRegion region) { + if (manager.getRegions().size() <= 2) { + sender.sendMessage(ChatColor.GRAY + + "(This region is NOW PROTECTED from modification from others. " + + "Don't want that? Use " + + ChatColor.AQUA + "/rg flag " + region.getId() + " passthrough allow" + + ChatColor.GRAY + ")"); + } + } + + /** + * Set a player's selection to a given region. + * + * @param player the player + * @param region the region + * @throws CommandException thrown on a command error + */ + protected static void setPlayerSelection(Player player, ProtectedRegion region) throws CommandException { + WorldEditPlugin worldEdit = WorldGuardPlugin.inst().getWorldEdit(); + + World world = player.getWorld(); + + // Set selection + if (region instanceof ProtectedCuboidRegion) { + ProtectedCuboidRegion cuboid = (ProtectedCuboidRegion) region; + Vector pt1 = cuboid.getMinimumPoint(); + Vector pt2 = cuboid.getMaximumPoint(); + CuboidSelection selection = new CuboidSelection(world, pt1, pt2); + worldEdit.setSelection(player, selection); + player.sendMessage(ChatColor.YELLOW + "Region selected as a cuboid."); + + } else if (region instanceof ProtectedPolygonalRegion) { + ProtectedPolygonalRegion poly2d = (ProtectedPolygonalRegion) region; + Polygonal2DSelection selection = new Polygonal2DSelection( + world, poly2d.getPoints(), + poly2d.getMinimumPoint().getBlockY(), + poly2d.getMaximumPoint().getBlockY() ); + worldEdit.setSelection(player, selection); + player.sendMessage(ChatColor.YELLOW + "Region selected as a polygon."); + + } else if (region instanceof GlobalProtectedRegion) { + throw new CommandException( + "Can't select global regions! " + + "That would cover the entire world."); + + } else { + throw new CommandException("Unknown region type: " + + region.getClass().getCanonicalName()); + } + } + + /** + * Utility method to set a flag. + * + * @param region the region + * @param flag the flag + * @param sender the sender + * @param value the value + * @throws InvalidFlagFormat thrown if the value is invalid + */ + protected static void setFlag(ProtectedRegion region, Flag flag, CommandSender sender, String value) throws InvalidFlagFormat { + region.setFlag(flag, flag.parseInput(WorldGuardPlugin.inst(), sender, value)); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionAdder.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionAdder.java new file mode 100644 index 00000000..e66491c6 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionAdder.java @@ -0,0 +1,108 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands.task; + +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.util.DomainInputResolver; +import com.sk89q.worldguard.protection.util.DomainInputResolver.UserLocatorPolicy; + +import javax.annotation.Nullable; +import java.util.concurrent.Callable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Creates a new region. + */ +public class RegionAdder implements Callable { + + private final WorldGuardPlugin plugin; + private final RegionManager manager; + private final ProtectedRegion region; + @Nullable + private String[] ownersInput; + private UserLocatorPolicy locatorPolicy = UserLocatorPolicy.UUID_ONLY; + + /** + * Create a new instance. + * + * @param plugin the plugin + * @param manager the manage + * @param region the region + */ + public RegionAdder(WorldGuardPlugin plugin, RegionManager manager, ProtectedRegion region) { + checkNotNull(plugin); + checkNotNull(manager); + checkNotNull(region); + + this.plugin = plugin; + this.manager = manager; + this.region = region; + } + + /** + * Add the owners from the command's arguments. + * + * @param args the arguments + * @param namesIndex the index in the list of arguments to read the first name from + */ + public void addOwnersFromCommand(CommandContext args, int namesIndex) { + if (args.argsLength() >= namesIndex) { + setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); + setOwnersInput(args.getSlice(namesIndex)); + } + } + + @Override + public ProtectedRegion call() throws Exception { + if (ownersInput != null) { + DomainInputResolver resolver = new DomainInputResolver(plugin.getProfileService(), ownersInput); + resolver.setLocatorPolicy(locatorPolicy); + DefaultDomain domain = resolver.call(); + region.getOwners().addAll(domain); + } + + manager.addRegion(region); + + return region; + } + + @Nullable + public String[] getOwnersInput() { + return ownersInput; + } + + public void setOwnersInput(@Nullable String[] ownersInput) { + this.ownersInput = ownersInput; + } + + public UserLocatorPolicy getLocatorPolicy() { + return locatorPolicy; + } + + public void setLocatorPolicy(UserLocatorPolicy locatorPolicy) { + this.locatorPolicy = locatorPolicy; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionLister.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionLister.java new file mode 100644 index 00000000..d5f1d43c --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionLister.java @@ -0,0 +1,232 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands.task; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.squirrelid.Profile; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class RegionLister implements Callable { + + private static final Logger log = Logger.getLogger(RegionLister.class.getCanonicalName()); + + private final WorldGuardPlugin plugin; + private final CommandSender sender; + private final RegionManager manager; + private OwnerMatcher ownerMatcher; + private int page; + + public RegionLister(WorldGuardPlugin plugin, RegionManager manager, CommandSender sender) { + checkNotNull(plugin); + checkNotNull(manager); + checkNotNull(sender); + + this.plugin = plugin; + this.manager = manager; + this.sender = sender; + } + + public int getPage() { + return page; + } + + public void setPage(int page) { + this.page = page; + } + + public void filterOwnedByPlayer(final Player player) { + ownerMatcher = new OwnerMatcher() { + @Override + public String getName() { + return player.getName(); + } + + @Override + public boolean isContainedWithin(DefaultDomain domain) throws CommandException { + return domain.contains(player.getUniqueId()); + } + }; + } + + public void filterOwnedByName(String name, boolean nameOnly) { + if (nameOnly) { + filterOwnedByName(name); + } else { + filterOwnedByProfile(name); + } + } + + private void filterOwnedByName(final String name) { + ownerMatcher = new OwnerMatcher() { + @Override + public String getName() { + return name; + } + + @Override + public boolean isContainedWithin(DefaultDomain domain) throws CommandException { + return domain.contains(name); + } + }; + } + + private void filterOwnedByProfile(final String name) { + ownerMatcher = new OwnerMatcher() { + private UUID uniqueId; + + @Override + public String getName() { + return name; + } + + @Override + public boolean isContainedWithin(DefaultDomain domain) throws CommandException { + if (uniqueId == null) { + Profile profile; + + try { + profile = plugin.getProfileService().findByName(name); + } catch (IOException e) { + log.log(Level.WARNING, "Failed UUID lookup of '" + name + "'", e); + throw new CommandException("Failed to lookup the UUID of '" + name + "'"); + } catch (InterruptedException e) { + log.log(Level.WARNING, "Failed UUID lookup of '" + name + "'", e); + throw new CommandException("The lookup the UUID of '" + name + "' was interrupted"); + } + + if (profile == null) { + throw new CommandException("A user by the name of '" + name + "' does not seem to exist."); + } + + uniqueId = profile.getUniqueId(); + } + + return domain.contains(uniqueId); + } + }; + } + + @Override + public Integer call() throws Exception { + Map regions = manager.getRegions(); + + // Build a list of regions to show + List entries = new ArrayList(); + + int index = 0; + for (String id : regions.keySet()) { + RegionListEntry entry = new RegionListEntry(id, index++); + + // Filtering by owner? + ProtectedRegion region = regions.get(id); + if (ownerMatcher != null) { + entry.isOwner = ownerMatcher.isContainedWithin(region.getOwners()); + entry.isMember = ownerMatcher.isContainedWithin(region.getMembers()); + + if (!entry.isOwner && !entry.isMember) { + continue; // Skip + } + } + + entries.add(entry); + } + + Collections.sort(entries); + + final int totalSize = entries.size(); + final int pageSize = 10; + final int pages = (int) Math.ceil(totalSize / (float) pageSize); + + sender.sendMessage(ChatColor.RED + + (ownerMatcher == null ? "Regions (page " : "Regions for " + ownerMatcher.getName() + " (page ") + + (page + 1) + " of " + pages + "):"); + + if (page < pages) { + // Print + for (int i = page * pageSize; i < page * pageSize + pageSize; i++) { + if (i >= totalSize) { + break; + } + + sender.sendMessage(ChatColor.YELLOW.toString() + entries.get(i)); + } + } + + return page; + } + + private static interface OwnerMatcher { + public String getName(); + + public boolean isContainedWithin(DefaultDomain domain) throws CommandException; + } + + private class RegionListEntry implements Comparable { + private final String id; + private final int index; + boolean isOwner; + boolean isMember; + + private RegionListEntry(String id, int index) { + this.id = id; + this.index = index; + } + + @Override + public int compareTo(RegionListEntry o) { + if (isOwner != o.isOwner) { + return isOwner ? 1 : -1; + } + if (isMember != o.isMember) { + return isMember ? 1 : -1; + } + return id.compareTo(o.id); + } + + @Override + public String toString() { + if (isOwner) { + return (index + 1) + ". +" + id; + } else if (isMember) { + return (index + 1) + ". -" + id; + } else { + return (index + 1) + ". " + id; + } + } + } +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionRemover.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionRemover.java new file mode 100644 index 00000000..4653f707 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/task/RegionRemover.java @@ -0,0 +1,93 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.commands.task; + +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.managers.RemovalStrategy; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import javax.annotation.Nullable; +import java.util.Set; +import java.util.concurrent.Callable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Removes a region. + */ +public class RegionRemover implements Callable> { + + private final RegionManager manager; + private final ProtectedRegion region; + @Nullable + private RemovalStrategy removalStrategy; + + /** + * Create a new instance. + * + * @param manager a region manager + * @param region the region to remove + */ + public RegionRemover(RegionManager manager, ProtectedRegion region) { + checkNotNull(manager); + checkNotNull(region); + this.manager = manager; + this.region = region; + } + + /** + * GSet a parent removal strategy. + * + * @return a removal strategy or null (see{@link #setRemovalStrategy(RemovalStrategy)} + */ + @Nullable + public RemovalStrategy getRemovalStrategy() { + return removalStrategy; + } + + /** + * Set a parent removal strategy. Set it to {@code null} to have the code + * check for children and throw an error if any are found. + * + * @param removalStrategy a removal strategy, or {@code null} to error if children exist + */ + public void setRemovalStrategy(@Nullable RemovalStrategy removalStrategy) { + this.removalStrategy = removalStrategy; + } + + @Override + public Set call() throws Exception { + if (removalStrategy == null) { + for (ProtectedRegion test : manager.getRegions().values()) { + ProtectedRegion parent = test.getParent(); + if (parent != null && parent.equals(region)) { + throw new CommandException( + "The region '" + region.getId() + "' has child regions. Use -f to force removal of children " + + "or -u to unset the parent value of these children."); + } + } + + return manager.removeRegion(region.getId(), RemovalStrategy.UNSET_PARENT_IN_CHILDREN); + } else { + return manager.removeRegion(region.getId(), removalStrategy); + } + } +} From 608d782321da771673fa050d99fec785895aedad Mon Sep 17 00:00:00 2001 From: sk89q Date: Thu, 14 Aug 2014 22:23:47 -0700 Subject: [PATCH 049/133] Activate debugging listener of the property "worldguard.debug.listener" is true. --- .../java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 5d526951..5b0ba8ea 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -229,7 +229,9 @@ public void run() { (new RegionProtectionListener(this)).registerEvents(); (new BlockedPotionsListener(this)).registerEvents(); (new EventAbstractionListener(this)).registerEvents(); - (new DebuggingListener(this, getLogger())).registerEvents(); + if ("true".equalsIgnoreCase(System.getProperty("worldguard.debug.listener"))) { + (new DebuggingListener(this, getLogger())).registerEvents(); + } configuration.updateCommandBookGodMode(); From 3f16fef57eba8534b6735c0b8726a35f7697400d Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 15 Aug 2014 01:43:37 -0700 Subject: [PATCH 050/133] Fix and improve protection handlers. --- .../sk89q/worldguard/bukkit/BukkitUtil.java | 5 +- .../sk89q/worldguard/bukkit/RegionQuery.java | 24 +- .../sk89q/worldguard/bukkit/cause/Cause.java | 44 +++- .../worldguard/bukkit/event/BulkEvent.java | 38 +++ .../event/block/AbstractBlockEvent.java | 123 +++++++-- .../bukkit/event/block/BreakBlockEvent.java | 6 + .../bukkit/event/block/PlaceBlockEvent.java | 6 + .../bukkit/event/block/UseBlockEvent.java | 6 + .../bukkit/listener/BlacklistListener.java | 70 ++--- .../listener/ChestProtectionListener.java | 89 ++++--- .../bukkit/listener/DebuggingListener.java | 77 ++++-- .../listener/EventAbstractionListener.java | 23 +- .../listener/RegionProtectionListener.java | 242 ++++++++++++++---- .../listener/WorldGuardEntityListener.java | 36 --- .../worldguard/bukkit/util/Entities.java | 8 +- .../sk89q/worldguard/bukkit/util/Events.java | 20 ++ 16 files changed, 582 insertions(+), 235 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/bukkit/event/BulkEvent.java diff --git a/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java b/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java index b483b9a9..d82beecf 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/BukkitUtil.java @@ -42,7 +42,6 @@ import org.bukkit.entity.Tameable; import org.bukkit.inventory.ItemStack; -import javax.annotation.Nullable; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -320,9 +319,9 @@ public static Target createTarget(Block block) { * @param material a fallback material * @return a target */ - public static Target createTarget(@Nullable Block block, Material material) { + public static Target createTarget(Block block, Material material) { checkNotNull(material); - if (block != null) { + if (block.getType() == material) { return new MaterialTarget(block.getTypeId(), block.getData()); } else { return new MaterialTarget(material.getId(), (short) 0); diff --git a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java index 72d06bd4..7b6a7fb4 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/RegionQuery.java @@ -110,23 +110,7 @@ public ApplicableRegionSet queryContains(Location location) { * @throws NullPointerException if there is no player for this query */ public boolean testPermission(Location location, Player player) { - checkNotNull(location); - checkNotNull(player); - - LocalPlayer localPlayer = plugin.wrapPlayer(player); - World world = location.getWorld(); - WorldConfiguration worldConfig = config.get(world); - - if (!worldConfig.useRegions) { - return true; - } - - if (globalManager.hasBypass(localPlayer, world)) { - return true; - } else { - RegionManager manager = globalManager.get(location.getWorld()); - return manager == null || cache.queryContains(manager, location).canBuild(localPlayer); - } + return testPermission(location, player, new StateFlag[0]); } /** @@ -151,8 +135,8 @@ public boolean testPermission(Location location, Player player) { */ public boolean testPermission(Location location, Player player, StateFlag... flags) { checkNotNull(location); - checkNotNull(flags); checkNotNull(player); + checkNotNull(flags); LocalPlayer localPlayer = plugin.wrapPlayer(player); World world = location.getWorld(); @@ -162,6 +146,10 @@ public boolean testPermission(Location location, Player player, StateFlag... fla return true; } + if (player.hasPermission("worldguard.region.bypass." + world.getName())) { + return true; + } + RegionManager manager = globalManager.get(location.getWorld()); if (manager != null) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java b/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java index a114578d..1047fa2e 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/cause/Cause.java @@ -20,10 +20,14 @@ package com.sk89q.worldguard.bukkit.cause; import com.google.common.base.Joiner; +import com.sk89q.worldguard.bukkit.util.WGMetadata; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; +import org.bukkit.entity.TNTPrimed; import org.bukkit.entity.Tameable; import org.bukkit.entity.Vehicle; +import org.bukkit.metadata.Metadatable; import javax.annotation.Nullable; import java.util.ArrayList; @@ -43,6 +47,7 @@ */ public class Cause { + private static final String CAUSE_KEY = "worldguard.cause"; private static final Cause UNKNOWN = new Cause(Collections.emptyList()); private final List causes; @@ -77,6 +82,17 @@ public Player getPlayerRootCause() { return null; } + @Nullable + public Entity getEntityRootCause() { + for (Object object : causes) { + if (object instanceof Entity) { + return (Entity) object; + } + } + + return null; + } + @Override public String toString() { return Joiner.on(" | ").join(causes); @@ -95,12 +111,28 @@ private static void expand(List list, @Nullable Object ... element) { continue; } - if (o instanceof Projectile) { + // Add manually tracked parent causes + Object source = o; + int index = list.size(); + while (source instanceof Metadatable) { + source = WGMetadata.getIfPresent((Metadatable) source, CAUSE_KEY, Object.class); + if (source != null) { + list.add(index, source); + } + } + + if (o instanceof TNTPrimed) { + expand(list, ((TNTPrimed) o).getSource()); + list.add(o); + } else if (o instanceof Projectile) { expand(list, ((Projectile) o).getShooter()); + list.add(o); } else if (o instanceof Vehicle) { expand(list, ((Vehicle) o).getPassenger()); + list.add(o); } else if (o instanceof Tameable) { expand(list, ((Tameable) o).getOwner()); + list.add(o); } else { list.add(o); } @@ -135,4 +167,14 @@ public static Cause unknown() { return UNKNOWN; } + /** + * Add a parent cause to a {@code Metadatable} object. + * + * @param target the target + * @param parent the parent cause + */ + public static void trackParentCause(Metadatable target, Object parent) { + WGMetadata.put(target, CAUSE_KEY, parent); + } + } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/BulkEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/BulkEvent.java new file mode 100644 index 00000000..20df01b2 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/BulkEvent.java @@ -0,0 +1,38 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.event; + +/** + * A bulk event contains several affected objects in a list. + */ +public interface BulkEvent { + + /** + * Return whether the event is explicitly cancelled. + * + *

By default, bulk events will cancel itself if the number of affected + * objects drops to zero. This method returns the true cancellation + * status.

+ * + * @return true if really cancelled + */ + boolean isExplicitlyCancelled(); + +} diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java index 6ebd57bf..bbd013af 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/AbstractBlockEvent.java @@ -19,56 +19,125 @@ package com.sk89q.worldguard.bukkit.event.block; +import com.google.common.base.Predicate; import com.sk89q.worldguard.bukkit.cause.Cause; import com.sk89q.worldguard.bukkit.event.AbstractDelegateEvent; +import com.sk89q.worldguard.bukkit.event.BulkEvent; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.event.Event; import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; -abstract class AbstractBlockEvent extends AbstractDelegateEvent { +abstract class AbstractBlockEvent extends AbstractDelegateEvent implements BulkEvent { - private final Location target; - @Nullable - private final Block block; + private final World world; + private final List blocks; private final Material effectiveMaterial; - protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { + protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List blocks, Material effectiveMaterial) { super(originalEvent, cause); - checkNotNull(block); - this.target = block.getLocation(); - this.block = block; - this.effectiveMaterial = block.getType(); - } - - protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { - super(originalEvent, cause); - this.target = target; - this.block = null; + checkNotNull(world); + checkNotNull(blocks); + checkNotNull(effectiveMaterial); + this.world = world; + this.blocks = blocks; this.effectiveMaterial = effectiveMaterial; } - /** - * Get the target block being affected. - * - * @return a block - */ - public Location getTarget() { - return target; + protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { + this(originalEvent, cause, block.getWorld(), createList(checkNotNull(block)), block.getType()); + } + + protected AbstractBlockEvent(@Nullable Event originalEvent, Cause cause, Location target, Material effectiveMaterial) { + this(originalEvent, cause, target.getWorld(), createList(target.getBlock()), effectiveMaterial); + } + + private static List createList(Block block) { + List blocks = new ArrayList(); + blocks.add(block); + return blocks; + } + + @Override + public boolean isCancelled() { + return super.isCancelled() || blocks.isEmpty(); + } + + @Override + public boolean isExplicitlyCancelled() { + return super.isCancelled(); } /** - * Get the block. + * Get the world. * - * @return the block + * @return the world */ - @Nullable - public Block getBlock() { - return block; + public World getWorld() { + return world; + } + + /** + * Get the affected blocks. + * + * @return a list of affected block + */ + public List getBlocks() { + return blocks; + } + + /** + * Filter the list of affected blocks with the given predicate. If the + * predicate returns {@code false}, then the block is removed. + * + * @param predicate the predicate + * @param cancelEventOnFalse true to cancel the event and clear the block + * list once the predicate returns {@code false} + * @return true if one or more blocks were filtered out + */ + public boolean filterBlocks(Predicate predicate, boolean cancelEventOnFalse) { + boolean hasRemoval = false; + + Iterator it = blocks.iterator(); + while (it.hasNext()) { + if (!predicate.apply(it.next().getLocation())) { + hasRemoval = true; + + if (cancelEventOnFalse) { + getBlocks().clear(); + setCancelled(true); + break; + } else { + it.remove(); + } + } + } + + return hasRemoval; + } + + /** + * Filter the list of affected blocks with the given predicate. If the + * predicate returns {@code false}, then the block is removed. + * + *

This method will not fail fast and + * cancel the event the first instance that the predicate returns + * {@code false}. See {@link #filterBlocks(Predicate, boolean)} to adjust + * this behavior.

+ * + * @param predicate the predicate + * @return true if one or more blocks were filtered out + */ + public boolean filterBlocks(Predicate predicate) { + return filterBlocks(predicate, false); } /** diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java index 097eeaef..f5cc3088 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/BreakBlockEvent.java @@ -22,16 +22,22 @@ import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import javax.annotation.Nullable; +import java.util.List; public class BreakBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); + public BreakBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List blocks, Material effectiveMaterial) { + super(originalEvent, cause, world, blocks, effectiveMaterial); + } + public BreakBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { super(originalEvent, cause, block); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java index c6dd711d..ea8f1d49 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/PlaceBlockEvent.java @@ -22,16 +22,22 @@ import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import javax.annotation.Nullable; +import java.util.List; public class PlaceBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); + public PlaceBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List blocks, Material effectiveMaterial) { + super(originalEvent, cause, world, blocks, effectiveMaterial); + } + public PlaceBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { super(originalEvent, cause, block); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java b/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java index 5c581196..921043ed 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/event/block/UseBlockEvent.java @@ -22,11 +22,13 @@ import com.sk89q.worldguard.bukkit.cause.Cause; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import javax.annotation.Nullable; +import java.util.List; /** * Fired when a block is interacted with. @@ -35,6 +37,10 @@ public class UseBlockEvent extends AbstractBlockEvent { private static final HandlerList handlers = new HandlerList(); + public UseBlockEvent(@Nullable Event originalEvent, Cause cause, World world, List blocks, Material effectiveMaterial) { + super(originalEvent, cause, world, blocks, effectiveMaterial); + } + public UseBlockEvent(@Nullable Event originalEvent, Cause cause, Block block) { super(originalEvent, cause, block); } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java index 549981fe..3149071e 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/BlacklistListener.java @@ -19,6 +19,7 @@ package com.sk89q.worldguard.bukkit.listener; +import com.google.common.base.Predicate; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.blacklist.event.BlockBreakBlacklistEvent; import com.sk89q.worldguard.blacklist.event.BlockDispenseBlacklistEvent; @@ -38,8 +39,8 @@ import com.sk89q.worldguard.bukkit.event.entity.SpawnEntityEvent; import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; import com.sk89q.worldguard.bukkit.util.Materials; +import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.Item; import org.bukkit.entity.Player; @@ -68,75 +69,86 @@ public BlacklistListener(WorldGuardPlugin plugin) { } @EventHandler(ignoreCancelled = true) - public void onBreakBlock(BreakBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); + public void onBreakBlock(final BreakBlockEvent event) { + final Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; } - LocalPlayer localPlayer = getPlugin().wrapPlayer(player); - Block target = event.getBlock(); - WorldConfiguration wcfg = getWorldConfig(player); + final LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + final WorldConfiguration wcfg = getWorldConfig(player); // Blacklist guard if (wcfg.getBlacklist() == null) { return; } - if (!wcfg.getBlacklist().check( - new BlockBreakBlacklistEvent(localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiveMaterial())), false, false)) { - event.setCancelled(true); - } else if (!wcfg.getBlacklist().check( - new ItemDestroyWithBlacklistEvent(localPlayer, toVector(event.getTarget()), createTarget(player.getItemInHand())), false, false)) { - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + if (!wcfg.getBlacklist().check( + new BlockBreakBlacklistEvent(localPlayer, toVector(target), createTarget(target.getBlock(), event.getEffectiveMaterial())), false, false)) { + return false; + } else if (!wcfg.getBlacklist().check( + new ItemDestroyWithBlacklistEvent(localPlayer, toVector(target), createTarget(player.getItemInHand())), false, false)) { + return false; + } + + return true; + } + }); } @EventHandler(ignoreCancelled = true) - public void onPlaceBlock(PlaceBlockEvent event) { + public void onPlaceBlock(final PlaceBlockEvent event) { Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; } - LocalPlayer localPlayer = getPlugin().wrapPlayer(player); - Block target = event.getBlock(); - WorldConfiguration wcfg = getWorldConfig(player); + final LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + final WorldConfiguration wcfg = getWorldConfig(player); // Blacklist guard if (wcfg.getBlacklist() == null) { return; } - if (!wcfg.getBlacklist().check(new BlockPlaceBlacklistEvent( - localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiveMaterial())), false, false)) { - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + return wcfg.getBlacklist().check(new BlockPlaceBlacklistEvent( + localPlayer, toVector(target), createTarget(target.getBlock(), event.getEffectiveMaterial())), false, false); + + } + }); } @EventHandler(ignoreCancelled = true) - public void onUseBlock(UseBlockEvent event) { + public void onUseBlock(final UseBlockEvent event) { Player player = event.getCause().getPlayerRootCause(); if (player == null) { return; } - LocalPlayer localPlayer = getPlugin().wrapPlayer(player); - Block target = event.getBlock(); - WorldConfiguration wcfg = getWorldConfig(player); + final LocalPlayer localPlayer = getPlugin().wrapPlayer(player); + final WorldConfiguration wcfg = getWorldConfig(player); // Blacklist guard if (wcfg.getBlacklist() == null) { return; } - if (!wcfg.getBlacklist().check(new BlockInteractBlacklistEvent( - localPlayer, toVector(event.getTarget()), createTarget(target, event.getEffectiveMaterial())), false, false)) { - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + return wcfg.getBlacklist().check(new BlockInteractBlacklistEvent( + localPlayer, toVector(target), createTarget(target.getBlock(), event.getEffectiveMaterial())), false, false); + } + }); } @EventHandler(ignoreCancelled = true) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java index 9f8536c0..a2abeb54 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/ChestProtectionListener.java @@ -19,6 +19,7 @@ package com.sk89q.worldguard.bukkit.listener; +import com.google.common.base.Predicate; import com.sk89q.worldedit.blocks.BlockID; import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; @@ -46,31 +47,36 @@ public ChestProtectionListener(WorldGuardPlugin plugin) { } @EventHandler(ignoreCancelled = true) - public void onPlaceBlock(PlaceBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); + public void onPlaceBlock(final PlaceBlockEvent event) { + final Player player = event.getCause().getPlayerRootCause(); if (player != null) { - WorldConfiguration wcfg = getWorldConfig(player); + final WorldConfiguration wcfg = getWorldConfig(player); // Early guard if (!wcfg.signChestProtection) { return; } - if (wcfg.getChestProtection().isChest(event.getEffectiveMaterial().getId()) && wcfg.isChestProtected(target.getBlock(), player)) { - player.sendMessage(ChatColor.DARK_RED + "This spot is for a chest that you don't have permission for."); - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + if (wcfg.getChestProtection().isChest(event.getEffectiveMaterial().getId()) && wcfg.isChestProtected(target.getBlock(), player)) { + player.sendMessage(ChatColor.DARK_RED + "This spot is for a chest that you don't have permission for."); + return false; + } + + return true; + } + }, true); } } @EventHandler(ignoreCancelled = true) - public void onBreakBlock(BreakBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); + public void onBreakBlock(final BreakBlockEvent event) { + final Player player = event.getCause().getPlayerRootCause(); - WorldConfiguration wcfg = getWorldConfig(target.getWorld()); + final WorldConfiguration wcfg = getWorldConfig(event.getWorld()); // Early guard if (!wcfg.signChestProtection) { @@ -78,24 +84,33 @@ public void onBreakBlock(BreakBlockEvent event) { } if (player != null) { - if (wcfg.isChestProtected(target.getBlock(), player)) { - player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + if (wcfg.isChestProtected(target.getBlock(), player)) { + player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); + return false; + } + + return true; + } + }, true); } else { - if (wcfg.isChestProtected(target.getBlock())) { - // No player? Deny anyway - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + return !wcfg.isChestProtected(target.getBlock()); + + } + }); } } @EventHandler(ignoreCancelled = true) - public void onUseBlock(UseBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); + public void onUseBlock(final UseBlockEvent event) { + final Player player = event.getCause().getPlayerRootCause(); - WorldConfiguration wcfg = getWorldConfig(target.getWorld()); + final WorldConfiguration wcfg = getWorldConfig(event.getWorld()); // Early guard if (!wcfg.signChestProtection) { @@ -103,15 +118,25 @@ public void onUseBlock(UseBlockEvent event) { } if (player != null) { - if (wcfg.isChestProtected(target.getBlock(), player)) { - player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + if (wcfg.isChestProtected(target.getBlock(), player)) { + player.sendMessage(ChatColor.DARK_RED + "This chest is protected."); + return false; + } + + return true; + } + }, true); } else { - if (wcfg.isChestProtected(target.getBlock())) { - // No player? Deny anyway - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + return !wcfg.isChestProtected(target.getBlock()); + + } + }); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java index 4943be13..3f527b4c 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/DebuggingListener.java @@ -28,8 +28,13 @@ import com.sk89q.worldguard.bukkit.event.entity.UseEntityEvent; import com.sk89q.worldguard.bukkit.event.inventory.UseItemEvent; import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.event.Event; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import javax.annotation.Nullable; +import java.util.List; import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; @@ -50,22 +55,25 @@ public DebuggingListener(WorldGuardPlugin plugin, Logger logger) { this.logger = logger; } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onPlaceBlock(PlaceBlockEvent event) { StringBuilder builder = new StringBuilder(); builder.append("PLACE"); builder.append(" "); builder.append("").append(event.getEffectiveMaterial()); builder.append(" "); - builder.append("@").append(toBlockString(event.getTarget())); + builder.append("@").append(toBlockString(event.getBlocks())); builder.append(" "); builder.append("[").append(event.getCause()).append("]"); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onBreakBlock(BreakBlockEvent event) { StringBuilder builder = new StringBuilder(); builder.append("DIG"); @@ -74,13 +82,16 @@ public void onBreakBlock(BreakBlockEvent event) { builder.append(" "); builder.append("[").append(event.getCause()).append("]"); builder.append(" "); - builder.append("@").append(toBlockString(event.getTarget())); + builder.append("@").append(toBlockString(event.getBlocks())); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onUseBlock(UseBlockEvent event) { StringBuilder builder = new StringBuilder(); builder.append("INTERACT"); @@ -89,13 +100,16 @@ public void onUseBlock(UseBlockEvent event) { builder.append(" "); builder.append("[").append(event.getCause()).append("]"); builder.append(" "); - builder.append("@").append(toBlockString(event.getTarget())); + builder.append("@").append(toBlockString(event.getBlocks())); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onSpawnEntity(SpawnEntityEvent event) { StringBuilder builder = new StringBuilder(); builder.append("SPAWN"); @@ -106,11 +120,14 @@ public void onSpawnEntity(SpawnEntityEvent event) { builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onDestroyEntity(DestroyEntityEvent event) { StringBuilder builder = new StringBuilder(); builder.append("DESTROY"); @@ -121,11 +138,14 @@ public void onDestroyEntity(DestroyEntityEvent event) { builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onUseEntity(UseEntityEvent event) { StringBuilder builder = new StringBuilder(); builder.append("INTERACT"); @@ -136,11 +156,14 @@ public void onUseEntity(UseEntityEvent event) { builder.append(" "); builder.append("@").append(toBlockString(event.getTarget())); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } - @EventHandler + @EventHandler(priority = EventPriority.MONITOR) public void onUseItem(UseItemEvent event) { StringBuilder builder = new StringBuilder(); builder.append("USE"); @@ -151,7 +174,10 @@ public void onUseItem(UseItemEvent event) { builder.append(" "); builder.append("@").append(event.getWorld().getName()); builder.append(" "); - builder.append(":").append(event.getOriginalEvent().getEventName()); + builder.append(":").append(getEventName(event.getOriginalEvent())); + if (event.isCancelled()) { + builder.append(" [CANCELLED]"); + } logger.info(builder.toString()); } @@ -159,4 +185,21 @@ private static String toBlockString(Location location) { return location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ(); } + private static String toBlockString(List blocks) { + StringBuilder builder = new StringBuilder(); + boolean first = true; + for (Block block : blocks) { + if (!first) { + builder.append("|"); + } + builder.append(block.getX()).append(",").append(block.getY()).append(",").append(block.getZ()); + first = false; + } + return builder.toString(); + } + + private String getEventName(@Nullable Event event) { + return event != null ? event.getEventName() : "?"; + } + } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java index c4e83da4..95690988 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java @@ -31,7 +31,6 @@ import com.sk89q.worldguard.bukkit.util.Blocks; import com.sk89q.worldguard.bukkit.util.Events; import com.sk89q.worldguard.bukkit.util.Materials; -import com.sk89q.worldguard.bukkit.util.WGMetadata; import org.bukkit.DyeColor; import org.bukkit.Material; import org.bukkit.World; @@ -67,6 +66,7 @@ import org.bukkit.event.entity.EntityDamageByBlockEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.EntityTameEvent; import org.bukkit.event.entity.EntityUnleashEvent; import org.bukkit.event.entity.PotionSplashEvent; @@ -101,7 +101,6 @@ public class EventAbstractionListener implements Listener { * Currently disabled as it creates a lot of new events. */ public static final boolean ABSTRACT_FROM_TO_EVENTS = false; - private static final String FALLING_SOURCE_KEY = "worldguard.fallingSource"; private final WorldGuardPlugin plugin; @@ -155,7 +154,7 @@ public void onEntityChangeBlock(EntityChangeBlockEvent event) { if (event.getTo() == Material.AIR) { // Track the source so later we can create a proper chain of causes if (entity instanceof FallingBlock) { - WGMetadata.put(entity, FALLING_SOURCE_KEY, block); + Cause.trackParentCause(entity, block); // Switch around the event Events.fireToCancel(event, new SpawnEntityEvent(event, create(block), entity)); @@ -163,23 +162,21 @@ public void onEntityChangeBlock(EntityChangeBlockEvent event) { Events.fireToCancel(event, new BreakBlockEvent(event, create(entity), event.getBlock())); } } else { - Cause cause; - - // Return the source for falling blocks - if (entity instanceof FallingBlock) { - Block source = WGMetadata.getIfPresent(entity, FALLING_SOURCE_KEY, Block.class); - cause = create(source, entity); - } else { - cause = create(entity); - } + Cause cause = create(entity); Events.fireToCancel(event, new PlaceBlockEvent(event, cause, event.getBlock().getLocation(), to)); } } } + @EventHandler + public void onEntityExplode(EntityExplodeEvent event) { + Entity entity = event.getEntity(); + + Events.fireBulkEventToCancel(event, new BreakBlockEvent(event, create(entity), event.getLocation().getWorld(), event.blockList(), Material.AIR)); + } + // TODO: Handle pistons - // TODO: Handle EntityExplodeEvent //------------------------------------------------------------------------- // Block external interaction diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java index cd02b8ff..d87005ea 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/RegionProtectionListener.java @@ -19,8 +19,12 @@ package com.sk89q.worldguard.bukkit.listener; +import com.google.common.base.Predicate; +import com.sk89q.worldguard.bukkit.ConfigurationManager; import com.sk89q.worldguard.bukkit.RegionQuery; +import com.sk89q.worldguard.bukkit.WorldConfiguration; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import com.sk89q.worldguard.bukkit.cause.Cause; import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent; import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent; import com.sk89q.worldguard.bukkit.event.block.UseBlockEvent; @@ -30,14 +34,20 @@ import com.sk89q.worldguard.bukkit.util.Entities; import com.sk89q.worldguard.bukkit.util.Materials; import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.StateFlag; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Creeper; +import org.bukkit.entity.EnderDragon; +import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import javax.annotation.Nullable; + /** * Handle events that need to be processed by region protection. */ @@ -52,92 +62,196 @@ public RegionProtectionListener(WorldGuardPlugin plugin) { super(plugin); } + /** + * Tell a sender that s/he cannot do something 'here'. + * + * @param sender the sender + * @param subject the subject that the sender was blocked from touching + */ private void tellErrorMessage(CommandSender sender, Object subject) { sender.sendMessage(ChatColor.DARK_RED + "Sorry, but you are not allowed to do that here."); } + /** + * Return whether the given cause is whitelist (should be ignored). + * + * @param cause the cause + * @return true if whitelisted + */ + private boolean isWhitelisted(Cause cause) { + return false; + } + + /** + * Create a new predicate to test a state flag for each location. + * + * @param query the query + * @param flag the flag + * @return a predicate + */ + private Predicate createStateFlagPredicate(final RegionQuery query, final StateFlag flag) { + return new Predicate() { + @Override + public boolean apply(@Nullable Location location) { + return query.testState(location, null, flag); + } + }; + } + @EventHandler(ignoreCancelled = true) - public void onPlaceBlock(PlaceBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); - Material type = event.getEffectiveMaterial(); + public void onPlaceBlock(final PlaceBlockEvent event) { + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } - if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(); - boolean canPlace; + final Material type = event.getEffectiveMaterial(); - // Flint and steel, fire charge - if (type == Material.FIRE) { - canPlace = query.testPermission(target, player, DefaultFlag.LIGHTER); + final Player player; - } else { - canPlace = query.testPermission(target, player); - } + if ((player = event.getCause().getPlayerRootCause()) != null) { + final RegionQuery query = getPlugin().getRegionContainer().createQuery(); - if (!canPlace) { - tellErrorMessage(player, target); - event.setCancelled(true); - } + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + boolean canPlace; + + // Flint and steel, fire charge + if (type == Material.FIRE) { + canPlace = query.testPermission(target, player, DefaultFlag.LIGHTER); + + } else { + canPlace = query.testPermission(target, player); + } + + if (!canPlace) { + tellErrorMessage(player, target); + return false; + } + + return true; + } + }); + } else { + event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) - public void onBreakBlock(BreakBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); + public void onBreakBlock(final BreakBlockEvent event) { + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } - if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(); - boolean canBreak = query.testPermission(target, player); + final RegionQuery query = getPlugin().getRegionContainer().createQuery(); + ConfigurationManager globalConfig = getPlugin().getGlobalStateManager(); + WorldConfiguration config = globalConfig.get(event.getWorld()); + + final Player player; + final Entity entity; + + // ==================================================================== + // Player caused + // ==================================================================== + + if ((player = event.getCause().getPlayerRootCause()) != null) { + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + boolean canBreak = query.testPermission(target, player); + + if (!canBreak) { + tellErrorMessage(player, target); + return false; + } + + return true; + } + }); + + // ==================================================================== + // Entity caused + // ==================================================================== + + } else if ((entity = event.getCause().getEntityRootCause()) != null) { + // Creeper + if (entity instanceof Creeper) { + event.filterBlocks(createStateFlagPredicate(query, DefaultFlag.CREEPER_EXPLOSION), config.explosionFlagCancellation); + + // Enderdragon + } else if (entity instanceof EnderDragon) { + event.filterBlocks(createStateFlagPredicate(query, DefaultFlag.ENDERDRAGON_BLOCK_DAMAGE), config.explosionFlagCancellation); + + // TNT + explosive TNT carts + } else if (Entities.isTNTBased(entity)) { + event.filterBlocks(createStateFlagPredicate(query, DefaultFlag.TNT), config.explosionFlagCancellation); - if (!canBreak) { - tellErrorMessage(player, target); - event.setCancelled(true); } + } else { + event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) - public void onUseBlock(UseBlockEvent event) { - Player player = event.getCause().getPlayerRootCause(); - Location target = event.getTarget(); - Material type = event.getEffectiveMaterial(); + public void onUseBlock(final UseBlockEvent event) { + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } - if (player != null) { - RegionQuery query = getPlugin().getRegionContainer().createQuery(); - boolean canUse; + final Material type = event.getEffectiveMaterial(); - // Inventory blocks (CHEST_ACCESS) - if (Materials.isInventoryBlock(type)) { - canUse = query.testPermission(target, player, DefaultFlag.USE, DefaultFlag.CHEST_ACCESS); + final Player player; - // Beds (SLEEP) - } else if (type == Material.BED) { - canUse = query.testPermission(target, player, DefaultFlag.USE, DefaultFlag.SLEEP); + if ((player = event.getCause().getPlayerRootCause()) != null) { + final RegionQuery query = getPlugin().getRegionContainer().createQuery(); - // TNT (TNT) - } else if (type == Material.TNT) { - canUse = query.testPermission(target, player, DefaultFlag.TNT); + event.filterBlocks(new Predicate() { + @Override + public boolean apply(Location target) { + boolean canUse; - // Everything else - } else { - canUse = query.testPermission(target, player, DefaultFlag.USE); - } + // Inventory blocks (CHEST_ACCESS) + if (Materials.isInventoryBlock(type)) { + canUse = query.testPermission(target, player, DefaultFlag.USE, DefaultFlag.CHEST_ACCESS); - if (!canUse) { - tellErrorMessage(player, target); - event.setCancelled(true); - } + // Beds (SLEEP) + } else if (type == Material.BED) { + canUse = query.testPermission(target, player, DefaultFlag.USE, DefaultFlag.SLEEP); + + // TNT (TNT) + } else if (type == Material.TNT) { + canUse = query.testPermission(target, player, DefaultFlag.TNT); + + // Everything else + } else { + canUse = query.testPermission(target, player, DefaultFlag.USE); + } + + if (!canUse) { + tellErrorMessage(player, target); + return false; + } + + return true; + } + }); + } else { + event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) public void onSpawnEntity(SpawnEntityEvent event) { - Player player = event.getCause().getPlayerRootCause(); + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } + Location target = event.getTarget(); EntityType type = event.getEffectiveType(); - if (player != null) { + Player player; + + if ((player = event.getCause().getPlayerRootCause()) != null) { RegionQuery query = getPlugin().getRegionContainer().createQuery(); boolean canSpawn; @@ -151,16 +265,23 @@ public void onSpawnEntity(SpawnEntityEvent event) { tellErrorMessage(player, target); event.setCancelled(true); } + } else { + event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) public void onDestroyEntity(DestroyEntityEvent event) { - Player player = event.getCause().getPlayerRootCause(); + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } + Location target = event.getTarget(); EntityType type = event.getEntity().getType(); - if (player != null) { + Player player; + + if ((player = event.getCause().getPlayerRootCause()) != null) { RegionQuery query = getPlugin().getRegionContainer().createQuery(); boolean canDestroy; @@ -174,15 +295,22 @@ public void onDestroyEntity(DestroyEntityEvent event) { tellErrorMessage(player, target); event.setCancelled(true); } + } else { + event.setCancelled(true); } } @EventHandler(ignoreCancelled = true) public void onUseEntity(UseEntityEvent event) { - Player player = event.getCause().getPlayerRootCause(); + if (isWhitelisted(event.getCause())) { + return; // Whitelisted cause + } + Location target = event.getTarget(); - if (player != null) { + Player player; + + if ((player = event.getCause().getPlayerRootCause()) != null) { RegionQuery query = getPlugin().getRegionContainer().createQuery(); boolean canUse = query.testPermission(target, player, DefaultFlag.USE); @@ -190,6 +318,8 @@ public void onUseEntity(UseEntityEvent event) { tellErrorMessage(player, target); event.setCancelled(true); } + } else { + event.setCancelled(true); } } diff --git a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java index ad740a6e..89031adc 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardEntityListener.java @@ -569,35 +569,11 @@ public void onEntityExplode(EntityExplodeEvent event) { event.blockList().clear(); return; } - - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - - for (Block block : event.blockList()) { - if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.CREEPER_EXPLOSION)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } } else if (ent instanceof EnderDragon) { if (wcfg.blockEnderDragonBlockDamage) { event.blockList().clear(); return; } - - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - - for (Block block : event.blockList()) { - if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.ENDERDRAGON_BLOCK_DAMAGE)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } } else if (ent instanceof TNTPrimed || ent instanceof ExplosiveMinecart) { if (wcfg.blockTNTExplosions) { event.setCancelled(true); @@ -607,18 +583,6 @@ public void onEntityExplode(EntityExplodeEvent event) { event.blockList().clear(); return; } - - if (wcfg.useRegions) { - RegionManager mgr = plugin.getGlobalRegionManager().get(world); - - for (Block block : event.blockList()) { - if (!plugin.getRegionContainer().createQuery().queryContains(block.getLocation()).allows(DefaultFlag.TNT)) { - event.blockList().clear(); - if (wcfg.explosionFlagCancellation) event.setCancelled(true); - return; - } - } - } } else if (ent instanceof Fireball) { if (ent instanceof WitherSkull) { if (wcfg.blockWitherSkullExplosions) { diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java index 765b9f0e..4704a342 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Entities.java @@ -22,7 +22,9 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Projectile; +import org.bukkit.entity.TNTPrimed; import org.bukkit.entity.Tameable; +import org.bukkit.entity.minecart.ExplosiveMinecart; import org.bukkit.projectiles.ProjectileSource; import javax.annotation.Nullable; @@ -45,11 +47,11 @@ public static boolean isTamed(@Nullable Entity entity) { /** * Return if the given entity type is TNT-based. * - * @param type the type + * @param entity the entity * @return true if TNT based */ - public static boolean isTNTBased(EntityType type) { - return type == EntityType.PRIMED_TNT || type == EntityType.MINECART_TNT; + public static boolean isTNTBased(Entity entity) { + return entity instanceof TNTPrimed || entity instanceof ExplosiveMinecart; } /** diff --git a/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java b/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java index 6ad8ec73..637fe1fe 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/util/Events.java @@ -19,6 +19,7 @@ package com.sk89q.worldguard.bukkit.util; +import com.sk89q.worldguard.bukkit.event.BulkEvent; import org.bukkit.Bukkit; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; @@ -83,4 +84,23 @@ public static boolean fireItemEventToCancel(Play return false; } + /** + * Fire the {@code eventToFire} and cancel the original if the fired event + * is explicitly cancelled. + * + * @param original the original event to potentially cancel + * @param eventToFire the event to fire to consider cancelling the original event + * @param an event that can be fired and is cancellable + * @return true if the event was fired and it caused the original event to be cancelled + */ + public static boolean fireBulkEventToCancel(Cancellable original, T eventToFire) { + Bukkit.getServer().getPluginManager().callEvent(eventToFire); + if (eventToFire.isExplicitlyCancelled()) { + original.setCancelled(true); + return true; + } + + return false; + } + } From d3f3489c7eb47bcfeb258986df89e338f2d9c278 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 15 Aug 2014 03:48:25 -0700 Subject: [PATCH 051/133] Fix child regions not inheriting parent flags. This also fixes a unit test. --- .../protection/ApplicableRegionSet.java | 27 ++++++++++++++++--- .../protection/RegionPriorityTest.java | 16 +++++++---- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java index 878aff2d..50bf78b0 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java +++ b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java @@ -251,8 +251,7 @@ private boolean internalGetState(StateFlag flag, @Nullable LocalPlayer player, @ lastPriority = region.getPriority(); // Ignore non-build regions - if (player != null - && region.getFlag(DefaultFlag.PASSTHROUGH) == State.ALLOW) { + if (player != null && getStateFlagIncludingParents(region, DefaultFlag.PASSTHROUGH) == State.ALLOW) { continue; } @@ -267,7 +266,7 @@ private boolean internalGetState(StateFlag flag, @Nullable LocalPlayer player, @ } } - State v = region.getFlag(flag); + State v = getStateFlagIncludingParents(region, flag); // Allow DENY to override everything if (v == State.DENY) { @@ -327,6 +326,28 @@ private void clearParents(Set needsClear, Set } } + /** + * Get a region's state flag, checking parent regions until a value for the + * flag can be found (if one even exists). + * + * @param region the region + * @param flag the flag + * @return the value + */ + private static State getStateFlagIncludingParents(ProtectedRegion region, StateFlag flag) { + while (region != null) { + State value = region.getFlag(flag); + + if (value != null) { + return value; + } + + region = region.getParent(); + } + + return null; + } + /** * Gets the value of a flag. Do not use this for state flags * (use {@link #allows(StateFlag, LocalPlayer)} for that). diff --git a/src/test/java/com/sk89q/worldguard/protection/RegionPriorityTest.java b/src/test/java/com/sk89q/worldguard/protection/RegionPriorityTest.java index 6b7be7bc..e08ca413 100644 --- a/src/test/java/com/sk89q/worldguard/protection/RegionPriorityTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/RegionPriorityTest.java @@ -19,8 +19,6 @@ package com.sk89q.worldguard.protection; -import java.util.ArrayList; -import org.junit.Before; import com.sk89q.worldedit.BlockVector; import com.sk89q.worldedit.BlockVector2D; import com.sk89q.worldedit.Vector; @@ -29,9 +27,17 @@ import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.managers.RegionManager; -import com.sk89q.worldguard.protection.regions.*; +import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; + +import java.util.ArrayList; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public abstract class RegionPriorityTest { static String COURTYARD_ID = "courtyard"; @@ -143,7 +149,7 @@ public void testPriorities() throws Exception { public void testPriorities2() throws Exception { ApplicableRegionSet appl; - fountain.setPriority(0); + courtyard.setPriority(0); fountain.setPriority(5); appl = manager.getApplicableRegions(inCourtyard); From 1d24be8c3477816390e256813d6e1a0c1618a304 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 15 Aug 2014 12:30:37 -0700 Subject: [PATCH 052/133] Rewrite state calculation in ApplicableRegionSet to be easier to read. --- .../protection/ApplicableRegionSet.java | 225 ++++++++++-------- .../protection/flags/StateFlag.java | 57 +++++ 2 files changed, 177 insertions(+), 105 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java index 50bf78b0..714ed827 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java +++ b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java @@ -41,6 +41,7 @@ import java.util.TreeSet; import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldguard.protection.flags.StateFlag.*; /** * Represents the effective set of flags, owners, and members for a given @@ -92,7 +93,7 @@ public ApplicableRegionSet(SortedSet applicable, @Nullable Prot */ public boolean canBuild(LocalPlayer player) { checkNotNull(player); - return internalGetState(DefaultFlag.BUILD, player, null); + return test(calculateState(DefaultFlag.BUILD, player, null)); } /** @@ -121,7 +122,7 @@ public boolean allows(StateFlag flag) { throw new IllegalArgumentException("Can't use build flag with allows()"); } - return internalGetState(flag, null, null); + return test(calculateState(flag, null, null)); } /** @@ -138,7 +139,7 @@ public boolean allows(StateFlag flag, @Nullable LocalPlayer player) { if (flag == DefaultFlag.BUILD) { throw new IllegalArgumentException("Can't use build flag with allows()"); } - return internalGetState(flag, null, player); + return test(calculateState(flag, null, player)); } /** @@ -178,21 +179,126 @@ public boolean isMemberOfAll(LocalPlayer player) { } /** - * Test whether a flag tests true. + * Calculate the effective value of a flag based on the regions + * in this set, membership, the global region (if set), and the default + * value of a flag {@link StateFlag#getDefault()}. * * @param flag the flag to check * @param player the player, or null to not check owners and members * @param groupPlayer a player to use for the group flag check * @return the allow/deny state for the flag */ - private boolean internalGetState(StateFlag flag, @Nullable LocalPlayer player, @Nullable LocalPlayer groupPlayer) { + private State calculateState(StateFlag flag, @Nullable LocalPlayer player, @Nullable LocalPlayer groupPlayer) { checkNotNull(flag); - boolean found = false; - boolean hasFlagDefined = false; - boolean allowed = false; // Used for ALLOW override - boolean def = flag.getDefault(); - + int minimumPriority = Integer.MIN_VALUE; + boolean regionsThatCountExistHere = false; // We can't do a application.isEmpty() because + // PASSTHROUGH regions have to be skipped + // (in some cases) + State state = null; // Start with NONE + + // Say there are two regions in one location: CHILD and PARENT (CHILD + // is a child of PARENT). If there are two overlapping regions in WG, a + // player has to be a member of /both/ (or flags permit) in order to + // build in that location. However, inheritance is supposed + // to allow building if the player is a member of just CHILD. That + // presents a problem. + // + // To rectify this, we keep two sets. When we iterate over the list of + // regions, there are two scenarios that we may encounter: + // + // 1) PARENT first, CHILD later: + // PARENT and its parents are added to needsClear. + // When the loop reaches CHILD, all parents of CHILD (including + // PARENT) are removed from needsClear. (Any parents not in + // needsClear are added to hasCleared for the 2nd scenario.) + // + // 2) CHILD first, PARENT later: + // CHILD's parents are added to hasCleared. + // When the loop reaches PARENT, since PARENT is already in + // hasCleared, it doe not add PARENT to needsClear. + // + // If there are any regions left over in needsClear, that means that + // there was at least one region that the player is not a member of + // (any of its children) and thus we can deny building. + + Set needsClear = new HashSet(); + Set hasCleared = new HashSet(); + + for (ProtectedRegion region : applicable) { + // Don't consider lower priorities below minimumPriority + // (which starts at Integer.MIN_VALUE). A region that "counts" + // (has the flag set OR has members) will raise minimumPriority + // its own priority. + if (region.getPriority() < minimumPriority) { + break; + } + + // If PASSTHROUGH is set and we are checking to see if a player + // is a member, then skip this region + if (player != null && getStateFlagIncludingParents(region, DefaultFlag.PASSTHROUGH) == State.ALLOW) { + continue; + } + + // If the flag has a group set on to it, skip this region if + // the group does not match our (group) player + if (groupPlayer != null && flag.getRegionGroupFlag() != null) { + RegionGroup group = region.getFlag(flag.getRegionGroupFlag()); + if (group == null) { + group = flag.getRegionGroupFlag().getDefault(); + } + + if (!RegionGroupFlag.isMember(region, group, groupPlayer)) { + continue; + } + } + + regionsThatCountExistHere = true; + + State v = getStateFlagIncludingParents(region, flag); + + // DENY overrides everything + if (v == State.DENY) { + state = State.DENY; + break; // No need to process any more regions + + // ALLOW means we don't care about membership + } else if (v == State.ALLOW) { + state = State.ALLOW; + minimumPriority = region.getPriority(); + + } else { + if (player != null) { + minimumPriority = region.getPriority(); + + if (!hasCleared.contains(region)) { + if (!region.isMember(player)) { + needsClear.add(region); + } else { + // Need to clear all parents + clearParents(needsClear, hasCleared, region); + } + } + } + } + } + + if (regionsThatCountExistHere) { + if (player != null) { + State membership = allowOrNone(needsClear.isEmpty()); + return combine(state, membership); + } else { + return combine(state, getDefault(flag, null)); + } + } else { + return combine(getDefault(flag, player)); + } + } + + @Nullable + private State getDefault(StateFlag flag, @Nullable LocalPlayer player) { + boolean allowed = flag.getDefault(); + // Handle defaults if (globalRegion != null) { State globalState = globalRegion.getFlag(flag); @@ -201,110 +307,19 @@ private boolean internalGetState(StateFlag flag, @Nullable LocalPlayer player, @ if (globalState != null) { // Build flag is very special if (player != null && globalRegion.hasMembersOrOwners()) { - def = globalRegion.isMember(player) && (globalState == State.ALLOW); + allowed = globalRegion.isMember(player) && (globalState == State.ALLOW); } else { - def = (globalState == State.ALLOW); + allowed = (globalState == State.ALLOW); } } else { // Build flag is very special if (player != null && globalRegion.hasMembersOrOwners()) { - def = globalRegion.isMember(player); + allowed = globalRegion.isMember(player); } } } - - // The player argument is used if and only if the flag is the build - // flag -- in which case, if there are any regions in this area, we - // default to FALSE, otherwise true if there are no defined regions. - // However, other flags are different -- if there are regions defined, - // we default to the global region value. - if (player == null) { - allowed = def; - } - - int lastPriority = Integer.MIN_VALUE; - // The algorithm is as follows: - // While iterating through the list of regions, if an entry disallows - // the flag, then put it into the needsClear set. If an entry allows - // the flag and it has a parent, then its parent is put into hasCleared. - // In the situation that the child is reached before the parent, upon - // the parent being reached, even if the parent disallows, because the - // parent will be in hasCleared, permission will be allowed. In the - // other case, where the parent is reached first, if it does not allow - // permissions, it will be placed into needsClear. If a child of - // the parent is reached later, the parent will be removed from - // needsClear. At the end, if needsClear is not empty, that means that - // permission should not be given. If a parent has multiple children - // and one child does not allow permissions, then it will be placed into - // needsClear just like as if was a parent. - - Set needsClear = new HashSet(); - Set hasCleared = new HashSet(); - - for (ProtectedRegion region : applicable) { - // Ignore lower priority regions - if (hasFlagDefined && region.getPriority() < lastPriority) { - break; - } - - lastPriority = region.getPriority(); - - // Ignore non-build regions - if (player != null && getStateFlagIncludingParents(region, DefaultFlag.PASSTHROUGH) == State.ALLOW) { - continue; - } - - // Check group permissions - if (groupPlayer != null && flag.getRegionGroupFlag() != null) { - RegionGroup group = region.getFlag(flag.getRegionGroupFlag()); - if (group == null) { - group = flag.getRegionGroupFlag().getDefault(); - } - if (!RegionGroupFlag.isMember(region, group, groupPlayer)) { - continue; - } - } - - State v = getStateFlagIncludingParents(region, flag); - - // Allow DENY to override everything - if (v == State.DENY) { - return false; - } - - // Forget about regions that allow it, although make sure the - // default state is now to allow - if (v == State.ALLOW) { - allowed = true; - found = true; - hasFlagDefined = true; - continue; - } - - // For the build flag, the flags are conditional and are based - // on membership, so we have to check for parent-child - // relationships - if (player != null) { - hasFlagDefined = true; - - //noinspection StatementWithEmptyBody - if (hasCleared.contains(region)) { - // Already cleared, so do nothing - } else { - if (!region.isMember(player)) { - needsClear.add(region); - } else { - // Need to clear all parents - clearParents(needsClear, hasCleared, region); - } - } - } - - found = true; - } - - return !found ? def : (allowed || (player != null && needsClear.isEmpty())); + return allowed ? State.ALLOW : null; } /** diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java b/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java index eeda9aab..b8de20bc 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java @@ -23,6 +23,8 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; +import javax.annotation.Nullable; + /** * * @author sk89q @@ -89,4 +91,59 @@ public Object marshal(State o) { } } + /** + * Test whether at least one of the given states is {@code ALLOW} + * but none are set to {@code DENY}. + * + * @param states zero or more states + * @return true if the condition is matched + */ + public static boolean test(State... states) { + boolean allowed = false; + + for (State state : states) { + if (state == State.DENY) { + return false; + } else if (state == State.ALLOW) { + allowed = true; + } + } + + return allowed; + } + + /** + * Combine states, letting {@code DENY} override {@code ALLOW} and + * {@code ALLOW} override {@code NONE} (or null). + * + * @param states zero or more states + * @return the new state + */ + @Nullable + public static State combine(State... states) { + boolean allowed = false; + + for (State state : states) { + if (state == State.DENY) { + return State.DENY; + } else if (state == State.ALLOW) { + allowed = true; + } + } + + return allowed ? State.ALLOW : null; + } + + /** + * Turn a boolean into either {@code NONE} (null) or {@code ALLOW} if + * the boolean is false or true, respectively. + * + * @param flag a boolean value + * @return a state + */ + @Nullable + public static State allowOrNone(boolean flag) { + return flag ? State.ALLOW : null; + } + } From 8a117522534175ec25bc390c60dabffea0d82048 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 15 Aug 2014 12:31:20 -0700 Subject: [PATCH 053/133] Fix a typo in ApplicableRegionSet and improve docs. --- .../protection/ApplicableRegionSet.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java index 714ed827..ed662e6c 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java +++ b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java @@ -208,19 +208,20 @@ private State calculateState(StateFlag flag, @Nullable LocalPlayer player, @Null // regions, there are two scenarios that we may encounter: // // 1) PARENT first, CHILD later: - // PARENT and its parents are added to needsClear. - // When the loop reaches CHILD, all parents of CHILD (including - // PARENT) are removed from needsClear. (Any parents not in - // needsClear are added to hasCleared for the 2nd scenario.) + // a) When the loop reaches PARENT, PARENT is added to needsClear. + // b) When the loop reaches CHILD, parents of CHILD (which includes + // PARENT) are removed from needsClear. + // c) needsClear is empty again. // // 2) CHILD first, PARENT later: - // CHILD's parents are added to hasCleared. - // When the loop reaches PARENT, since PARENT is already in - // hasCleared, it doe not add PARENT to needsClear. + // a) When the loop reaches CHILD, CHILD's parents (i.e. PARENT) are + // added to hasCleared. + // b) When the loop reaches PARENT, since PARENT is already in + // hasCleared, it does not add PARENT to needsClear. + // c) needsClear stays empty. // - // If there are any regions left over in needsClear, that means that - // there was at least one region that the player is not a member of - // (any of its children) and thus we can deny building. + // As long as the process ends with needsClear being empty, then + // we have satisfied all membership requirements. Set needsClear = new HashSet(); Set hasCleared = new HashSet(); @@ -229,7 +230,7 @@ private State calculateState(StateFlag flag, @Nullable LocalPlayer player, @Null // Don't consider lower priorities below minimumPriority // (which starts at Integer.MIN_VALUE). A region that "counts" // (has the flag set OR has members) will raise minimumPriority - // its own priority. + // to its own priority. if (region.getPriority() < minimumPriority) { break; } From c0d4ba5c6c05faf6612177708c603e276d72f7f4 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 15 Aug 2014 13:54:33 -0700 Subject: [PATCH 054/133] Swap the if() in the state flag test to make it more readable. --- .../protection/ApplicableRegionSet.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java index ed662e6c..650e59ba 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java +++ b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java @@ -284,15 +284,18 @@ private State calculateState(StateFlag flag, @Nullable LocalPlayer player, @Null } } - if (regionsThatCountExistHere) { - if (player != null) { - State membership = allowOrNone(needsClear.isEmpty()); - return combine(state, membership); + if (player != null) { + State fallback; + + if (regionsThatCountExistHere) { + fallback = allowOrNone(needsClear.isEmpty()); } else { - return combine(state, getDefault(flag, null)); + fallback = getDefault(flag, player); } + + return combine(state, fallback); } else { - return combine(getDefault(flag, player)); + return combine(state, getDefault(flag, null)); } } From d07e81288a8e78cc36c66c519daf123c745d3459 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 15 Aug 2014 14:16:05 -0700 Subject: [PATCH 055/133] Make the state flag test use a Predicate for membership testing. --- .../protection/ApplicableRegionSet.java | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java index 650e59ba..45efba92 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java +++ b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java @@ -19,13 +19,14 @@ package com.sk89q.worldguard.protection; +import com.google.common.base.Predicate; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.RegionGroup; import com.sk89q.worldguard.protection.flags.RegionGroupFlag; import com.sk89q.worldguard.protection.flags.StateFlag; -import com.sk89q.worldguard.protection.flags.StateFlag.State; +import com.sk89q.worldguard.protection.flags.StateFlag.*; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; @@ -93,7 +94,7 @@ public ApplicableRegionSet(SortedSet applicable, @Nullable Prot */ public boolean canBuild(LocalPlayer player) { checkNotNull(player); - return test(calculateState(DefaultFlag.BUILD, player, null)); + return test(calculateState(DefaultFlag.BUILD, new RegionMemberTest(player), null)); } /** @@ -184,13 +185,24 @@ public boolean isMemberOfAll(LocalPlayer player) { * value of a flag {@link StateFlag#getDefault()}. * * @param flag the flag to check - * @param player the player, or null to not check owners and members + * @param membershipTest null to perform a "wilderness check" or a predicate + * returns true if a the subject is a member of the + * region passed * @param groupPlayer a player to use for the group flag check * @return the allow/deny state for the flag */ - private State calculateState(StateFlag flag, @Nullable LocalPlayer player, @Nullable LocalPlayer groupPlayer) { + private State calculateState(StateFlag flag, @Nullable Predicate membershipTest, @Nullable LocalPlayer groupPlayer) { checkNotNull(flag); + // This method works in two modes: + // + // 1) Membership mode (if membershipTest != null): + // a) Regions in this set -> Check membership + Check region flags + // a) No regions -> Use global region + default value + // 1) Flag mode: + // a) Regions in this set -> Use global region + default value + // a) No regions -> Use global region + default value + int minimumPriority = Integer.MIN_VALUE; boolean regionsThatCountExistHere = false; // We can't do a application.isEmpty() because // PASSTHROUGH regions have to be skipped @@ -237,7 +249,7 @@ private State calculateState(StateFlag flag, @Nullable LocalPlayer player, @Null // If PASSTHROUGH is set and we are checking to see if a player // is a member, then skip this region - if (player != null && getStateFlagIncludingParents(region, DefaultFlag.PASSTHROUGH) == State.ALLOW) { + if (membershipTest != null && getStateFlagIncludingParents(region, DefaultFlag.PASSTHROUGH) == State.ALLOW) { continue; } @@ -269,11 +281,11 @@ private State calculateState(StateFlag flag, @Nullable LocalPlayer player, @Null minimumPriority = region.getPriority(); } else { - if (player != null) { + if (membershipTest != null) { minimumPriority = region.getPriority(); if (!hasCleared.contains(region)) { - if (!region.isMember(player)) { + if (!membershipTest.apply(region)) { needsClear.add(region); } else { // Need to clear all parents @@ -284,13 +296,13 @@ private State calculateState(StateFlag flag, @Nullable LocalPlayer player, @Null } } - if (player != null) { + if (membershipTest != null) { State fallback; if (regionsThatCountExistHere) { fallback = allowOrNone(needsClear.isEmpty()); } else { - fallback = getDefault(flag, player); + fallback = getDefault(flag, membershipTest); } return combine(state, fallback); @@ -300,7 +312,7 @@ private State calculateState(StateFlag flag, @Nullable LocalPlayer player, @Null } @Nullable - private State getDefault(StateFlag flag, @Nullable LocalPlayer player) { + private State getDefault(StateFlag flag, @Nullable Predicate membershipTest) { boolean allowed = flag.getDefault(); // Handle defaults @@ -310,15 +322,15 @@ private State getDefault(StateFlag flag, @Nullable LocalPlayer player) { // The global region has this flag set if (globalState != null) { // Build flag is very special - if (player != null && globalRegion.hasMembersOrOwners()) { - allowed = globalRegion.isMember(player) && (globalState == State.ALLOW); + if (membershipTest != null && globalRegion.hasMembersOrOwners()) { + allowed = membershipTest.apply(globalRegion) && (globalState == State.ALLOW); } else { allowed = (globalState == State.ALLOW); } } else { // Build flag is very special - if (player != null && globalRegion.hasMembersOrOwners()) { - allowed = globalRegion.isMember(player); + if (membershipTest != null && globalRegion.hasMembersOrOwners()) { + allowed = membershipTest.apply(globalRegion); } } } @@ -487,4 +499,20 @@ public static ApplicableRegionSet getEmpty() { return EMPTY; } + /** + * Returns true if a player is a member (or owner) of a region. + */ + private static class RegionMemberTest implements Predicate { + private final LocalPlayer player; + + private RegionMemberTest(LocalPlayer player) { + this.player = checkNotNull(player); + } + + @Override + public boolean apply(ProtectedRegion region) { + return region.isMember(player); + } + } + } From b1fb143fd4b773729b3d8f8e05568b138d829f72 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 16 Aug 2014 02:42:02 -0700 Subject: [PATCH 056/133] Add a new (unused for now) flag value implementation. It supports region groups and fixes inconsistency issues. --- .../protection/ApplicableRegionSet.java | 4 +- .../protection/FlagValueCalculator.java | 572 ++++++ .../worldguard/protection/flags/Flag.java | 36 +- .../protection/flags/RegionGroupFlag.java | 11 +- .../protection/flags/StateFlag.java | 57 +- .../protection/FlagValueCalculatorTest.java | 1821 +++++++++++++++++ .../protection/MockApplicableRegionSet.java | 11 + 7 files changed, 2497 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java create mode 100644 src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java diff --git a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java index 45efba92..6a9fe1e2 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java +++ b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java @@ -313,7 +313,7 @@ private State calculateState(StateFlag flag, @Nullable Predicate membershipTest) { - boolean allowed = flag.getDefault(); + boolean allowed = flag.getDefault() == State.ALLOW; // Handle defaults if (globalRegion != null) { @@ -425,9 +425,11 @@ public , V> V getFlag(T flag, @Nullable LocalPlayer groupPlaye // Check group permissions if (groupPlayer != null && flag.getRegionGroupFlag() != null) { RegionGroup group = region.getFlag(flag.getRegionGroupFlag()); + if (group == null) { group = flag.getRegionGroupFlag().getDefault(); } + if (!RegionGroupFlag.isMember(region, group, groupPlayer)) { continue; } diff --git a/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java b/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java new file mode 100644 index 00000000..b8bdb2f6 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java @@ -0,0 +1,572 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection; + +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.Flag; +import com.sk89q.worldguard.protection.flags.RegionGroup; +import com.sk89q.worldguard.protection.flags.RegionGroupFlag; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.flags.StateFlag.State; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Calculates the value of a flag given a list of regions and an optional + * global region. + * + *

Since there may be multiple overlapping regions, regions with + * differing priorities, regions with inheritance, flags with region groups + * assigned to them, and much more, the task of calculating the "effective" + * value of a flag is far from trivial. This class abstracts away the + * difficult with a number of methods for performing these calculations.

+ */ +public class FlagValueCalculator { + + private final SortedSet applicable; + @Nullable + private final ProtectedRegion globalRegion; + + /** + * Create a new instance. + * + * @param applicable a list of applicable regions + * @param globalRegion an optional global region (null to not use one) + */ + public FlagValueCalculator(SortedSet applicable, @Nullable ProtectedRegion globalRegion) { + checkNotNull(applicable); + + this.applicable = applicable; + this.globalRegion = globalRegion; + } + + /** + * Return the membership status of the given player, indicating + * whether there are no (counted) regions in the list of regions, + * whether the player is a member of all regions, or whether + * the region is not a member of all regions. + * + *

A region is "counted" if it doesn't have the + * {@link DefaultFlag#PASSTHROUGH} flag set to {@code ALLOW}. (The + * explicit purpose of the PASSTHROUGH flag is to have the region + * be skipped over in this check.)

+ * + *

This method is mostly for internal use. It's not particularly + * useful.

+ * + * @param player the player + * @return the membership result + */ + public Result getMembership(LocalPlayer player) { + checkNotNull(player); + + int minimumPriority = Integer.MIN_VALUE; + boolean foundApplicableRegion = false; + + // Say there are two regions in one location: CHILD and PARENT (CHILD + // is a child of PARENT). If there are two overlapping regions in WG, a + // player has to be a member of /both/ (or flags permit) in order to + // build in that location. However, inheritance is supposed + // to allow building if the player is a member of just CHILD. That + // presents a problem. + // + // To rectify this, we keep two sets. When we iterate over the list of + // regions, there are two scenarios that we may encounter: + // + // 1) PARENT first, CHILD later: + // a) When the loop reaches PARENT, PARENT is added to needsClear. + // b) When the loop reaches CHILD, parents of CHILD (which includes + // PARENT) are removed from needsClear. + // c) needsClear is empty again. + // + // 2) CHILD first, PARENT later: + // a) When the loop reaches CHILD, CHILD's parents (i.e. PARENT) are + // added to hasCleared. + // b) When the loop reaches PARENT, since PARENT is already in + // hasCleared, it does not add PARENT to needsClear. + // c) needsClear stays empty. + // + // As long as the process ends with needsClear being empty, then + // we have satisfied all membership requirements. + + Set needsClear = new HashSet(); + Set hasCleared = new HashSet(); + + for (ProtectedRegion region : applicable) { + // Don't consider lower priorities below minimumPriority + // (which starts at Integer.MIN_VALUE). A region that "counts" + // (has the flag set OR has members) will raise minimumPriority + // to its own priority. + if (region.getPriority() < minimumPriority) { + break; + } + + // If PASSTHROUGH is set, ignore this region + if (getEffectiveFlag(region, DefaultFlag.PASSTHROUGH, player) == State.ALLOW) { + continue; + } + + minimumPriority = region.getPriority(); + foundApplicableRegion = true; + + if (!hasCleared.contains(region)) { + if (!region.isMember(player)) { + needsClear.add(region); + } else { + // Need to clear all parents + removeParents(needsClear, hasCleared, region); + } + } + } + + if (foundApplicableRegion) { + return needsClear.isEmpty() ? Result.SUCCESS : Result.FAIL; + } else { + return Result.NO_REGIONS; + } + } + + /** + * Test whether the given player is permitted to place, break, or + * modify any block, entity, or other object. A list of flags is to be + * provided (one of which should probably be {@link DefaultFlag#BUILD}) + * so that the calculation can consider all of those flags. + * + *

For example, if we are checking for the ability to interact + * with a chest, we would want to give permission if (1) the player is + * a member of the region, (2) the {@code build} flag is set to + * {@code ALLOW}, or (3) the {@code chest-access} flag is set to + * {@code ALLOW}. However, if any of the two flags are set + * to {@code DENY}, that must override everything else and deny access.

+ * + *

This method handles that example perfectly. To use the method for + * the example, the call would look like this:

+ * + *
testPermission(player, DefaultFlag.BUILD, DefaultFlag.CHEST_ACCESS)
+ * + * @param player the player + * @param flags zero or more flags + * @return true if permission is granted + */ + public State testPermission(LocalPlayer player, StateFlag... flags) { + checkNotNull(player); + checkNotNull(flags); + + // Legacy behavior dictates that the global region is really a + // "wilderness" region. It has no effect when there are one or more + // regions without the PASSTHROUGH flag set. + // + // In addition, the global region can never override any PASSTHROUGH + // region. + // + // Lastly, if the global region has members, then permission will + // be denied by default except to those members that are a part of + // the global region, turning the global region into a region itself + // that covers the entire world. Unfortunately, this is really a hack + // and we support it for legacy reasons. + + switch (getMembership(player)) { + case SUCCESS: + return StateFlag.combine(getState(player, flags), State.ALLOW); + case FAIL: + return getState(player, flags); + case NO_REGIONS: + if (globalRegion != null && globalRegion.hasMembersOrOwners()) { + if (globalRegion.isMember(player)) { + return StateFlag.combine(getState(player, flags), State.ALLOW); + } else { + State value = null; + + for (StateFlag flag : flags) { + value = StateFlag.combine(value,globalRegion.getFlag(flag)); + if (value == State.DENY) { + break; + } + } + + return value; + } + } + default: + return getStateWithFallback(player, flags); + } + } + + /** + * Get the effective value for a list of state flags. The rules of + * states is observed here; that is, {@code DENY} overrides {@code ALLOW}, + * and {@code ALLOW} overrides {@code NONE}. This method will check + * the global region and {@link Flag#getDefault()} (in that order) if + * a value for the flag is not set in any region. + * + *

This method does not properly process build + * permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)} + * for that purpose. This method is ideal for testing non-build related + * state flags (although a rarity), an example of which would be whether + * to play a song to players that enter an area.

+ * + *

A player can be provided that is used to determine whether the value + * of a flag on a particular region should be used. For example, if a + * flag's region group is set to {@link RegionGroup#MEMBERS} and the given + * player is not a member, then the region would be skipped when + * querying that flag. If {@code null} is provided for the player, then + * only flags that use {@link RegionGroup#ALL}, + * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

+ * + * @param player an optional player, which would be used to determine the region group to apply + * @param flags a list of flags to check + * @return a state + */ + @Nullable + public State getStateWithFallback(@Nullable LocalPlayer player, StateFlag... flags) { + State value = null; + + for (StateFlag flag : flags) { + value = StateFlag.combine(value, getSingleValueWithFallback(player, flag)); + if (value == State.DENY) { + break; + } + } + + return value; + } + + + /** + * Get the effective value for a list of state flags. The rules of + * states is observed here; that is, {@code DENY} overrides {@code ALLOW}, + * and {@code ALLOW} overrides {@code NONE}. This method does not check + * the global region and ignores a flag's default value. + * + *

This method does not properly process build + * permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)} + * for that purpose. This method is ideal for testing non-build related + * state flags (although a rarity), an example of which would be whether + * to play a song to players that enter an area.

+ * + *

A player can be provided that is used to determine whether the value + * of a flag on a particular region should be used. For example, if a + * flag's region group is set to {@link RegionGroup#MEMBERS} and the given + * player is not a member, then the region would be skipped when + * querying that flag. If {@code null} is provided for the player, then + * only flags that use {@link RegionGroup#ALL}, + * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

+ * + * @param player an optional player, which would be used to determine the region group to apply + * @param flags a list of flags to check + * @return a state + */ + @Nullable + public State getState(@Nullable LocalPlayer player, StateFlag... flags) { + State value = null; + + for (StateFlag flag : flags) { + value = StateFlag.combine(value, getSingleValue(player, flag)); + if (value == State.DENY) { + break; + } + } + + return value; + } + + /** + * Get the effective value for a flag. If there are multiple values + * (for example, if there are multiple regions with the same priority + * but with different farewell messages set, there would be multiple + * completing values), then the selected (or "winning") value will depend + * on the flag type. This method will check the global region + * for a value as well as the flag's default value. + * + *

Only some flag types actually have a strategy for picking the + * "best value." For most types, the actual value that is chosen to be + * returned is undefined (it could be any value). As of writing, the only + * type of flag that can consistently return the same 'best' value is + * {@link StateFlag}.

+ * + *

This method does not properly process build + * permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)} + * for that purpose.

+ * + *

A player can be provided that is used to determine whether the value + * of a flag on a particular region should be used. For example, if a + * flag's region group is set to {@link RegionGroup#MEMBERS} and the given + * player is not a member, then the region would be skipped when + * querying that flag. If {@code null} is provided for the player, then + * only flags that use {@link RegionGroup#ALL}, + * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

+ * + * @param player an optional player, which would be used to determine the region group to apply + * @param flag the flag + * @return a value, which could be {@code null} + * @see #getSingleValue(LocalPlayer, Flag) does not check global region, defaults + */ + @Nullable + public V getSingleValueWithFallback(@Nullable LocalPlayer player, Flag flag) { + checkNotNull(flag); + + V value = getSingleValue(player, flag); + + if (value != null) { + return value; + } + + // Get the value from the global region + if (globalRegion != null) { + value = globalRegion.getFlag(flag); + } + + // Still no value? Check the default value for the flag + if (value == null) { + value = flag.getDefault(); + } + + return flag.validateDefaultValue(value); + } + + /** + * Get the effective value for a flag. If there are multiple values + * (for example, if there are multiple regions with the same priority + * but with different farewell messages set, there would be multiple + * completing values), then the selected (or "winning") value will depend + * on the flag type. This method never checks the global region or + * the flag's default value. + * + *

Only some flag types actually have a strategy for picking the + * "best value." For most types, the actual value that is chosen to be + * returned is undefined (it could be any value). As of writing, the only + * type of flag that can consistently return the same 'best' value is + * {@link StateFlag}.

+ * + *

This method does not properly process build + * permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)} + * for that purpose.

+ * + *

A player can be provided that is used to determine whether the value + * of a flag on a particular region should be used. For example, if a + * flag's region group is set to {@link RegionGroup#MEMBERS} and the given + * player is not a member, then the region would be skipped when + * querying that flag. If {@code null} is provided for the player, then + * only flags that use {@link RegionGroup#ALL}, + * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

+ * + * @param player an optional player, which would be used to determine the region group to apply + * @param flag the flag + * @return a value, which could be {@code null} + * @see #getSingleValueWithFallback(LocalPlayer, Flag) checks global regions, defaults + */ + @Nullable + public V getSingleValue(@Nullable LocalPlayer player, Flag flag) { + Collection values = getValues(player, flag); + return flag.chooseValue(values); + } + + /** + * Get the effective values for a flag, returning a collection of all + * values. It is up to the caller to determine which value, if any, + * from the collection will be used. + * + *

This method does not properly process build + * permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)} + * for that purpose.

+ * + *

A player can be provided that is used to determine whether the value + * of a flag on a particular region should be used. For example, if a + * flag's region group is set to {@link RegionGroup#MEMBERS} and the given + * player is not a member, then the region would be skipped when + * querying that flag. If {@code null} is provided for the player, then + * only flags that use {@link RegionGroup#ALL}, + * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

+ */ + public Collection getValues(@Nullable LocalPlayer player, Flag flag) { + checkNotNull(flag); + + int minimumPriority = Integer.MIN_VALUE; + + // Say there are two regions in one location: CHILD and PARENT (CHILD + // is a child of PARENT). If the two are overlapping regions in WG, + // both with values set, then we have a problem. Due to inheritance, + // only the CHILD's value for the flag should be used because it + // overrides its parent's value, but default behavior is to collect + // all the values into a list. + // + // To rectify this, we keep a map of consideredValues (region -> value) + // and an ignoredRegions set. When we iterate over the list of + // regions, there are two scenarios that we may encounter: + // + // 1) PARENT first, CHILD later: + // a) When the loop reaches PARENT, PARENT's value is added to + // consideredValues + // b) When the loop reaches CHILD, parents of CHILD (which includes + // PARENT) are removed from consideredValues (so we no longer + // consider those values). The CHILD's value is then added to + // consideredValues. + // c) In the end, only CHILD's value exists in consideredValues. + // + // 2) CHILD first, PARENT later: + // a) When the loop reaches CHILD, CHILD's value is added to + // consideredValues. In addition, the CHILD's parents (which + // includes PARENT) are added to ignoredRegions. + // b) When the loop reaches PARENT, since PARENT is in + // ignoredRegions, the parent is skipped over. + // c) In the end, only CHILD's value exists in consideredValues. + + Map consideredValues = new HashMap(); + Set ignoredRegions = new HashSet(); + + for (ProtectedRegion region : applicable) { + // Don't consider lower priorities below minimumPriority + // (which starts at Integer.MIN_VALUE). A region that "counts" + // (has the flag set) will raise minimumPriority to its own + // priority. + if (region.getPriority() < minimumPriority) { + break; + } + + V value = getEffectiveFlag(region, flag, player); + + if (value != null) { + if (!ignoredRegions.contains(region)) { + minimumPriority = region.getPriority(); + + ignoreValuesOfParents(consideredValues, ignoredRegions, region); + consideredValues.put(region, value); + + if (value == State.DENY) { + // Since DENY overrides all other values, there + // is no need to consider any further regions + break; + } + } + } + } + + return consideredValues.values(); + } + + /** + * Get a region's state flag, checking parent regions until a value for the + * flag can be found (if one even exists). + * + * @param region the region + * @param flag the flag + * @return the value + */ + public V getEffectiveFlag(final ProtectedRegion region, Flag flag, @Nullable LocalPlayer player) { + ProtectedRegion current = region; + + while (current != null) { + V value = current.getFlag(flag); + boolean use = true; + + if (flag.getRegionGroupFlag() != null) { + RegionGroup group = current.getFlag(flag.getRegionGroupFlag()); + if (group == null) { + group = flag.getRegionGroupFlag().getDefault(); + } + + if (!RegionGroupFlag.isMember(region, group, player)) { + use = false; + } + } + + if (use && value != null) { + return value; + } + + current = current.getParent(); + } + + return null; + } + + /** + * Clear a region's parents for isFlagAllowed(). + * + * @param needsClear the regions that should be cleared + * @param hasCleared the regions already cleared + * @param region the region to start from + */ + private void removeParents(Set needsClear, Set hasCleared, ProtectedRegion region) { + ProtectedRegion parent = region.getParent(); + + while (parent != null) { + if (!needsClear.remove(parent)) { + hasCleared.add(parent); + } + + parent = parent.getParent(); + } + } + + /** + * Clear a region's parents for getFlag(). + * + * @param needsClear The regions that should be cleared + * @param hasCleared The regions already cleared + * @param region The region to start from + */ + private void ignoreValuesOfParents(Map needsClear, Set hasCleared, ProtectedRegion region) { + ProtectedRegion parent = region.getParent(); + + while (parent != null) { + if (needsClear.remove(parent) == null) { + hasCleared.add(parent); + } + + parent = parent.getParent(); + } + } + + /** + * Describes the membership result from + * {@link #getMembership(LocalPlayer)}. + */ + public static enum Result { + /** + * Indicates that there are no regions or the only regions are + * ones with {@link DefaultFlag#PASSTHROUGH} enabled. + */ + NO_REGIONS, + + /** + * Indicates that the player is not a member of all overlapping + * regions. + */ + FAIL, + + /** + * Indicates that the player is a member of all overlapping + * regions. + */ + SUCCESS + } + +} diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java b/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java index f3ee58c6..996c8f3a 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java @@ -19,11 +19,11 @@ package com.sk89q.worldguard.protection.flags; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import org.bukkit.command.CommandSender; -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; - import javax.annotation.Nullable; +import java.util.Collection; /** * @@ -44,19 +44,45 @@ public Flag(String name, RegionGroup defaultGroup) { } public Flag(String name) { - this(name, RegionGroup.NON_MEMBERS); + this(name, RegionGroup.ALL); } public String getName() { return name; } + + /** + * Suppress the value of the flag that came from the global region, reducing + * its severity (i.e. DENY -> NONE). + * + *

This is really only used for the {@link StateFlag}.

+ * + * @param current the value to suppress + * @return a new value + */ + public T validateDefaultValue(T current) { + return current; + } + + @Nullable + public T getDefault() { + return null; + } + + @Nullable + public T chooseValue(Collection values) { + if (!values.isEmpty()) { + return values.iterator().next(); + } else { + return null; + } + } public RegionGroupFlag getRegionGroupFlag() { return regionGroup; } - public abstract T parseInput(WorldGuardPlugin plugin, CommandSender sender, - String input) throws InvalidFlagFormat; + public abstract T parseInput(WorldGuardPlugin plugin, CommandSender sender, String input) throws InvalidFlagFormat; public abstract T unmarshal(@Nullable Object o); diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java b/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java index 772eb529..01232f95 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java @@ -38,6 +38,7 @@ public RegionGroupFlag(String name, RegionGroup def) { this.def = def; } + @Override public RegionGroup getDefault() { return def; } @@ -63,23 +64,23 @@ public RegionGroup detectValue(String input) { } } - public static boolean isMember(ProtectedRegion region, RegionGroup group, LocalPlayer player) { + public static boolean isMember(ProtectedRegion region, RegionGroup group, @Nullable LocalPlayer player) { if (group == null || group == RegionGroup.ALL) { return true; } else if (group == RegionGroup.OWNERS) { - if (region.isOwner(player)) { + if (player != null && region.isOwner(player)) { return true; } } else if (group == RegionGroup.MEMBERS) { - if (region.isMember(player)) { + if (player != null && region.isMember(player)) { return true; } } else if (group == RegionGroup.NON_OWNERS) { - if (!region.isOwner(player)) { + if (player == null || !region.isOwner(player)) { return true; } } else if (group == RegionGroup.NON_MEMBERS) { - if (!region.isMember(player)) { + if (player == null || !region.isMember(player)) { return true; } } diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java b/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java index b8de20bc..8e2fed0d 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java @@ -19,11 +19,11 @@ package com.sk89q.worldguard.protection.flags; +import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import org.bukkit.command.CommandSender; -import com.sk89q.worldguard.bukkit.WorldGuardPlugin; - import javax.annotation.Nullable; +import java.util.Collection; /** * @@ -48,8 +48,24 @@ public StateFlag(String name, boolean def) { this.def = def; } - public boolean getDefault() { - return def; + @Override + public State getDefault() { + return def ? State.ALLOW : null; + } + + @Override + public State validateDefaultValue(State current) { + return denyToNone(current); + } + + @Override + @Nullable + public State chooseValue(Collection values) { + if (!values.isEmpty()) { + return combine(values); + } else { + return null; + } } @Override @@ -134,6 +150,28 @@ public static State combine(State... states) { return allowed ? State.ALLOW : null; } + /** + * Combine states, letting {@code DENY} override {@code ALLOW} and + * {@code ALLOW} override {@code NONE} (or null). + * + * @param states zero or more states + * @return the new state + */ + @Nullable + public static State combine(Collection states) { + boolean allowed = false; + + for (State state : states) { + if (state == State.DENY) { + return State.DENY; + } else if (state == State.ALLOW) { + allowed = true; + } + } + + return allowed ? State.ALLOW : null; + } + /** * Turn a boolean into either {@code NONE} (null) or {@code ALLOW} if * the boolean is false or true, respectively. @@ -146,4 +184,15 @@ public static State allowOrNone(boolean flag) { return flag ? State.ALLOW : null; } + /** + * Turn {@code DENY} into {@code NONE} (null). + * + * @param state a state + * @return a state + */ + @Nullable + public static State denyToNone(State state) { + return state == State.DENY ? null : state; + } + } diff --git a/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java b/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java new file mode 100644 index 00000000..a4351ad7 --- /dev/null +++ b/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java @@ -0,0 +1,1821 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.protection; + +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.protection.FlagValueCalculator.Result; +import com.sk89q.worldguard.protection.flags.DefaultFlag; +import com.sk89q.worldguard.protection.flags.RegionGroup; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.flags.StateFlag.State; +import com.sk89q.worldguard.protection.flags.StringFlag; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.hamcrest.Matchers; +import org.junit.Test; + +import static com.google.common.collect.ImmutableSet.copyOf; +import static com.google.common.collect.ImmutableSet.of; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +@SuppressWarnings({"UnusedAssignment", "UnusedDeclaration"}) +public class FlagValueCalculatorTest { + + @Test + public void testGetSingleFallbackMembershipWilderness() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.NO_REGIONS)); + } + + @Test + public void testGetMembershipWildernessWithGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + LocalPlayer player = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.NO_REGIONS)); + } + + @Test + public void testGetMembershipPassthroughRegions() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + region = mock.add(0); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.NO_REGIONS)); + } + + @Test + public void testGetMembershipPassthroughAndRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + region = mock.add(0); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.FAIL)); + } + + @Test + public void testGetMembershipPassthroughAndRegionMemberOf() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + region = mock.add(0); + + LocalPlayer player = mock.createPlayer(); + region.getMembers().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.SUCCESS)); + } + + @Test + public void testGetMembershipPassthroughAndRegionMemberOfAndAnotherNot() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + region = mock.add(0); + + LocalPlayer player = mock.createPlayer(); + region.getMembers().addPlayer(player); + + region = mock.add(0); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.FAIL)); + + // Add another player (should still fail) + region.getMembers().addPlayer(mock.createPlayer()); + + result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.FAIL)); + } + + @Test + public void testGetMembershipPassthroughAndRegionMemberOfAndAnotherNotWithHigherPriority() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + region = mock.add(0); + + LocalPlayer player = mock.createPlayer(); + region.getMembers().addPlayer(player); + + region = mock.add(10); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.FAIL)); + } + + @Test + public void testGetMembershipPassthroughAndRegionMemberOfWithHigherPriorityAndAnotherNot() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + region = mock.add(10); + + LocalPlayer player = mock.createPlayer(); + region.getMembers().addPlayer(player); + + region = mock.add(0); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.SUCCESS)); + } + + @Test + public void testGetMembershipPassthroughAndRegionMemberOfWithAnotherParent() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion passthrough = mock.add(0); + passthrough.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + ProtectedRegion parent = mock.add(0); + + ProtectedRegion region = mock.add(0); + region.setParent(parent); + + LocalPlayer player = mock.createPlayer(); + region.getMembers().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.SUCCESS)); + } + + @Test + public void testGetMembershipPassthroughAndRegionMemberOfWithAnotherChild() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion passthrough = mock.add(0); + passthrough.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + ProtectedRegion parent = mock.add(0); + + ProtectedRegion region = mock.add(0); + region.setParent(parent); + + LocalPlayer player = mock.createPlayer(); + parent.getMembers().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.SUCCESS)); + } + + @Test + public void testGetMembershipPassthroughAndRegionMemberOfWithAnotherChildAndAnother() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion passthrough = mock.add(0); + passthrough.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + ProtectedRegion parent = mock.add(0); + + ProtectedRegion region = mock.add(0); + region.setParent(parent); + + region = mock.add(0); + + LocalPlayer player = mock.createPlayer(); + parent.getMembers().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.FAIL)); + } + + @Test + public void testGetMembershipThirdPriorityLower() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion passthrough = mock.add(0); + passthrough.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + + ProtectedRegion parent = mock.add(0); + + ProtectedRegion region = mock.add(0); + region.setParent(parent); + + region = mock.add(0); + region.setPriority(-5); + + LocalPlayer player = mock.createPlayer(); + parent.getMembers().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.SUCCESS)); + } + + // ======================================================================== + // ======================================================================== + + @Test + public void testTestPermissionWildernessDefaults() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(player, flag1), is((State) null)); + assertThat(result.testPermission(player, flag2), is(State.ALLOW)); + } + + @Test + public void testTestPermissionWildernessDefaultsWithGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer player = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(player, flag1), is((State) null)); + assertThat(result.testPermission(player, flag2), is(State.ALLOW)); + } + + @Test + public void testTestPermissionWildernessDefaultsWithGlobalRegionOverride() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer player = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.ALLOW); + global.setFlag(flag2, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(player, flag1), is(State.ALLOW)); + assertThat(result.testPermission(player, flag2), is((State) null)); + } + + @Test + public void testTestPermissionWildernessWithGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion region = mock.global(); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is((State) null)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWildernessWithGlobalRegionOverride() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion region = mock.global(); + region.setFlag(flag2, State.ALLOW); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion region = mock.add(0); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is((State) null)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithRegionAndFlagAllow() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion region = mock.add(0); + region.getMembers().addPlayer(member); + region.setFlag(flag1, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithRegionAndFlagDeny() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion region = mock.add(0); + region.getMembers().addPlayer(member); + region.setFlag(flag1, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.DENY)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithRegionAndFlagDenyAndRegionGroup() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion region = mock.add(0); + region.getMembers().addPlayer(member); + region.setFlag(flag1, State.DENY); + region.setFlag(flag1.getRegionGroupFlag(), RegionGroup.NON_MEMBERS); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.DENY)); + } + + @Test + public void testTestPermissionWithRegionAndGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + + ProtectedRegion region = mock.add(0); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is((State) null)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithRegionAndGlobalRegionDeny() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.DENY); // No effect + + ProtectedRegion region = mock.add(0); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is((State) null)); + } + + @Test + public void testTestPermissionWithRegionAndGlobalRegionAllow() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.ALLOW); // No effect + + ProtectedRegion region = mock.add(0); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithRegionAndGlobalRegionMembership() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.getMembers().addPlayer(nonMember); + + ProtectedRegion region = mock.add(0); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + // ======================================================================== + // ======================================================================== + + @Test + public void testGetStateWithFallbackSingle() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", false); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is((State) null)); + } + + @Test + public void testGetStateWithFallbackSeveralNoneAreDenyNoneAreTrue() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + StateFlag flag3 = new StateFlag("test3", false); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is((State) null)); + } + + @Test + public void testGetStateWithFallbackSeveralNoneAreDenyOneIsTrue() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + StateFlag flag3 = new StateFlag("test3", false); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.ALLOW)); + } + + @Test + public void testGetStateWithFallbackSeveralNoneAreDenyNoneAreTrueWithEmptyGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + StateFlag flag3 = new StateFlag("test3", false); + + ProtectedRegion global = mock.global(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is((State) null)); + } + + @Test + public void testGetStateWithFallbackSeveralNoneAreDenyNoneAreTrueWithGlobalRegionValueSetAllow() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + StateFlag flag3 = new StateFlag("test3", false); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.ALLOW)); + } + + @Test + public void testGetStateWithFallbackSeveralNoneAreDenySomeAreTrueWithGlobalRegionValueSetDeny() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", true); + StateFlag flag2 = new StateFlag("test2", false); + StateFlag flag3 = new StateFlag("test3", false); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is((State) null)); + } + + @Test + public void testGetStateWithFallbackWithGlobalRegionAllowAndRegionDeny() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + StateFlag flag3 = new StateFlag("test3", false); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.ALLOW); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag1, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.DENY)); + } + + @Test + public void testGetStateWithFallbackWithGlobalRegionAllowAndRegionDenyOnDifferentFlag() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + StateFlag flag3 = new StateFlag("test3", false); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.ALLOW); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag2, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.DENY)); + } + + @Test + public void testGetStateWithFallbackWithGlobalRegionDenyAndRegionAllow() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + StateFlag flag3 = new StateFlag("test3", false); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.DENY); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag1, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.ALLOW)); + } + + @Test + public void testGetStateWithFallbackWithGlobalRegionDenyOnDifferentAndRegionAllow() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + StateFlag flag3 = new StateFlag("test3", false); + + ProtectedRegion global = mock.global(); + global.setFlag(flag2, State.DENY); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag1, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.ALLOW)); + } + + // ======================================================================== + // ======================================================================== + + @Test + public void testGetSingleValueWithFallbackWithFalseDefaultValue() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", false); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is((State) null)); + } + + @Test + public void testGetSingleValueWithFallbackWithTrueDefaultValue() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", true); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); + } + + @Test + public void testGetSingleValueWithFallbackWithFalseDefaultValueEmptyGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", false); + + ProtectedRegion global = mock.global(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is((State) null)); + } + + @Test + public void testGetSingleValueWithFallbackWithTrueDefaultValueEmptyGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", true); + + ProtectedRegion global = mock.global(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); + } + + @Test + public void testGetSingleValueWithFallbackWithDefaultValueSameGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", true); + + ProtectedRegion global = mock.global(); + global.setFlag(flag, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); + } + + @Test + public void testGetSingleValueWithFallbackWithTrueDefaultValueDenyGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", true); + + ProtectedRegion global = mock.global(); + global.setFlag(flag, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is((State) null)); + } + + @Test + public void testGetSingleValueWithFallbackWithFalseDefaultValueAllowGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", false); + + ProtectedRegion global = mock.global(); + global.setFlag(flag, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); + } + + @Test + public void testGetSingleValueWithFallbackWithStringFlag() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StringFlag flag = new StringFlag("test"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is((String) null)); + } + + @Test + public void testGetSingleValueWithFallbackWithStringFlagEmptyGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StringFlag flag = new StringFlag("test"); + + ProtectedRegion global = mock.global(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is((String) null)); + } + + @Test + public void testGetSingleValueWithFallbackWithStringFlagGlobalRegionValueSet() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StringFlag flag = new StringFlag("test"); + + ProtectedRegion global = mock.global(); + global.setFlag(flag, "hello there"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is("hello there")); + } + + @Test + public void testGetSingleValueWithFallbackWithFalseDefaultValueAndEmptyRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", false); + + ProtectedRegion region = mock.add(0); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is((State) null)); + } + + @Test + public void testGetSingleValueWithFallbackWithTrueDefaultValueAndEmptyRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", true); + + ProtectedRegion region = mock.add(0); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); + } + + @Test + public void testGetSingleValueWithFallbackWithTrueDefaultValueAndRegionDeny() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", true); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is(State.DENY)); + } + + @Test + public void testGetSingleValueWithFallbackWithTrueDefaultValueAndRegionAllow() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", true); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); + } + + @Test + public void testGetSingleValueWithFallbackWithFalseDefaultValueAndRegionAllow() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", false); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); + } + + @Test + public void testGetSingleValueWithFallbackWithFalseDefaultValueAndRegionDeny() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag = new StateFlag("test", false); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is(State.DENY)); + } + + @Test + public void testGetSingleValueWithFallbackWithStringFlagAndEmptyRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StringFlag flag = new StringFlag("test"); + + ProtectedRegion region = mock.add(0); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is((String) null)); + } + + @Test + public void testGetSingleValueWithFallbackWithStringFlagAndRegionValueSet() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StringFlag flag = new StringFlag("test"); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag, "beep beep"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is("beep beep")); + } + + @Test + public void testGetSingleValueWithFallbackWithStringFlagAndRegionValueSetAndPriority() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StringFlag flag = new StringFlag("test"); + + ProtectedRegion region = mock.add(10); + region.setFlag(flag, "ello there"); + + region = mock.add(0); + region.setFlag(flag, "beep beep"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is("ello there")); + } + + @Test + public void testGetSingleValueWithFallbackWithStringFlagAndRegionValueSetAndInheritanceAndRegionGroup() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StringFlag flag = new StringFlag("test", RegionGroup.ALL); + + ProtectedRegion parent = mock.add(0); + parent.setFlag(flag, "ello there"); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag, "beep beep"); + region.setFlag(flag.getRegionGroupFlag(), RegionGroup.MEMBERS); + region.setParent(parent); + + LocalPlayer nonMember = mock.createPlayer(); + + LocalPlayer member = mock.createPlayer(); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValueWithFallback(null, flag), is("ello there")); + assertThat(result.getSingleValueWithFallback(nonMember, flag), is("ello there")); + assertThat(result.getSingleValueWithFallback(member, flag), is("beep beep")); + } + + // ======================================================================== + // ======================================================================== + + @Test + public void testGetSingleValueSingleRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PVP, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValue(null, DefaultFlag.LIGHTER), is((State) null)); + assertThat(result.getSingleValue(null, DefaultFlag.PVP), is(State.DENY)); + } + + @Test + public void testGetSingleValueDenyOverridesAllow() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PVP, State.DENY); + + region = mock.add(0); + region.setFlag(DefaultFlag.PVP, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValue(null, DefaultFlag.LIGHTER), is((State) null)); + assertThat(result.getSingleValue(null, DefaultFlag.PVP), is(State.DENY)); + } + + @Test + public void testGetSingleValueAllowOverridesNone() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + + region = mock.add(0); + region.setFlag(DefaultFlag.PVP, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValue(null, DefaultFlag.LIGHTER), is((State) null)); + assertThat(result.getSingleValue(null, DefaultFlag.PVP), is(State.ALLOW)); + } + + @Test + public void testGetSingleValueMultipleFlags() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.LIGHTER, State.DENY); + region.setFlag(DefaultFlag.PVP, State.ALLOW); + + region = mock.add(0); + region.setFlag(DefaultFlag.PVP, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValue(null, DefaultFlag.LIGHTER), is(State.DENY)); + assertThat(result.getSingleValue(null, DefaultFlag.PVP), is(State.DENY)); + assertThat(result.getSingleValue(null, DefaultFlag.LAVA_FIRE), is((State) null)); + } + + @Test + public void testGetSingleValueStringFlag() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + + region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getSingleValue(null, DefaultFlag.FAREWELL_MESSAGE), isOneOf("test1", "test2")); + assertThat(result.getSingleValue(null, DefaultFlag.GREET_MESSAGE), is((String) null)); + assertThat(result.getSingleValue(null, DefaultFlag.LAVA_FIRE), is((State) null)); + } + + // ======================================================================== + // ======================================================================== + + @Test + public void testGetValuesTwoWithSamePriority() throws Exception { + // ==================================================================== + // Two regions with the same priority + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + + region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1", "test2"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + @Test + public void testGetValuesTwoWithDuplicateFlagValues() throws Exception { + // ==================================================================== + // Two regions with duplicate values + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test"); + + region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test", "test"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + @Test + public void testGetValuesWithHigherPriority() throws Exception { + // ==================================================================== + // One of the regions has a higher priority (should override) + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(10); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + + region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + @Test + public void testGetValuesWithTwoElevatedPriorities() throws Exception { + // ==================================================================== + // Two regions with the same elevated priority + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(10); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test3"); + + region = mock.add(10); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + + region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1", "test3"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + @Test + public void testGetValuesParentChildWithSamePriority() throws Exception { + // ==================================================================== + // Child region and parent region with the same priority + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(10); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test3"); + + ProtectedRegion region = mock.add(10); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setParent(parent1); + + region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + @Test + public void testGetValuesParentWithHigherPriority() throws Exception { + // ==================================================================== + // Parent region with a higher priority than the child + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(20); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test3"); + + ProtectedRegion region = mock.add(10); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setParent(parent1); + + region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test3"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + @Test + public void testGetValuesParentWithLowerPriority() throws Exception { + // ==================================================================== + // Parent region with a lower priority than the child + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(5); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test3"); + + ProtectedRegion region = mock.add(10); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setParent(parent1); + + region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + @Test + public void testGetValuesThirdRegionWithHigherPriorityThanParentChild() throws Exception { + // ==================================================================== + // Third region with higher priority than parent and child + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(5); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test3"); + + ProtectedRegion region = mock.add(10); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setParent(parent1); + + region = mock.add(20); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test2"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + @Test + public void testGetValuesParentsAndInheritance() throws Exception { + // ==================================================================== + // Multiple regions with parents, one region using flag from parent + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(5); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + + ProtectedRegion region = mock.add(20); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + region.setParent(parent1); + + ProtectedRegion parent2 = mock.add(6); + parent2.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test3"); + + region = mock.add(20); + region.setParent(parent2); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test2", "test3"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + @Test + public void testGetValuesParentsAndInheritanceHighPriorityAndNoFlag() throws Exception { + // ==================================================================== + // Multiple regions with parents, one region with high priority but no flag + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(5); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + + ProtectedRegion region = mock.add(20); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + region.setParent(parent1); + + ProtectedRegion parent2 = mock.add(6); + parent2.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test3"); + + region = mock.add(30); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test2"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + @Test + public void testGetValuesParentWithSamePriorityAsHighest() throws Exception { + // ==================================================================== + // As before, except a parent region has the same priority as the previous highest + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(30); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + + ProtectedRegion region = mock.add(20); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + region.setParent(parent1); + + ProtectedRegion parent2 = mock.add(6); + parent2.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test3"); + + region = mock.add(30); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); + assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + // ======================================================================== + // ======================================================================== + + @Test + public void testGetEffectiveFlagSingleRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + // ==================================================================== + // Single region + // ==================================================================== + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagWithALLGroupAndNonMember() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + // ==================================================================== + // Single region with group ALL and non-member player + // ==================================================================== + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagWithALLGroupAndNull() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + // ==================================================================== + // Single region with group ALL and null player + // ==================================================================== + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagNONMEMBERSGroupNonMember() throws Exception { + // ==================================================================== + // Single region with group NON-MEMBERS and non-member player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.NON_MEMBERS); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagNONMEMBERSGroupNull() throws Exception { + // ==================================================================== + // Single region with group NON-MEMBERS and null player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.NON_MEMBERS); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagNONOWNERSGroupNonMember() throws Exception { + // ==================================================================== + // Single region with group NON-OWNERS and non-member player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.NON_OWNERS); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagMEMBERSGroupNonMember() throws Exception { + // ==================================================================== + // Single region with group MEMBERS and non-member player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.MEMBERS); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo(null)); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagMEMBERSGroupNull() throws Exception { + // ==================================================================== + // Single region with group MEMBERS and null player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.MEMBERS); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo(null)); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagMEMBERSGroupMember() throws Exception { + // ==================================================================== + // Single region with group MEMBERS and member player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.MEMBERS); + + LocalPlayer player = mock.createPlayer(); + region.getMembers().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagMEMBERSGroupOwner() throws Exception { + // ==================================================================== + // Single region with group MEMBERS and owner player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.MEMBERS); + + LocalPlayer player = mock.createPlayer(); + region.getOwners().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagOWNERSGroupOwner() throws Exception { + // ==================================================================== + // Single region with group OWNERS and owner player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.OWNERS); + + LocalPlayer player = mock.createPlayer(); + region.getOwners().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagOWNERSGroupMember() throws Exception { + // ==================================================================== + // Single region with group OWNERS and member player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.OWNERS); + + LocalPlayer player = mock.createPlayer(); + region.getMembers().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo(null)); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagNONOWNERSGroupOwner() throws Exception { + // ==================================================================== + // Single region with group NON-OWNERS and owner player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.NON_OWNERS); + + LocalPlayer player = mock.createPlayer(); + region.getOwners().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo(null)); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagNONMEMBERSGroupOwner() throws Exception { + // ==================================================================== + // Single region with group NON-MEMBERS and owner player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.NON_MEMBERS); + + LocalPlayer player = mock.createPlayer(); + region.getOwners().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo(null)); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagNONOWNERSGroupMember() throws Exception { + // ==================================================================== + // Single region with group NON-OWNERS and member player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.NON_OWNERS); + + LocalPlayer player = mock.createPlayer(); + region.getMembers().addPlayer(player); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagNONOWNERSNonMember() throws Exception { + // ==================================================================== + // Single region with group NON-OWNERS and non-member player + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.NON_OWNERS); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagThreeInheritance() throws Exception { + // ==================================================================== + // Three-level inheritance + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(0); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + ProtectedRegion parent2 = mock.add(0); + parent2.setParent(parent1); + + ProtectedRegion region = mock.add(0); + region.setParent(parent2); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("test1")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagThreeInheritanceMiddleOverride() throws Exception { + // ==================================================================== + // Three-level inheritance, overridden on middle level + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(0); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + ProtectedRegion parent2 = mock.add(0); + parent2.setParent(parent1); + parent2.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + ProtectedRegion region = mock.add(0); + region.setParent(parent2); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("test2")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagThreeInheritanceLastOverride() throws Exception { + // ==================================================================== + // Three-level inheritance, overridden on last level + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(0); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + ProtectedRegion parent2 = mock.add(0); + parent2.setParent(parent1); + + ProtectedRegion region = mock.add(0); + region.setParent(parent2); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test3"); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("test3")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagInheritanceAndDifferingGroups() throws Exception { + // ==================================================================== + // Three-level inheritance, overridden on last level + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(0); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "everyone"); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + ProtectedRegion parent2 = mock.add(0); + parent2.setFlag(DefaultFlag.FAREWELL_MESSAGE, "members"); + parent2.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.MEMBERS); + parent2.setParent(parent1); + + ProtectedRegion region = mock.add(0); + region.setParent(parent2); + + LocalPlayer player1 = mock.createPlayer(); + parent2.getMembers().addPlayer(player1); + + LocalPlayer player2 = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player1), equalTo("members")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player2), equalTo("everyone")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("everyone")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagInheritanceAndDifferingGroupsMemberOnChild() throws Exception { + // ==================================================================== + // Three-level inheritance, overridden on last level + // ==================================================================== + + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(0); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "everyone"); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + ProtectedRegion parent2 = mock.add(0); + parent2.setFlag(DefaultFlag.FAREWELL_MESSAGE, "members"); + parent2.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.MEMBERS); + parent2.setParent(parent1); + + ProtectedRegion region = mock.add(0); + region.setParent(parent2); + + LocalPlayer player1 = mock.createPlayer(); + region.getMembers().addPlayer(player1); + + LocalPlayer player2 = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player1), equalTo("members")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player2), equalTo("everyone")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("everyone")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagInheritanceAndDifferingGroupsMemberOnParent() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(0); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "everyone"); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + ProtectedRegion parent2 = mock.add(0); + parent2.setFlag(DefaultFlag.FAREWELL_MESSAGE, "members"); + parent2.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.MEMBERS); + parent2.setParent(parent1); + + ProtectedRegion region = mock.add(0); + region.setParent(parent2); + + LocalPlayer player1 = mock.createPlayer(); + parent1.getMembers().addPlayer(player1); + + LocalPlayer player2 = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player1), equalTo("members")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player2), equalTo("everyone")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("everyone")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagInheritanceAndDifferingGroupsMemberOnParentFlagOnBottom() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.add(0); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "everyone"); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + ProtectedRegion parent2 = mock.add(0); + parent2.setParent(parent1); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "members"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.MEMBERS); + region.setParent(parent2); + + LocalPlayer player1 = mock.createPlayer(); + parent1.getMembers().addPlayer(player1); + + LocalPlayer player2 = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player1), equalTo("members")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player2), equalTo("everyone")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("everyone")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagInheritanceAndDifferingGroupsMemberOnParentFlagOnBottomGroupOutside() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion parent1 = mock.createOutside(0); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE, "everyone"); + parent1.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.ALL); + + ProtectedRegion parent2 = mock.add(0); + parent2.setParent(parent1); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "members"); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE.getRegionGroupFlag(), RegionGroup.MEMBERS); + region.setParent(parent2); + + LocalPlayer player1 = mock.createPlayer(); + parent1.getMembers().addPlayer(player1); + + LocalPlayer player2 = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player1), equalTo("members")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, player2), equalTo("everyone")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("everyone")); + assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); + } +} \ No newline at end of file diff --git a/src/test/java/com/sk89q/worldguard/protection/MockApplicableRegionSet.java b/src/test/java/com/sk89q/worldguard/protection/MockApplicableRegionSet.java index 24939297..1459d52b 100644 --- a/src/test/java/com/sk89q/worldguard/protection/MockApplicableRegionSet.java +++ b/src/test/java/com/sk89q/worldguard/protection/MockApplicableRegionSet.java @@ -51,6 +51,13 @@ public ProtectedRegion global() { return global; } + public ProtectedRegion createOutside(int priority) { + ProtectedRegion region = new ProtectedCuboidRegion(getNextId(), + new BlockVector(0, 0, 0), new BlockVector(1, 1, 1)); + region.setPriority(priority); + return region; + } + public ProtectedRegion add(int priority) { ProtectedRegion region = new ProtectedCuboidRegion(getNextId(), new BlockVector(0, 0, 0), new BlockVector(1, 1, 1)); @@ -77,6 +84,10 @@ public ApplicableRegionSet getApplicableSet() { return new ApplicableRegionSet(regions, global); } + public FlagValueCalculator getFlagCalculator() { + return new FlagValueCalculator(regions, global); + } + private String getNextId() { id++; return "REGION_" + id; From 4d43ef53053b06b6d4aec473a3dc462aabce5c80 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 16 Aug 2014 13:08:09 -0700 Subject: [PATCH 057/133] Change flags to use ALL as their region group. --- .../protection/flags/DefaultFlag.java | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/DefaultFlag.java b/src/main/java/com/sk89q/worldguard/protection/flags/DefaultFlag.java index e22bbd4a..2b083949 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/DefaultFlag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/DefaultFlag.java @@ -28,18 +28,18 @@ */ public final class DefaultFlag { - public static final StateFlag PASSTHROUGH = new StateFlag("passthrough", false, RegionGroup.ALL); - public static final StateFlag BUILD = new StateFlag("build", true, RegionGroup.NON_MEMBERS); + public static final StateFlag PASSTHROUGH = new StateFlag("passthrough", false); + public static final StateFlag BUILD = new StateFlag("build", true); public static final RegionGroupFlag CONSTRUCT = new RegionGroupFlag("construct", RegionGroup.MEMBERS); - public static final StateFlag PVP = new StateFlag("pvp", true, RegionGroup.ALL); - public static final StateFlag MOB_DAMAGE = new StateFlag("mob-damage", true, RegionGroup.ALL); - public static final StateFlag MOB_SPAWNING = new StateFlag("mob-spawning", true, RegionGroup.ALL); - public static final StateFlag CREEPER_EXPLOSION = new StateFlag("creeper-explosion", true, RegionGroup.ALL); + public static final StateFlag PVP = new StateFlag("pvp", true); + public static final StateFlag MOB_DAMAGE = new StateFlag("mob-damage", true); + public static final StateFlag MOB_SPAWNING = new StateFlag("mob-spawning", true); + public static final StateFlag CREEPER_EXPLOSION = new StateFlag("creeper-explosion", true); public static final StateFlag ENDERDRAGON_BLOCK_DAMAGE = new StateFlag("enderdragon-block-damage", true); - public static final StateFlag GHAST_FIREBALL = new StateFlag("ghast-fireball", true, RegionGroup.ALL); + public static final StateFlag GHAST_FIREBALL = new StateFlag("ghast-fireball", true); public static final StateFlag OTHER_EXPLOSION = new StateFlag("other-explosion", true); public static final StateFlag SLEEP = new StateFlag("sleep", true); - public static final StateFlag TNT = new StateFlag("tnt", true, RegionGroup.ALL); + public static final StateFlag TNT = new StateFlag("tnt", true); public static final StateFlag LIGHTER = new StateFlag("lighter", true); public static final StateFlag FIRE_SPREAD = new StateFlag("fire-spread", true); public static final StateFlag LAVA_FIRE = new StateFlag("lava-fire", true); @@ -62,8 +62,8 @@ public final class DefaultFlag { public static final StateFlag VINE_GROWTH = new StateFlag("vine-growth", true); public static final StateFlag SOIL_DRY = new StateFlag("soil-dry", true); public static final StateFlag ENDER_BUILD = new StateFlag("enderman-grief", true); - public static final StateFlag INVINCIBILITY = new StateFlag("invincible", false, RegionGroup.ALL); - public static final StateFlag EXP_DROPS = new StateFlag("exp-drops", true, RegionGroup.ALL); + public static final StateFlag INVINCIBILITY = new StateFlag("invincible", false); + public static final StateFlag EXP_DROPS = new StateFlag("exp-drops", true); public static final StateFlag SEND_CHAT = new StateFlag("send-chat", true); public static final StateFlag RECEIVE_CHAT = new StateFlag("receive-chat", true); public static final StateFlag ENTRY = new StateFlag("entry", true); @@ -73,29 +73,29 @@ public final class DefaultFlag { public static final StateFlag ENTITY_PAINTING_DESTROY = new StateFlag("entity-painting-destroy", true); public static final StateFlag ENTITY_ITEM_FRAME_DESTROY = new StateFlag("entity-item-frame-destroy", true); public static final StateFlag POTION_SPLASH = new StateFlag("potion-splash", true); - public static final StringFlag GREET_MESSAGE = new StringFlag("greeting", RegionGroup.ALL); - public static final StringFlag FAREWELL_MESSAGE = new StringFlag("farewell", RegionGroup.ALL); - public static final BooleanFlag NOTIFY_ENTER = new BooleanFlag("notify-enter", RegionGroup.ALL); - public static final BooleanFlag NOTIFY_LEAVE = new BooleanFlag("notify-leave", RegionGroup.ALL); - public static final SetFlag DENY_SPAWN = new SetFlag("deny-spawn", RegionGroup.ALL, new EntityTypeFlag(null)); - public static final EnumFlag GAME_MODE = new EnumFlag("game-mode", GameMode.class, RegionGroup.ALL); - public static final IntegerFlag HEAL_DELAY = new IntegerFlag("heal-delay", RegionGroup.ALL); - public static final IntegerFlag HEAL_AMOUNT = new IntegerFlag("heal-amount", RegionGroup.ALL); - public static final DoubleFlag MIN_HEAL = new DoubleFlag("heal-min-health", RegionGroup.ALL); - public static final DoubleFlag MAX_HEAL = new DoubleFlag("heal-max-health", RegionGroup.ALL); - public static final IntegerFlag FEED_DELAY = new IntegerFlag("feed-delay", RegionGroup.ALL); - public static final IntegerFlag FEED_AMOUNT = new IntegerFlag("feed-amount", RegionGroup.ALL); - public static final IntegerFlag MIN_FOOD = new IntegerFlag("feed-min-hunger", RegionGroup.ALL); - public static final IntegerFlag MAX_FOOD = new IntegerFlag("feed-max-hunger", RegionGroup.ALL); - // public static final IntegerFlag MAX_PLAYERS = new IntegerFlag("max-players-allowed", RegionGroup.ALL); - // public static final StringFlag MAX_PLAYERS_MESSAGE = new StringFlag("max-players-reject-message", RegionGroup.ALL); + public static final StringFlag GREET_MESSAGE = new StringFlag("greeting"); + public static final StringFlag FAREWELL_MESSAGE = new StringFlag("farewell"); + public static final BooleanFlag NOTIFY_ENTER = new BooleanFlag("notify-enter"); + public static final BooleanFlag NOTIFY_LEAVE = new BooleanFlag("notify-leave"); + public static final SetFlag DENY_SPAWN = new SetFlag("deny-spawn", new EntityTypeFlag(null)); + public static final EnumFlag GAME_MODE = new EnumFlag("game-mode", GameMode.class); + public static final IntegerFlag HEAL_DELAY = new IntegerFlag("heal-delay"); + public static final IntegerFlag HEAL_AMOUNT = new IntegerFlag("heal-amount"); + public static final DoubleFlag MIN_HEAL = new DoubleFlag("heal-min-health"); + public static final DoubleFlag MAX_HEAL = new DoubleFlag("heal-max-health"); + public static final IntegerFlag FEED_DELAY = new IntegerFlag("feed-delay"); + public static final IntegerFlag FEED_AMOUNT = new IntegerFlag("feed-amount"); + public static final IntegerFlag MIN_FOOD = new IntegerFlag("feed-min-hunger"); + public static final IntegerFlag MAX_FOOD = new IntegerFlag("feed-max-hunger"); + // public static final IntegerFlag MAX_PLAYERS = new IntegerFlag("max-players-allowed"); + // public static final StringFlag MAX_PLAYERS_MESSAGE = new StringFlag("max-players-reject-message"); public static final LocationFlag TELE_LOC = new LocationFlag("teleport", RegionGroup.MEMBERS); public static final LocationFlag SPAWN_LOC = new LocationFlag("spawn", RegionGroup.MEMBERS); public static final StateFlag ENABLE_SHOP = new StateFlag("allow-shop", false); public static final BooleanFlag BUYABLE = new BooleanFlag("buyable"); public static final DoubleFlag PRICE = new DoubleFlag("price"); - public static final SetFlag BLOCKED_CMDS = new SetFlag("blocked-cmds", RegionGroup.ALL, new CommandStringFlag(null)); - public static final SetFlag ALLOWED_CMDS = new SetFlag("allowed-cmds", RegionGroup.ALL, new CommandStringFlag(null)); + public static final SetFlag BLOCKED_CMDS = new SetFlag("blocked-cmds", new CommandStringFlag(null)); + public static final SetFlag ALLOWED_CMDS = new SetFlag("allowed-cmds", new CommandStringFlag(null)); public static final Flag[] flagsList = new Flag[] { PASSTHROUGH, BUILD, CONSTRUCT, PVP, CHEST_ACCESS, PISTONS, From 7481acba8c3b4be02e180cf717f4c0b80bac4b36 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 16 Aug 2014 13:30:58 -0700 Subject: [PATCH 058/133] Updated FlagValueCalculator to treat global regions a lowest priority region. --- .../protection/ApplicableRegionSet.java | 293 +----- .../protection/FlagValueCalculator.java | 204 ++-- .../worldguard/protection/flags/Flag.java | 18 - .../protection/flags/RegionGroupFlag.java | 1 - .../protection/flags/StateFlag.java | 10 +- .../protection/FlagValueCalculatorTest.java | 955 ++++++++++-------- 6 files changed, 629 insertions(+), 852 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java index 6a9fe1e2..62d5596f 100644 --- a/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java +++ b/src/main/java/com/sk89q/worldguard/protection/ApplicableRegionSet.java @@ -26,23 +26,18 @@ import com.sk89q.worldguard.protection.flags.RegionGroup; import com.sk89q.worldguard.protection.flags.RegionGroupFlag; import com.sk89q.worldguard.protection.flags.StateFlag; -import com.sk89q.worldguard.protection.flags.StateFlag.*; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import javax.annotation.Nullable; 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.SortedSet; import java.util.TreeSet; import static com.google.common.base.Preconditions.checkNotNull; -import static com.sk89q.worldguard.protection.flags.StateFlag.*; +import static com.sk89q.worldguard.protection.flags.StateFlag.test; /** * Represents the effective set of flags, owners, and members for a given @@ -61,6 +56,7 @@ public class ApplicableRegionSet implements Iterable { private final SortedSet applicable; @Nullable private final ProtectedRegion globalRegion; + private final FlagValueCalculator flagValueCalculator; /** * Construct the object. @@ -84,6 +80,7 @@ public ApplicableRegionSet(SortedSet applicable, @Nullable Prot checkNotNull(applicable); this.applicable = applicable; this.globalRegion = globalRegion; + this.flagValueCalculator = new FlagValueCalculator(applicable, globalRegion); } /** @@ -94,7 +91,7 @@ public ApplicableRegionSet(SortedSet applicable, @Nullable Prot */ public boolean canBuild(LocalPlayer player) { checkNotNull(player); - return test(calculateState(DefaultFlag.BUILD, new RegionMemberTest(player), null)); + return test(flagValueCalculator.testPermission(player, DefaultFlag.BUILD)); } /** @@ -123,7 +120,7 @@ public boolean allows(StateFlag flag) { throw new IllegalArgumentException("Can't use build flag with allows()"); } - return test(calculateState(flag, null, null)); + return test(flagValueCalculator.queryState(null, flag)); } /** @@ -140,7 +137,8 @@ public boolean allows(StateFlag flag, @Nullable LocalPlayer player) { if (flag == DefaultFlag.BUILD) { throw new IllegalArgumentException("Can't use build flag with allows()"); } - return test(calculateState(flag, null, player)); + + return test(flagValueCalculator.queryState(player, flag)); } /** @@ -179,206 +177,6 @@ public boolean isMemberOfAll(LocalPlayer player) { return true; } - /** - * Calculate the effective value of a flag based on the regions - * in this set, membership, the global region (if set), and the default - * value of a flag {@link StateFlag#getDefault()}. - * - * @param flag the flag to check - * @param membershipTest null to perform a "wilderness check" or a predicate - * returns true if a the subject is a member of the - * region passed - * @param groupPlayer a player to use for the group flag check - * @return the allow/deny state for the flag - */ - private State calculateState(StateFlag flag, @Nullable Predicate membershipTest, @Nullable LocalPlayer groupPlayer) { - checkNotNull(flag); - - // This method works in two modes: - // - // 1) Membership mode (if membershipTest != null): - // a) Regions in this set -> Check membership + Check region flags - // a) No regions -> Use global region + default value - // 1) Flag mode: - // a) Regions in this set -> Use global region + default value - // a) No regions -> Use global region + default value - - int minimumPriority = Integer.MIN_VALUE; - boolean regionsThatCountExistHere = false; // We can't do a application.isEmpty() because - // PASSTHROUGH regions have to be skipped - // (in some cases) - State state = null; // Start with NONE - - // Say there are two regions in one location: CHILD and PARENT (CHILD - // is a child of PARENT). If there are two overlapping regions in WG, a - // player has to be a member of /both/ (or flags permit) in order to - // build in that location. However, inheritance is supposed - // to allow building if the player is a member of just CHILD. That - // presents a problem. - // - // To rectify this, we keep two sets. When we iterate over the list of - // regions, there are two scenarios that we may encounter: - // - // 1) PARENT first, CHILD later: - // a) When the loop reaches PARENT, PARENT is added to needsClear. - // b) When the loop reaches CHILD, parents of CHILD (which includes - // PARENT) are removed from needsClear. - // c) needsClear is empty again. - // - // 2) CHILD first, PARENT later: - // a) When the loop reaches CHILD, CHILD's parents (i.e. PARENT) are - // added to hasCleared. - // b) When the loop reaches PARENT, since PARENT is already in - // hasCleared, it does not add PARENT to needsClear. - // c) needsClear stays empty. - // - // As long as the process ends with needsClear being empty, then - // we have satisfied all membership requirements. - - Set needsClear = new HashSet(); - Set hasCleared = new HashSet(); - - for (ProtectedRegion region : applicable) { - // Don't consider lower priorities below minimumPriority - // (which starts at Integer.MIN_VALUE). A region that "counts" - // (has the flag set OR has members) will raise minimumPriority - // to its own priority. - if (region.getPriority() < minimumPriority) { - break; - } - - // If PASSTHROUGH is set and we are checking to see if a player - // is a member, then skip this region - if (membershipTest != null && getStateFlagIncludingParents(region, DefaultFlag.PASSTHROUGH) == State.ALLOW) { - continue; - } - - // If the flag has a group set on to it, skip this region if - // the group does not match our (group) player - if (groupPlayer != null && flag.getRegionGroupFlag() != null) { - RegionGroup group = region.getFlag(flag.getRegionGroupFlag()); - if (group == null) { - group = flag.getRegionGroupFlag().getDefault(); - } - - if (!RegionGroupFlag.isMember(region, group, groupPlayer)) { - continue; - } - } - - regionsThatCountExistHere = true; - - State v = getStateFlagIncludingParents(region, flag); - - // DENY overrides everything - if (v == State.DENY) { - state = State.DENY; - break; // No need to process any more regions - - // ALLOW means we don't care about membership - } else if (v == State.ALLOW) { - state = State.ALLOW; - minimumPriority = region.getPriority(); - - } else { - if (membershipTest != null) { - minimumPriority = region.getPriority(); - - if (!hasCleared.contains(region)) { - if (!membershipTest.apply(region)) { - needsClear.add(region); - } else { - // Need to clear all parents - clearParents(needsClear, hasCleared, region); - } - } - } - } - } - - if (membershipTest != null) { - State fallback; - - if (regionsThatCountExistHere) { - fallback = allowOrNone(needsClear.isEmpty()); - } else { - fallback = getDefault(flag, membershipTest); - } - - return combine(state, fallback); - } else { - return combine(state, getDefault(flag, null)); - } - } - - @Nullable - private State getDefault(StateFlag flag, @Nullable Predicate membershipTest) { - boolean allowed = flag.getDefault() == State.ALLOW; - - // Handle defaults - if (globalRegion != null) { - State globalState = globalRegion.getFlag(flag); - - // The global region has this flag set - if (globalState != null) { - // Build flag is very special - if (membershipTest != null && globalRegion.hasMembersOrOwners()) { - allowed = membershipTest.apply(globalRegion) && (globalState == State.ALLOW); - } else { - allowed = (globalState == State.ALLOW); - } - } else { - // Build flag is very special - if (membershipTest != null && globalRegion.hasMembersOrOwners()) { - allowed = membershipTest.apply(globalRegion); - } - } - } - - return allowed ? State.ALLOW : null; - } - - /** - * Clear a region's parents for isFlagAllowed(). - * - * @param needsClear the regions that should be cleared - * @param hasCleared the regions already cleared - * @param region the region to start from - */ - private void clearParents(Set needsClear, Set hasCleared, ProtectedRegion region) { - ProtectedRegion parent = region.getParent(); - - while (parent != null) { - if (!needsClear.remove(parent)) { - hasCleared.add(parent); - } - - parent = parent.getParent(); - } - } - - /** - * Get a region's state flag, checking parent regions until a value for the - * flag can be found (if one even exists). - * - * @param region the region - * @param flag the flag - * @return the value - */ - private static State getStateFlagIncludingParents(ProtectedRegion region, StateFlag flag) { - while (region != null) { - State value = region.getFlag(flag); - - if (value != null) { - return value; - } - - region = region.getParent(); - } - - return null; - } - /** * Gets the value of a flag. Do not use this for state flags * (use {@link #allows(StateFlag, LocalPlayer)} for that). @@ -402,82 +200,7 @@ public , V> V getFlag(T flag) { */ @Nullable public , V> V getFlag(T flag, @Nullable LocalPlayer groupPlayer) { - checkNotNull(flag); - - /* - if (flag instanceof StateFlag) { - throw new IllegalArgumentException("Cannot use StateFlag with getFlag()"); - } - */ - - int lastPriority = 0; - boolean found = false; - - Map needsClear = new HashMap(); - Set hasCleared = new HashSet(); - - for (ProtectedRegion region : applicable) { - // Ignore lower priority regions - if (found && region.getPriority() < lastPriority) { - break; - } - - // Check group permissions - if (groupPlayer != null && flag.getRegionGroupFlag() != null) { - RegionGroup group = region.getFlag(flag.getRegionGroupFlag()); - - if (group == null) { - group = flag.getRegionGroupFlag().getDefault(); - } - - if (!RegionGroupFlag.isMember(region, group, groupPlayer)) { - continue; - } - } - - //noinspection StatementWithEmptyBody - if (hasCleared.contains(region)) { - // Already cleared, so do nothing - } else if (region.getFlag(flag) != null) { - clearParents(needsClear, hasCleared, region); - - needsClear.put(region, region.getFlag(flag)); - - found = true; - } - - lastPriority = region.getPriority(); - } - - if (!needsClear.isEmpty()) { - return needsClear.values().iterator().next(); - } else { - if (globalRegion != null) { - V gFlag = globalRegion.getFlag(flag); - if (gFlag != null) return gFlag; - } - return null; - } - } - - /** - * Clear a region's parents for getFlag(). - * - * @param needsClear The regions that should be cleared - * @param hasCleared The regions already cleared - * @param region The region to start from - */ - private void clearParents(Map needsClear, - Set hasCleared, ProtectedRegion region) { - ProtectedRegion parent = region.getParent(); - - while (parent != null) { - if (needsClear.remove(parent) == null) { - hasCleared.add(parent); - } - - parent = parent.getParent(); - } + return flagValueCalculator.queryValue(groupPlayer, flag); } /** diff --git a/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java b/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java index b8bdb2f6..3455ab9d 100644 --- a/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java +++ b/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java @@ -19,6 +19,8 @@ package com.sk89q.worldguard.protection; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.protection.flags.DefaultFlag; import com.sk89q.worldguard.protection.flags.Flag; @@ -50,23 +52,37 @@ */ public class FlagValueCalculator { - private final SortedSet applicable; + private final SortedSet regions; @Nullable private final ProtectedRegion globalRegion; /** * Create a new instance. * - * @param applicable a list of applicable regions + * @param regions a list of applicable regions * @param globalRegion an optional global region (null to not use one) */ - public FlagValueCalculator(SortedSet applicable, @Nullable ProtectedRegion globalRegion) { - checkNotNull(applicable); + public FlagValueCalculator(SortedSet regions, @Nullable ProtectedRegion globalRegion) { + checkNotNull(regions); - this.applicable = applicable; + this.regions = regions; this.globalRegion = globalRegion; } + /** + * Returns an iterable of regions sorted by priority (descending), with + * the global region tacked on at the end if one exists. + * + * @return an iterable + */ + private Iterable getApplicable() { + if (globalRegion != null) { + return Iterables.concat(regions, ImmutableList.of(globalRegion)); + } else { + return regions; + } + } + /** * Return the membership status of the given player, indicating * whether there are no (counted) regions in the list of regions, @@ -119,12 +135,12 @@ public Result getMembership(LocalPlayer player) { Set needsClear = new HashSet(); Set hasCleared = new HashSet(); - for (ProtectedRegion region : applicable) { + for (ProtectedRegion region : getApplicable()) { // Don't consider lower priorities below minimumPriority // (which starts at Integer.MIN_VALUE). A region that "counts" // (has the flag set OR has members) will raise minimumPriority // to its own priority. - if (region.getPriority() < minimumPriority) { + if (getPriority(region) < minimumPriority) { break; } @@ -133,7 +149,7 @@ public Result getMembership(LocalPlayer player) { continue; } - minimumPriority = region.getPriority(); + minimumPriority = getPriority(region); foundApplicableRegion = true; if (!hasCleared.contains(region)) { @@ -194,37 +210,27 @@ public State testPermission(LocalPlayer player, StateFlag... flags) { switch (getMembership(player)) { case SUCCESS: - return StateFlag.combine(getState(player, flags), State.ALLOW); + return StateFlag.combine(queryState(player, flags), State.ALLOW); case FAIL: - return getState(player, flags); + return queryState(player, flags); case NO_REGIONS: - if (globalRegion != null && globalRegion.hasMembersOrOwners()) { - if (globalRegion.isMember(player)) { - return StateFlag.combine(getState(player, flags), State.ALLOW); - } else { - State value = null; - - for (StateFlag flag : flags) { - value = StateFlag.combine(value,globalRegion.getFlag(flag)); - if (value == State.DENY) { - break; - } - } - - return value; + default: + State fallback = null; + for (StateFlag flag : flags) { + if (flag.getDefault()) { + fallback = State.ALLOW; + break; } } - default: - return getStateWithFallback(player, flags); + return StateFlag.combine(queryState(player, flags), fallback); } } + /** * Get the effective value for a list of state flags. The rules of * states is observed here; that is, {@code DENY} overrides {@code ALLOW}, - * and {@code ALLOW} overrides {@code NONE}. This method will check - * the global region and {@link Flag#getDefault()} (in that order) if - * a value for the flag is not set in any region. + * and {@code ALLOW} overrides {@code NONE}. * *

This method does not properly process build * permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)} @@ -245,50 +251,11 @@ public State testPermission(LocalPlayer player, StateFlag... flags) { * @return a state */ @Nullable - public State getStateWithFallback(@Nullable LocalPlayer player, StateFlag... flags) { + public State queryState(@Nullable LocalPlayer player, StateFlag... flags) { State value = null; for (StateFlag flag : flags) { - value = StateFlag.combine(value, getSingleValueWithFallback(player, flag)); - if (value == State.DENY) { - break; - } - } - - return value; - } - - - /** - * Get the effective value for a list of state flags. The rules of - * states is observed here; that is, {@code DENY} overrides {@code ALLOW}, - * and {@code ALLOW} overrides {@code NONE}. This method does not check - * the global region and ignores a flag's default value. - * - *

This method does not properly process build - * permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)} - * for that purpose. This method is ideal for testing non-build related - * state flags (although a rarity), an example of which would be whether - * to play a song to players that enter an area.

- * - *

A player can be provided that is used to determine whether the value - * of a flag on a particular region should be used. For example, if a - * flag's region group is set to {@link RegionGroup#MEMBERS} and the given - * player is not a member, then the region would be skipped when - * querying that flag. If {@code null} is provided for the player, then - * only flags that use {@link RegionGroup#ALL}, - * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

- * - * @param player an optional player, which would be used to determine the region group to apply - * @param flags a list of flags to check - * @return a state - */ - @Nullable - public State getState(@Nullable LocalPlayer player, StateFlag... flags) { - State value = null; - - for (StateFlag flag : flags) { - value = StateFlag.combine(value, getSingleValue(player, flag)); + value = StateFlag.combine(value, queryValue(player, flag)); if (value == State.DENY) { break; } @@ -302,8 +269,7 @@ public State getState(@Nullable LocalPlayer player, StateFlag... flags) { * (for example, if there are multiple regions with the same priority * but with different farewell messages set, there would be multiple * completing values), then the selected (or "winning") value will depend - * on the flag type. This method will check the global region - * for a value as well as the flag's default value. + * on the flag type. * *

Only some flag types actually have a strategy for picking the * "best value." For most types, the actual value that is chosen to be @@ -326,65 +292,10 @@ public State getState(@Nullable LocalPlayer player, StateFlag... flags) { * @param player an optional player, which would be used to determine the region group to apply * @param flag the flag * @return a value, which could be {@code null} - * @see #getSingleValue(LocalPlayer, Flag) does not check global region, defaults */ @Nullable - public V getSingleValueWithFallback(@Nullable LocalPlayer player, Flag flag) { - checkNotNull(flag); - - V value = getSingleValue(player, flag); - - if (value != null) { - return value; - } - - // Get the value from the global region - if (globalRegion != null) { - value = globalRegion.getFlag(flag); - } - - // Still no value? Check the default value for the flag - if (value == null) { - value = flag.getDefault(); - } - - return flag.validateDefaultValue(value); - } - - /** - * Get the effective value for a flag. If there are multiple values - * (for example, if there are multiple regions with the same priority - * but with different farewell messages set, there would be multiple - * completing values), then the selected (or "winning") value will depend - * on the flag type. This method never checks the global region or - * the flag's default value. - * - *

Only some flag types actually have a strategy for picking the - * "best value." For most types, the actual value that is chosen to be - * returned is undefined (it could be any value). As of writing, the only - * type of flag that can consistently return the same 'best' value is - * {@link StateFlag}.

- * - *

This method does not properly process build - * permissions. Instead, use {@link #testPermission(LocalPlayer, StateFlag...)} - * for that purpose.

- * - *

A player can be provided that is used to determine whether the value - * of a flag on a particular region should be used. For example, if a - * flag's region group is set to {@link RegionGroup#MEMBERS} and the given - * player is not a member, then the region would be skipped when - * querying that flag. If {@code null} is provided for the player, then - * only flags that use {@link RegionGroup#ALL}, - * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

- * - * @param player an optional player, which would be used to determine the region group to apply - * @param flag the flag - * @return a value, which could be {@code null} - * @see #getSingleValueWithFallback(LocalPlayer, Flag) checks global regions, defaults - */ - @Nullable - public V getSingleValue(@Nullable LocalPlayer player, Flag flag) { - Collection values = getValues(player, flag); + public V queryValue(@Nullable LocalPlayer player, Flag flag) { + Collection values = queryAllValues(player, flag); return flag.chooseValue(values); } @@ -405,7 +316,7 @@ public V getSingleValue(@Nullable LocalPlayer player, Flag flag) { * only flags that use {@link RegionGroup#ALL}, * {@link RegionGroup#NON_MEMBERS}, etc. will apply.

*/ - public Collection getValues(@Nullable LocalPlayer player, Flag flag) { + public Collection queryAllValues(@Nullable LocalPlayer player, Flag flag) { checkNotNull(flag); int minimumPriority = Integer.MIN_VALUE; @@ -441,12 +352,12 @@ public Collection getValues(@Nullable LocalPlayer player, Flag flag) { Map consideredValues = new HashMap(); Set ignoredRegions = new HashSet(); - for (ProtectedRegion region : applicable) { + for (ProtectedRegion region : getApplicable()) { // Don't consider lower priorities below minimumPriority // (which starts at Integer.MIN_VALUE). A region that "counts" // (has the flag set) will raise minimumPriority to its own // priority. - if (region.getPriority() < minimumPriority) { + if (getPriority(region) < minimumPriority) { break; } @@ -454,7 +365,7 @@ public Collection getValues(@Nullable LocalPlayer player, Flag flag) { if (value != null) { if (!ignoredRegions.contains(region)) { - minimumPriority = region.getPriority(); + minimumPriority = getPriority(region); ignoreValuesOfParents(consideredValues, ignoredRegions, region); consideredValues.put(region, value); @@ -471,6 +382,21 @@ public Collection getValues(@Nullable LocalPlayer player, Flag flag) { return consideredValues.values(); } + /** + * Get the effective priority of a region, overriding a region's priority + * when appropriate (i.e. with the global region). + * + * @param region the region + * @return the priority + */ + public int getPriority(final ProtectedRegion region) { + if (region == globalRegion) { + return Integer.MIN_VALUE; + } else { + return region.getPriority(); + } + } + /** * Get a region's state flag, checking parent regions until a value for the * flag can be found (if one even exists). @@ -479,7 +405,19 @@ public Collection getValues(@Nullable LocalPlayer player, Flag flag) { * @param flag the flag * @return the value */ + @SuppressWarnings("unchecked") public V getEffectiveFlag(final ProtectedRegion region, Flag flag, @Nullable LocalPlayer player) { + // The global region normally does not prevent building so + // PASSTHROUGH has to be ALLOW, except when people use the global + // region as a whitelist + if (region == globalRegion && flag == DefaultFlag.PASSTHROUGH) { + if (region.hasMembersOrOwners()) { + return null; + } else { + return (V) State.ALLOW; + } + } + ProtectedRegion current = region; while (current != null) { diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java b/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java index 996c8f3a..63da44d5 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/Flag.java @@ -51,24 +51,6 @@ public String getName() { return name; } - /** - * Suppress the value of the flag that came from the global region, reducing - * its severity (i.e. DENY -> NONE). - * - *

This is really only used for the {@link StateFlag}.

- * - * @param current the value to suppress - * @return a new value - */ - public T validateDefaultValue(T current) { - return current; - } - - @Nullable - public T getDefault() { - return null; - } - @Nullable public T chooseValue(Collection values) { if (!values.isEmpty()) { diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java b/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java index 01232f95..0e84a7eb 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/RegionGroupFlag.java @@ -38,7 +38,6 @@ public RegionGroupFlag(String name, RegionGroup def) { this.def = def; } - @Override public RegionGroup getDefault() { return def; } diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java b/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java index 8e2fed0d..4e3d92e7 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/StateFlag.java @@ -48,14 +48,8 @@ public StateFlag(String name, boolean def) { this.def = def; } - @Override - public State getDefault() { - return def ? State.ALLOW : null; - } - - @Override - public State validateDefaultValue(State current) { - return denyToNone(current); + public boolean getDefault() { + return def; } @Override diff --git a/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java b/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java index a4351ad7..2837cf00 100644 --- a/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java @@ -39,7 +39,7 @@ public class FlagValueCalculatorTest { @Test - public void testGetSingleFallbackMembershipWilderness() throws Exception { + public void testGetMembershipWilderness() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); LocalPlayer player = mock.createPlayer(); @@ -60,6 +60,32 @@ public void testGetMembershipWildernessWithGlobalRegion() throws Exception { assertThat(result.getMembership(player), is(Result.NO_REGIONS)); } + @Test + public void testGetMembershipGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion global = mock.global(); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.NO_REGIONS)); + } + + @Test + public void testGetMembershipGlobalRegionAndRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion global = mock.global(); + + ProtectedRegion region = mock.add(0); + + LocalPlayer player = mock.createPlayer(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getMembership(player), is(Result.FAIL)); + } + @Test public void testGetMembershipPassthroughRegions() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); @@ -296,7 +322,8 @@ public void testTestPermissionWildernessDefaultsWithGlobalRegionOverride() throw FlagValueCalculator result = mock.getFlagCalculator(); assertThat(result.testPermission(player, flag1), is(State.ALLOW)); - assertThat(result.testPermission(player, flag2), is((State) null)); + assertThat(result.testPermission(player, flag2), is(State.DENY)); + assertThat(result.testPermission(player, flag1, flag2), is(State.DENY)); } @Test @@ -348,6 +375,110 @@ public void testTestPermissionWildernessWithGlobalRegionOverride() throws Except assertThat(result.testPermission(nonMember), is((State) null)); } + @Test + public void testTestPermissionWithPassthroughRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is((State) null)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is((State) null)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithPassthroughRegionAndFlagAllow() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag1, State.ALLOW); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is((State) null)); + assertThat(result.testPermission(nonMember, flag1), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithPassthroughRegionAndFlagDeny() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag1, State.DENY); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.DENY)); + assertThat(result.testPermission(member, flag2), is((State) null)); + assertThat(result.testPermission(member, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(member), is((State) null)); + assertThat(result.testPermission(nonMember, flag1), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithPassthroughRegionAndFlagDenyAndRegionGroups() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag1, State.DENY); + region.setFlag(flag1.getRegionGroupFlag(), RegionGroup.MEMBERS); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.DENY)); + assertThat(result.testPermission(member, flag2), is((State) null)); + assertThat(result.testPermission(member, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(member), is((State) null)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is((State) null)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + @Test public void testTestPermissionWithRegion() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); @@ -483,18 +614,18 @@ public void testTestPermissionWithRegionAndGlobalRegionDeny() throws Exception { LocalPlayer nonMember = mock.createPlayer(); ProtectedRegion global = mock.global(); - global.setFlag(flag1, State.DENY); // No effect + global.setFlag(flag1, State.DENY); ProtectedRegion region = mock.add(0); region.getMembers().addPlayer(member); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1), is(State.DENY)); assertThat(result.testPermission(member, flag2), is(State.ALLOW)); - assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); - assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(member, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag1), is(State.DENY)); assertThat(result.testPermission(nonMember, flag2), is((State) null)); - assertThat(result.testPermission(nonMember, flag1, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.DENY)); } @Test @@ -508,7 +639,7 @@ public void testTestPermissionWithRegionAndGlobalRegionAllow() throws Exception LocalPlayer nonMember = mock.createPlayer(); ProtectedRegion global = mock.global(); - global.setFlag(flag1, State.ALLOW); // No effect + global.setFlag(flag1, State.ALLOW); ProtectedRegion region = mock.add(0); region.getMembers().addPlayer(member); @@ -518,13 +649,13 @@ public void testTestPermissionWithRegionAndGlobalRegionAllow() throws Exception assertThat(result.testPermission(member, flag2), is(State.ALLOW)); assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); assertThat(result.testPermission(member), is(State.ALLOW)); - assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag1), is(State.ALLOW)); assertThat(result.testPermission(nonMember, flag2), is((State) null)); assertThat(result.testPermission(nonMember), is((State) null)); } @Test - public void testTestPermissionWithRegionAndGlobalRegionMembership() throws Exception { + public void testTestPermissionWithRegionAllowAndGlobalRegionDeny() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); StateFlag flag1 = new StateFlag("test1", false); @@ -534,7 +665,166 @@ public void testTestPermissionWithRegionAndGlobalRegionMembership() throws Excep LocalPlayer nonMember = mock.createPlayer(); ProtectedRegion global = mock.global(); - global.getMembers().addPlayer(nonMember); + global.setFlag(flag1, State.DENY); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag1, State.ALLOW); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.ALLOW)); + } + + @Test + public void testTestPermissionWithRegionAllowAndGlobalRegionDenyDifferentFlags() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.DENY); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag2, State.ALLOW); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.DENY)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag1), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.DENY)); + } + + @Test + public void testTestPermissionWithPassthroughRegionAllowAndGlobalRegionDenyWithRegionGroup() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", false); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.DENY); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PASSTHROUGH, State.ALLOW); + region.setFlag(flag1, State.ALLOW); + region.setFlag(flag1.getRegionGroupFlag(), RegionGroup.NON_MEMBERS); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.DENY)); + assertThat(result.testPermission(member, flag2), is((State) null)); + assertThat(result.testPermission(member, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag1), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.ALLOW)); + } + + @Test + public void testTestPermissionWithRegionAllowAndGlobalRegionDenyWithRegionGroup() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.DENY); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag1, State.ALLOW); + region.setFlag(flag1.getRegionGroupFlag(), RegionGroup.NON_MEMBERS); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.DENY)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag1), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.ALLOW)); + } + + @Test + public void testTestPermissionWithRegionAllowAndGlobalRegionDenyDifferentFlagsWithRegionGroup() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer member = mock.createPlayer(); + LocalPlayer nonMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.setFlag(flag1, State.DENY); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag2, State.ALLOW); + region.setFlag(flag2.getRegionGroupFlag(), RegionGroup.MEMBERS); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.DENY)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag1), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.DENY)); + } + + @Test + public void testTestPermissionWithGlobalRegionMembership() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer nonMember = mock.createPlayer(); + LocalPlayer globalMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.getMembers().addPlayer(globalMember); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(globalMember, flag1), is(State.ALLOW)); + assertThat(result.testPermission(globalMember, flag2), is(State.ALLOW)); + assertThat(result.testPermission(globalMember, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(globalMember), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is((State) null)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithGlobalRegionMembershipAndRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer nonMember = mock.createPlayer(); + LocalPlayer member = mock.createPlayer(); + LocalPlayer globalMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.getMembers().addPlayer(globalMember); ProtectedRegion region = mock.add(0); region.getMembers().addPlayer(member); @@ -544,8 +834,115 @@ public void testTestPermissionWithRegionAndGlobalRegionMembership() throws Excep assertThat(result.testPermission(member, flag2), is(State.ALLOW)); assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(globalMember, flag1), is((State) null)); + assertThat(result.testPermission(globalMember, flag2), is((State) null)); + assertThat(result.testPermission(globalMember, flag1, flag2), is((State) null)); + assertThat(result.testPermission(globalMember), is((State) null)); assertThat(result.testPermission(nonMember, flag1), is((State) null)); assertThat(result.testPermission(nonMember, flag2), is((State) null)); + assertThat(result.testPermission(nonMember, flag1, flag2), is((State) null)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithGlobalRegionMembershipAndRegionGlobalFlag() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer nonMember = mock.createPlayer(); + LocalPlayer member = mock.createPlayer(); + LocalPlayer globalMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.setFlag(flag2, State.DENY); + global.getMembers().addPlayer(globalMember); + + ProtectedRegion region = mock.add(0); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.DENY)); + assertThat(result.testPermission(member, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(globalMember, flag1), is((State) null)); + assertThat(result.testPermission(globalMember, flag2), is(State.DENY)); + assertThat(result.testPermission(globalMember, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(globalMember), is((State) null)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithGlobalRegionMembershipAndRegionGlobalFlagRegionOverride() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer nonMember = mock.createPlayer(); + LocalPlayer member = mock.createPlayer(); + LocalPlayer globalMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.setFlag(flag2, State.DENY); + global.getMembers().addPlayer(globalMember); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag2, State.ALLOW); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(globalMember, flag1), is((State) null)); + assertThat(result.testPermission(globalMember, flag2), is(State.ALLOW)); + assertThat(result.testPermission(globalMember, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(globalMember), is((State) null)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(nonMember), is((State) null)); + } + + @Test + public void testTestPermissionWithGlobalRegionMembershipAndRegionGlobalFlagRegionOverrideAndRegionGroups() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + StateFlag flag1 = new StateFlag("test1", false); + StateFlag flag2 = new StateFlag("test2", true); + + LocalPlayer nonMember = mock.createPlayer(); + LocalPlayer member = mock.createPlayer(); + LocalPlayer globalMember = mock.createPlayer(); + + ProtectedRegion global = mock.global(); + global.setFlag(flag2, State.DENY); + global.getMembers().addPlayer(globalMember); + + ProtectedRegion region = mock.add(0); + region.setFlag(flag2, State.ALLOW); + region.setFlag(flag2.getRegionGroupFlag(), RegionGroup.MEMBERS); + region.getMembers().addPlayer(member); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.testPermission(member, flag1), is(State.ALLOW)); + assertThat(result.testPermission(member, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member, flag1, flag2), is(State.ALLOW)); + assertThat(result.testPermission(member), is(State.ALLOW)); + assertThat(result.testPermission(globalMember, flag1), is((State) null)); + assertThat(result.testPermission(globalMember, flag2), is(State.DENY)); + assertThat(result.testPermission(globalMember, flag1, flag2), is(State.DENY)); + assertThat(result.testPermission(globalMember), is((State) null)); + assertThat(result.testPermission(nonMember, flag1), is((State) null)); + assertThat(result.testPermission(nonMember, flag2), is(State.DENY)); + assertThat(result.testPermission(nonMember, flag1, flag2), is(State.DENY)); assertThat(result.testPermission(nonMember), is((State) null)); } @@ -553,192 +950,96 @@ public void testTestPermissionWithRegionAndGlobalRegionMembership() throws Excep // ======================================================================== @Test - public void testGetStateWithFallbackSingle() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag = new StateFlag("test", false); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is((State) null)); - } - - @Test - public void testGetStateWithFallbackSeveralNoneAreDenyNoneAreTrue() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag1 = new StateFlag("test1", false); - StateFlag flag2 = new StateFlag("test2", false); - StateFlag flag3 = new StateFlag("test3", false); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is((State) null)); - } - - @Test - public void testGetStateWithFallbackSeveralNoneAreDenyOneIsTrue() throws Exception { + public void testQueryStateWilderness() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); StateFlag flag1 = new StateFlag("test1", false); StateFlag flag2 = new StateFlag("test2", true); - StateFlag flag3 = new StateFlag("test3", false); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.ALLOW)); - } - - @Test - public void testGetStateWithFallbackSeveralNoneAreDenyNoneAreTrueWithEmptyGlobalRegion() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag1 = new StateFlag("test1", false); - StateFlag flag2 = new StateFlag("test2", false); - StateFlag flag3 = new StateFlag("test3", false); - - ProtectedRegion global = mock.global(); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is((State) null)); - } - - @Test - public void testGetStateWithFallbackSeveralNoneAreDenyNoneAreTrueWithGlobalRegionValueSetAllow() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag1 = new StateFlag("test1", false); - StateFlag flag2 = new StateFlag("test2", false); - StateFlag flag3 = new StateFlag("test3", false); - - ProtectedRegion global = mock.global(); - global.setFlag(flag1, State.ALLOW); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.ALLOW)); - } - - @Test - public void testGetStateWithFallbackSeveralNoneAreDenySomeAreTrueWithGlobalRegionValueSetDeny() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag1 = new StateFlag("test1", true); - StateFlag flag2 = new StateFlag("test2", false); - StateFlag flag3 = new StateFlag("test3", false); - - ProtectedRegion global = mock.global(); - global.setFlag(flag1, State.DENY); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is((State) null)); - } - - @Test - public void testGetStateWithFallbackWithGlobalRegionAllowAndRegionDeny() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag1 = new StateFlag("test1", false); - StateFlag flag2 = new StateFlag("test2", false); - StateFlag flag3 = new StateFlag("test3", false); - - ProtectedRegion global = mock.global(); - global.setFlag(flag1, State.ALLOW); - - ProtectedRegion region = mock.add(0); - region.setFlag(flag1, State.DENY); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.DENY)); - } - - @Test - public void testGetStateWithFallbackWithGlobalRegionAllowAndRegionDenyOnDifferentFlag() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag1 = new StateFlag("test1", false); - StateFlag flag2 = new StateFlag("test2", false); - StateFlag flag3 = new StateFlag("test3", false); - - ProtectedRegion global = mock.global(); - global.setFlag(flag1, State.ALLOW); - - ProtectedRegion region = mock.add(0); - region.setFlag(flag2, State.DENY); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.DENY)); - } - - @Test - public void testGetStateWithFallbackWithGlobalRegionDenyAndRegionAllow() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag1 = new StateFlag("test1", false); - StateFlag flag2 = new StateFlag("test2", false); - StateFlag flag3 = new StateFlag("test3", false); - - ProtectedRegion global = mock.global(); - global.setFlag(flag1, State.DENY); - - ProtectedRegion region = mock.add(0); - region.setFlag(flag1, State.ALLOW); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.ALLOW)); - } - - @Test - public void testGetStateWithFallbackWithGlobalRegionDenyOnDifferentAndRegionAllow() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag1 = new StateFlag("test1", false); - StateFlag flag2 = new StateFlag("test2", false); - StateFlag flag3 = new StateFlag("test3", false); - - ProtectedRegion global = mock.global(); - global.setFlag(flag2, State.DENY); - - ProtectedRegion region = mock.add(0); - region.setFlag(flag1, State.ALLOW); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getStateWithFallback(null, flag1, flag2, flag3), is(State.ALLOW)); + assertThat(result.queryState(null, flag1), is((State) null)); + assertThat(result.queryState(null, flag2), is((State) null)); } // ======================================================================== // ======================================================================== @Test - public void testGetSingleValueWithFallbackWithFalseDefaultValue() throws Exception { + public void testQueryValueSingleRegion() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); - StateFlag flag = new StateFlag("test", false); + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PVP, State.DENY); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is((State) null)); + assertThat(result.queryValue(null, DefaultFlag.LIGHTER), is((State) null)); + assertThat(result.queryValue(null, DefaultFlag.PVP), is(State.DENY)); } @Test - public void testGetSingleValueWithFallbackWithTrueDefaultValue() throws Exception { + public void testQueryValueDenyOverridesAllow() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); - StateFlag flag = new StateFlag("test", true); + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.PVP, State.DENY); + + region = mock.add(0); + region.setFlag(DefaultFlag.PVP, State.ALLOW); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); + assertThat(result.queryValue(null, DefaultFlag.LIGHTER), is((State) null)); + assertThat(result.queryValue(null, DefaultFlag.PVP), is(State.DENY)); } @Test - public void testGetSingleValueWithFallbackWithFalseDefaultValueEmptyGlobalRegion() throws Exception { + public void testQueryValueAllowOverridesNone() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); - StateFlag flag = new StateFlag("test", false); + ProtectedRegion region = mock.add(0); - ProtectedRegion global = mock.global(); + region = mock.add(0); + region.setFlag(DefaultFlag.PVP, State.ALLOW); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is((State) null)); + assertThat(result.queryValue(null, DefaultFlag.LIGHTER), is((State) null)); + assertThat(result.queryValue(null, DefaultFlag.PVP), is(State.ALLOW)); } @Test - public void testGetSingleValueWithFallbackWithTrueDefaultValueEmptyGlobalRegion() throws Exception { + public void testQueryValueMultipleFlags() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.LIGHTER, State.DENY); + region.setFlag(DefaultFlag.PVP, State.ALLOW); + + region = mock.add(0); + region.setFlag(DefaultFlag.PVP, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.queryValue(null, DefaultFlag.LIGHTER), is(State.DENY)); + assertThat(result.queryValue(null, DefaultFlag.PVP), is(State.DENY)); + assertThat(result.queryValue(null, DefaultFlag.LAVA_FIRE), is((State) null)); + } + + @Test + public void testQueryValueStringFlag() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); + + region = mock.add(0); + region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.queryValue(null, DefaultFlag.FAREWELL_MESSAGE), isOneOf("test1", "test2")); + assertThat(result.queryValue(null, DefaultFlag.GREET_MESSAGE), is((String) null)); + assertThat(result.queryValue(null, DefaultFlag.LAVA_FIRE), is((State) null)); + } + + @Test + public void testQueryValueEmptyGlobalRegion() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); StateFlag flag = new StateFlag("test", true); @@ -746,11 +1047,11 @@ public void testGetSingleValueWithFallbackWithTrueDefaultValueEmptyGlobalRegion( ProtectedRegion global = mock.global(); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); + assertThat(result.queryValue(null, flag), is((State) null)); } @Test - public void testGetSingleValueWithFallbackWithDefaultValueSameGlobalRegion() throws Exception { + public void testQueryValueGlobalRegionAllow() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); StateFlag flag = new StateFlag("test", true); @@ -759,11 +1060,11 @@ public void testGetSingleValueWithFallbackWithDefaultValueSameGlobalRegion() thr global.setFlag(flag, State.ALLOW); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); + assertThat(result.queryValue(null, flag), is(State.ALLOW)); } @Test - public void testGetSingleValueWithFallbackWithTrueDefaultValueDenyGlobalRegion() throws Exception { + public void testQueryValueGlobalRegionDeny() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); StateFlag flag = new StateFlag("test", true); @@ -772,34 +1073,11 @@ public void testGetSingleValueWithFallbackWithTrueDefaultValueDenyGlobalRegion() global.setFlag(flag, State.DENY); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is((State) null)); + assertThat(result.queryValue(null, flag), is(State.DENY)); } @Test - public void testGetSingleValueWithFallbackWithFalseDefaultValueAllowGlobalRegion() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag = new StateFlag("test", false); - - ProtectedRegion global = mock.global(); - global.setFlag(flag, State.ALLOW); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); - } - - @Test - public void testGetSingleValueWithFallbackWithStringFlag() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StringFlag flag = new StringFlag("test"); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is((String) null)); - } - - @Test - public void testGetSingleValueWithFallbackWithStringFlagEmptyGlobalRegion() throws Exception { + public void testQueryValueStringFlagWithGlobalRegion() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); StringFlag flag = new StringFlag("test"); @@ -807,145 +1085,62 @@ public void testGetSingleValueWithFallbackWithStringFlagEmptyGlobalRegion() thro ProtectedRegion global = mock.global(); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is((String) null)); + assertThat(result.queryValue(null, flag), is((String) null)); } @Test - public void testGetSingleValueWithFallbackWithStringFlagGlobalRegionValueSet() throws Exception { + public void testQueryValueStringFlagWithGlobalRegionValueSet() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); StringFlag flag = new StringFlag("test"); ProtectedRegion global = mock.global(); - global.setFlag(flag, "hello there"); + global.setFlag(flag, "hello"); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is("hello there")); + assertThat(result.queryValue(null, flag), is("hello")); } @Test - public void testGetSingleValueWithFallbackWithFalseDefaultValueAndEmptyRegion() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag = new StateFlag("test", false); - - ProtectedRegion region = mock.add(0); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is((State) null)); - } - - @Test - public void testGetSingleValueWithFallbackWithTrueDefaultValueAndEmptyRegion() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag = new StateFlag("test", true); - - ProtectedRegion region = mock.add(0); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); - } - - @Test - public void testGetSingleValueWithFallbackWithTrueDefaultValueAndRegionDeny() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag = new StateFlag("test", true); - - ProtectedRegion region = mock.add(0); - region.setFlag(flag, State.DENY); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is(State.DENY)); - } - - @Test - public void testGetSingleValueWithFallbackWithTrueDefaultValueAndRegionAllow() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag = new StateFlag("test", true); - - ProtectedRegion region = mock.add(0); - region.setFlag(flag, State.ALLOW); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); - } - - @Test - public void testGetSingleValueWithFallbackWithFalseDefaultValueAndRegionAllow() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag = new StateFlag("test", false); - - ProtectedRegion region = mock.add(0); - region.setFlag(flag, State.ALLOW); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is(State.ALLOW)); - } - - @Test - public void testGetSingleValueWithFallbackWithFalseDefaultValueAndRegionDeny() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StateFlag flag = new StateFlag("test", false); - - ProtectedRegion region = mock.add(0); - region.setFlag(flag, State.DENY); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is(State.DENY)); - } - - @Test - public void testGetSingleValueWithFallbackWithStringFlagAndEmptyRegion() throws Exception { + public void testQueryValueStringFlagWithGlobalRegionAndRegion() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); StringFlag flag = new StringFlag("test"); + ProtectedRegion global = mock.global(); + global.setFlag(flag, "hello"); + ProtectedRegion region = mock.add(0); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is((String) null)); + assertThat(result.queryValue(null, flag), is("hello")); } @Test - public void testGetSingleValueWithFallbackWithStringFlagAndRegionValueSet() throws Exception { + public void testQueryValueStringFlagWithGlobalRegionAndRegionOverride() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); StringFlag flag = new StringFlag("test"); + ProtectedRegion global = mock.global(); + global.setFlag(flag, "hello"); + ProtectedRegion region = mock.add(0); - region.setFlag(flag, "beep beep"); + region.setFlag(flag, "beep"); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is("beep beep")); + assertThat(result.queryValue(null, flag), is("beep")); } @Test - public void testGetSingleValueWithFallbackWithStringFlagAndRegionValueSetAndPriority() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - StringFlag flag = new StringFlag("test"); - - ProtectedRegion region = mock.add(10); - region.setFlag(flag, "ello there"); - - region = mock.add(0); - region.setFlag(flag, "beep beep"); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is("ello there")); - } - - @Test - public void testGetSingleValueWithFallbackWithStringFlagAndRegionValueSetAndInheritanceAndRegionGroup() throws Exception { + public void testQueryValueStringFlagWithEverything() throws Exception { MockApplicableRegionSet mock = new MockApplicableRegionSet(); StringFlag flag = new StringFlag("test", RegionGroup.ALL); + ProtectedRegion global = mock.global(); + global.setFlag(flag, "hello"); + ProtectedRegion parent = mock.add(0); parent.setFlag(flag, "ello there"); @@ -960,93 +1155,16 @@ public void testGetSingleValueWithFallbackWithStringFlagAndRegionValueSetAndInhe region.getMembers().addPlayer(member); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValueWithFallback(null, flag), is("ello there")); - assertThat(result.getSingleValueWithFallback(nonMember, flag), is("ello there")); - assertThat(result.getSingleValueWithFallback(member, flag), is("beep beep")); + assertThat(result.queryValue(null, flag), is("ello there")); + assertThat(result.queryValue(nonMember, flag), is("ello there")); + assertThat(result.queryValue(member, flag), is("beep beep")); } // ======================================================================== // ======================================================================== @Test - public void testGetSingleValueSingleRegion() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - ProtectedRegion region = mock.add(0); - region.setFlag(DefaultFlag.PVP, State.DENY); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValue(null, DefaultFlag.LIGHTER), is((State) null)); - assertThat(result.getSingleValue(null, DefaultFlag.PVP), is(State.DENY)); - } - - @Test - public void testGetSingleValueDenyOverridesAllow() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - ProtectedRegion region = mock.add(0); - region.setFlag(DefaultFlag.PVP, State.DENY); - - region = mock.add(0); - region.setFlag(DefaultFlag.PVP, State.ALLOW); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValue(null, DefaultFlag.LIGHTER), is((State) null)); - assertThat(result.getSingleValue(null, DefaultFlag.PVP), is(State.DENY)); - } - - @Test - public void testGetSingleValueAllowOverridesNone() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - ProtectedRegion region = mock.add(0); - - region = mock.add(0); - region.setFlag(DefaultFlag.PVP, State.ALLOW); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValue(null, DefaultFlag.LIGHTER), is((State) null)); - assertThat(result.getSingleValue(null, DefaultFlag.PVP), is(State.ALLOW)); - } - - @Test - public void testGetSingleValueMultipleFlags() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - ProtectedRegion region = mock.add(0); - region.setFlag(DefaultFlag.LIGHTER, State.DENY); - region.setFlag(DefaultFlag.PVP, State.ALLOW); - - region = mock.add(0); - region.setFlag(DefaultFlag.PVP, State.DENY); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValue(null, DefaultFlag.LIGHTER), is(State.DENY)); - assertThat(result.getSingleValue(null, DefaultFlag.PVP), is(State.DENY)); - assertThat(result.getSingleValue(null, DefaultFlag.LAVA_FIRE), is((State) null)); - } - - @Test - public void testGetSingleValueStringFlag() throws Exception { - MockApplicableRegionSet mock = new MockApplicableRegionSet(); - - ProtectedRegion region = mock.add(0); - region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test1"); - - region = mock.add(0); - region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); - - FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(result.getSingleValue(null, DefaultFlag.FAREWELL_MESSAGE), isOneOf("test1", "test2")); - assertThat(result.getSingleValue(null, DefaultFlag.GREET_MESSAGE), is((String) null)); - assertThat(result.getSingleValue(null, DefaultFlag.LAVA_FIRE), is((State) null)); - } - - // ======================================================================== - // ======================================================================== - - @Test - public void testGetValuesTwoWithSamePriority() throws Exception { + public void testQueryAllValuesTwoWithSamePriority() throws Exception { // ==================================================================== // Two regions with the same priority // ==================================================================== @@ -1060,12 +1178,12 @@ public void testGetValuesTwoWithSamePriority() throws Exception { region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1", "test2"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1", "test2"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); } @Test - public void testGetValuesTwoWithDuplicateFlagValues() throws Exception { + public void testQueryAllValuesTwoWithDuplicateFlagValues() throws Exception { // ==================================================================== // Two regions with duplicate values // ==================================================================== @@ -1079,12 +1197,12 @@ public void testGetValuesTwoWithDuplicateFlagValues() throws Exception { region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test"); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test", "test"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test", "test"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); } @Test - public void testGetValuesWithHigherPriority() throws Exception { + public void testQueryAllValuesWithHigherPriority() throws Exception { // ==================================================================== // One of the regions has a higher priority (should override) // ==================================================================== @@ -1098,12 +1216,12 @@ public void testGetValuesWithHigherPriority() throws Exception { region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); } @Test - public void testGetValuesWithTwoElevatedPriorities() throws Exception { + public void testQueryAllValuesWithTwoElevatedPriorities() throws Exception { // ==================================================================== // Two regions with the same elevated priority // ==================================================================== @@ -1120,12 +1238,12 @@ public void testGetValuesWithTwoElevatedPriorities() throws Exception { region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1", "test3"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1", "test3"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); } @Test - public void testGetValuesParentChildWithSamePriority() throws Exception { + public void testQueryAllValuesParentChildWithSamePriority() throws Exception { // ==================================================================== // Child region and parent region with the same priority // ==================================================================== @@ -1143,12 +1261,12 @@ public void testGetValuesParentChildWithSamePriority() throws Exception { region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); } @Test - public void testGetValuesParentWithHigherPriority() throws Exception { + public void testQueryAllValuesParentWithHigherPriority() throws Exception { // ==================================================================== // Parent region with a higher priority than the child // ==================================================================== @@ -1166,12 +1284,12 @@ public void testGetValuesParentWithHigherPriority() throws Exception { region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test3"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test3"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); } @Test - public void testGetValuesParentWithLowerPriority() throws Exception { + public void testQueryAllValuesParentWithLowerPriority() throws Exception { // ==================================================================== // Parent region with a lower priority than the child // ==================================================================== @@ -1189,12 +1307,12 @@ public void testGetValuesParentWithLowerPriority() throws Exception { region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); } @Test - public void testGetValuesThirdRegionWithHigherPriorityThanParentChild() throws Exception { + public void testQueryAllValuesThirdRegionWithHigherPriorityThanParentChild() throws Exception { // ==================================================================== // Third region with higher priority than parent and child // ==================================================================== @@ -1212,12 +1330,12 @@ public void testGetValuesThirdRegionWithHigherPriorityThanParentChild() throws E region.setFlag(DefaultFlag.FAREWELL_MESSAGE, "test2"); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test2"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test2"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); } @Test - public void testGetValuesParentsAndInheritance() throws Exception { + public void testQueryAllValuesParentsAndInheritance() throws Exception { // ==================================================================== // Multiple regions with parents, one region using flag from parent // ==================================================================== @@ -1238,12 +1356,12 @@ public void testGetValuesParentsAndInheritance() throws Exception { region.setParent(parent2); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test2", "test3"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test2", "test3"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); } @Test - public void testGetValuesParentsAndInheritanceHighPriorityAndNoFlag() throws Exception { + public void testQueryAllValuesParentsAndInheritanceHighPriorityAndNoFlag() throws Exception { // ==================================================================== // Multiple regions with parents, one region with high priority but no flag // ==================================================================== @@ -1263,12 +1381,12 @@ public void testGetValuesParentsAndInheritanceHighPriorityAndNoFlag() throws Exc region = mock.add(30); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test2"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test2"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); } @Test - public void testGetValuesParentWithSamePriorityAsHighest() throws Exception { + public void testQueryAllValuesParentWithSamePriorityAsHighest() throws Exception { // ==================================================================== // As before, except a parent region has the same priority as the previous highest // ==================================================================== @@ -1288,8 +1406,31 @@ public void testGetValuesParentWithSamePriorityAsHighest() throws Exception { region = mock.add(30); FlagValueCalculator result = mock.getFlagCalculator(); - assertThat(copyOf(result.getValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); - assertThat(result.getValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + assertThat(copyOf(result.queryAllValues(null, DefaultFlag.FAREWELL_MESSAGE)), equalTo(of("test1"))); + assertThat(result.queryAllValues(null, DefaultFlag.GREET_MESSAGE), is(Matchers.empty())); + } + + // ======================================================================== + // ======================================================================== + + @Test + public void testGetEffectivePriority() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.add(30); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getPriority(region), is(30)); + } + + @Test + public void testGetEffectivePriorityGlobalRegion() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion region = mock.global(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getPriority(region), is(Integer.MIN_VALUE)); } // ======================================================================== From 91e34601ecfcfd47528b90f5abfa999583a66c41 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 16 Aug 2014 13:42:14 -0700 Subject: [PATCH 059/133] Add supervisor/task library in. --- pom.xml | 14 -- .../worldguard/bukkit/WorldGuardPlugin.java | 8 +- .../bukkit/commands/AsyncCommandHelper.java | 2 +- .../bukkit/commands/WorldGuardCommands.java | 5 +- .../managers/index/ChunkHashTable.java | 2 +- .../util/concurrent/EvenMoreExecutors.java | 54 ++++++ .../worldguard/util/task/AbstractTask.java | 74 +++++++ .../util/task/FutureForwardingTask.java | 121 ++++++++++++ .../util/task/SimpleSupervisor.java | 62 ++++++ .../worldguard/util/task/Supervisor.java | 44 +++++ .../com/sk89q/worldguard/util/task/Task.java | 97 ++++++++++ .../util/task/TaskStateComparator.java | 43 ++++ .../util/task/progress/Progress.java | 183 ++++++++++++++++++ .../util/task/progress/ProgressIterator.java | 100 ++++++++++ .../task/progress/ProgressObservable.java | 34 ++++ 15 files changed, 820 insertions(+), 23 deletions(-) create mode 100644 src/main/java/com/sk89q/worldguard/util/concurrent/EvenMoreExecutors.java create mode 100644 src/main/java/com/sk89q/worldguard/util/task/AbstractTask.java create mode 100644 src/main/java/com/sk89q/worldguard/util/task/FutureForwardingTask.java create mode 100644 src/main/java/com/sk89q/worldguard/util/task/SimpleSupervisor.java create mode 100644 src/main/java/com/sk89q/worldguard/util/task/Supervisor.java create mode 100644 src/main/java/com/sk89q/worldguard/util/task/Task.java create mode 100644 src/main/java/com/sk89q/worldguard/util/task/TaskStateComparator.java create mode 100644 src/main/java/com/sk89q/worldguard/util/task/progress/Progress.java create mode 100644 src/main/java/com/sk89q/worldguard/util/task/progress/ProgressIterator.java create mode 100644 src/main/java/com/sk89q/worldguard/util/task/progress/ProgressObservable.java diff --git a/pom.xml b/pom.xml index c42e9773..f935799b 100644 --- a/pom.xml +++ b/pom.xml @@ -199,15 +199,6 @@ true - - com.sk89q - odeum - 0.2.0-SNAPSHOT - compile - jar - true - - junit junit @@ -400,7 +391,6 @@ com.jolbox:bonecp org.flywaydb:flyway-core com.sk89q:squirrelid - com.sk89q:odeum @@ -416,10 +406,6 @@ com.sk89q.squirrelid com.sk89q.worldguard.util.profile - - com.sk89q.odeum - com.sk89q.worldguard.internal.odeum - diff --git a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 5b0ba8ea..c2b60d85 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -30,10 +30,10 @@ import com.sk89q.minecraft.util.commands.MissingNestedCommandException; import com.sk89q.minecraft.util.commands.SimpleInjector; import com.sk89q.minecraft.util.commands.WrappedCommandException; -import com.sk89q.odeum.concurrent.EvenMoreExecutors; -import com.sk89q.odeum.task.SimpleSupervisor; -import com.sk89q.odeum.task.Supervisor; -import com.sk89q.odeum.task.Task; +import com.sk89q.worldguard.util.concurrent.EvenMoreExecutors; +import com.sk89q.worldguard.util.task.SimpleSupervisor; +import com.sk89q.worldguard.util.task.Supervisor; +import com.sk89q.worldguard.util.task.Task; import com.sk89q.squirrelid.cache.HashMapCache; import com.sk89q.squirrelid.cache.ProfileCache; import com.sk89q.squirrelid.cache.SQLiteCache; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java index a12862f3..dabc9fde 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/AsyncCommandHelper.java @@ -21,7 +21,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.sk89q.odeum.task.FutureForwardingTask; +import com.sk89q.worldguard.util.task.FutureForwardingTask; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import org.bukkit.World; import org.bukkit.command.CommandSender; diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java index 9812e0eb..44325133 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/WorldGuardCommands.java @@ -23,14 +23,13 @@ import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.odeum.task.Task; -import com.sk89q.odeum.task.TaskStateComparator; +import com.sk89q.worldguard.util.task.Task; +import com.sk89q.worldguard.util.task.TaskStateComparator; import com.sk89q.worldguard.bukkit.util.LoggerToChatHandler; import com.sk89q.worldguard.bukkit.util.ReportWriter; import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.util.PastebinPoster; import com.sk89q.worldguard.util.PastebinPoster.PasteCallback; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; diff --git a/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java b/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java index 7fcb15b2..a6718291 100644 --- a/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java +++ b/src/main/java/com/sk89q/worldguard/protection/managers/index/ChunkHashTable.java @@ -23,7 +23,7 @@ import com.google.common.base.Supplier; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; -import com.sk89q.odeum.concurrent.EvenMoreExecutors; +import com.sk89q.worldguard.util.concurrent.EvenMoreExecutors; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector2D; import com.sk89q.worldguard.protection.managers.RegionDifference; diff --git a/src/main/java/com/sk89q/worldguard/util/concurrent/EvenMoreExecutors.java b/src/main/java/com/sk89q/worldguard/util/concurrent/EvenMoreExecutors.java new file mode 100644 index 00000000..6b29fdd7 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/concurrent/EvenMoreExecutors.java @@ -0,0 +1,54 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * Provides additional executors. + */ +public final class EvenMoreExecutors { + + private EvenMoreExecutors() { + } + + /** + * Creates a thread pool that creates new threads as needed up to + * a maximum number of threads, but will reuse previously constructed + * threads when they are available. + * + * @param minThreads the minimum number of threads to have at a given time + * @param maxThreads the maximum number of threads to have at a given time + * @param queueSize the size of the queue before new submissions are rejected + * @return the newly created thread pool + */ + public static ExecutorService newBoundedCachedThreadPool(int minThreads, int maxThreads, int queueSize) { + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( + minThreads, maxThreads, + 60L, TimeUnit.SECONDS, + new LinkedBlockingDeque(queueSize)); + threadPoolExecutor.allowCoreThreadTimeOut(true); + return threadPoolExecutor; + } + +} diff --git a/src/main/java/com/sk89q/worldguard/util/task/AbstractTask.java b/src/main/java/com/sk89q/worldguard/util/task/AbstractTask.java new file mode 100644 index 00000000..6e86edd3 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/task/AbstractTask.java @@ -0,0 +1,74 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.task; + +import com.google.common.util.concurrent.AbstractFuture; + +import javax.annotation.Nullable; +import java.util.Date; +import java.util.UUID; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An abstract task that stores a name and owner. + * + * @param the type returned + */ +public abstract class AbstractTask extends AbstractFuture implements Task { + + private final UUID uniqueId = UUID.randomUUID(); + private final String name; + private final Object owner; + private final Date creationDate = new Date(); + + /** + * Create a new instance. + * + * @param name the name + * @param owner the owner + */ + protected AbstractTask(String name, @Nullable Object owner) { + checkNotNull(name); + this.name = name; + this.owner = owner; + } + + @Override + public UUID getUniqueId() { + return uniqueId; + } + + @Override + public String getName() { + return name; + } + + @Nullable + @Override + public Object getOwner() { + return owner; + } + + @Override + public Date getCreationDate() { + return creationDate; + } +} diff --git a/src/main/java/com/sk89q/worldguard/util/task/FutureForwardingTask.java b/src/main/java/com/sk89q/worldguard/util/task/FutureForwardingTask.java new file mode 100644 index 00000000..5cd66049 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/task/FutureForwardingTask.java @@ -0,0 +1,121 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.task; + +import com.google.common.util.concurrent.ListenableFuture; +import com.sk89q.worldguard.util.task.progress.Progress; + +import javax.annotation.Nullable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A task that wraps a {@code ListenableFuture}. + * + *

{@link State#SCHEDULED} is never returned because it is not possible + * to test whether the future has "started," so {@link State#RUNNING} is + * returned in its place.

+ * + *

Use {@link #create(ListenableFuture, String, Object)} to create a new + * instance.

+ * + * @param the type returned + */ +public class FutureForwardingTask extends AbstractTask { + + private final ListenableFuture future; + + private FutureForwardingTask(ListenableFuture future, String name, @Nullable Object owner) { + super(name, owner); + checkNotNull(future); + this.future = future; + } + + @Override + public void addListener(Runnable listener, Executor executor) { + future.addListener(listener, executor); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return future.cancel(mayInterruptIfRunning); + } + + @Override + public boolean isCancelled() { + return future.isCancelled(); + } + + @Override + public boolean isDone() { + return future.isDone(); + } + + @Override + public V get() throws InterruptedException, ExecutionException { + return future.get(); + } + + @Override + public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return future.get(timeout, unit); + } + + @Override + public State getState() { + if (isCancelled()) { + return State.CANCELLED; + } else if (isDone()) { + try { + get(); + return State.SUCCEEDED; + } catch (InterruptedException e) { + return State.CANCELLED; + } catch (ExecutionException e) { + return State.FAILED; + } + } else { + return State.RUNNING; + } + } + + @Override + public Progress getProgress() { + return Progress.indeterminate(); + } + + /** + * Create a new instance. + * + * @param future the future + * @param name the name of the task + * @param owner the owner of the task, or {@code null} + * @param the type returned by the future + * @return a new instance + */ + public static com.sk89q.worldguard.util.task.FutureForwardingTask create(ListenableFuture future, String name, @Nullable Object owner) { + return new com.sk89q.worldguard.util.task.FutureForwardingTask(future, name, owner); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/util/task/SimpleSupervisor.java b/src/main/java/com/sk89q/worldguard/util/task/SimpleSupervisor.java new file mode 100644 index 00000000..0757077c --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/task/SimpleSupervisor.java @@ -0,0 +1,62 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.task; + +import com.google.common.util.concurrent.MoreExecutors; + +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An implementation of a {@code Supervisor}. + */ +public class SimpleSupervisor implements Supervisor { + + private final List> monitored = new ArrayList>(); + private final Object lock = new Object(); + + @Override + public List> getTasks() { + synchronized (lock) { + return new ArrayList>(monitored); + } + } + + @Override + public void monitor(final Task task) { + checkNotNull(task); + + synchronized (lock) { + monitored.add(task); + } + + task.addListener(new Runnable() { + @Override + public void run() { + synchronized (lock) { + monitored.remove(task); + } + } + }, MoreExecutors.sameThreadExecutor()); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/util/task/Supervisor.java b/src/main/java/com/sk89q/worldguard/util/task/Supervisor.java new file mode 100644 index 00000000..b357ba37 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/task/Supervisor.java @@ -0,0 +1,44 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.task; + +import java.util.List; + +/** + * Manages running tasks and informs users of their progress, but does not + * execute the task. + */ +public interface Supervisor { + + /** + * Get a list of running or queued tasks. + * + * @return a list of tasks + */ + List> getTasks(); + + /** + * Monitor the given task. + * + * @param task the task + */ + void monitor(Task task); + +} diff --git a/src/main/java/com/sk89q/worldguard/util/task/Task.java b/src/main/java/com/sk89q/worldguard/util/task/Task.java new file mode 100644 index 00000000..b9d03b01 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/task/Task.java @@ -0,0 +1,97 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.task; + +import com.google.common.util.concurrent.ListenableFuture; +import com.sk89q.worldguard.util.task.progress.ProgressObservable; + +import javax.annotation.Nullable; +import java.util.Date; +import java.util.UUID; + +/** + * A task is a job that can be scheduled, run, or cancelled. Tasks can report + * on their own status. Tasks have owners. + */ +public interface Task extends ListenableFuture, ProgressObservable { + + /** + * Get the unique ID of this task. + * + * @return this task's unique ID + */ + UUID getUniqueId(); + + /** + * Get the name of the task so it can be printed to the user. + * + * @return the name of the task + */ + String getName(); + + /** + * Get the owner of the task. + * + * @return an owner object, if one is known or valid, otherwise {@code null} + */ + @Nullable + Object getOwner(); + + /** + * Get the state of the task. + * + * @return the state of the task + */ + State getState(); + + /** + * Get the time at which the task was created. + * + * @return a date + */ + Date getCreationDate(); + + /** + * Represents the state of a task. + */ + public enum State { + /** + * The task has been scheduled to run but is not running yet. + */ + SCHEDULED, + /** + * The task has been cancelled and may be stopped or will stop. + */ + CANCELLED, + /** + * The task is currently running. + */ + RUNNING, + /** + * The task has failed. + */ + FAILED, + /** + * The task has succeeded. + */ + SUCCEEDED + } + +} diff --git a/src/main/java/com/sk89q/worldguard/util/task/TaskStateComparator.java b/src/main/java/com/sk89q/worldguard/util/task/TaskStateComparator.java new file mode 100644 index 00000000..1804cc35 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/task/TaskStateComparator.java @@ -0,0 +1,43 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.task; + +import java.util.Comparator; + +/** + * Compares task states according to the order of the {@link com.sk89q.worldguard.util.task.Task.State} + * enumeration. + */ +public class TaskStateComparator implements Comparator> { + + @Override + public int compare(com.sk89q.worldguard.util.task.Task o1, Task o2) { + int ordinal1 = o1.getState().ordinal(); + int ordinal2 = o2.getState().ordinal(); + if (ordinal1 < ordinal2) { + return -1; + } else if (ordinal1 > ordinal2) { + return 1; + } else { + return 0; + } + } + +} diff --git a/src/main/java/com/sk89q/worldguard/util/task/progress/Progress.java b/src/main/java/com/sk89q/worldguard/util/task/progress/Progress.java new file mode 100644 index 00000000..31bbe010 --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/task/progress/Progress.java @@ -0,0 +1,183 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.task.progress; + +import java.util.Arrays; +import java.util.Collection; + +/** + * A progress object describes the progress of an operation, specifying + * either a percentage of completion or a status of indeterminacy. + * + *

Progress objects are immutable.

+ * + *

To create a new instance, use one of the static constructors + * on this class.

+ */ +public abstract class Progress { + + /** + * Create a new instance. + */ + private Progress() { + } + + /** + * Return whether the current progress is indeterminate. + * + * @return true if indeterminate + */ + public abstract boolean isIndeterminate(); + + /** + * Get the progress percentage. + * + *

If {@link #isIndeterminate()} returns {@code true}, the behavior + * of this method is undefined.

+ * + * @return a number in the range [0, 1] + */ + public abstract double getProgress(); + + /** + * Get a static progress object that is indeterminate. + * + * @return a progress object + */ + public static com.sk89q.worldguard.util.task.progress.Progress indeterminate() { + return INDETERMINATE; + } + + /** + * Get a static progress object that is complete. + * + * @return a progress object + */ + public static com.sk89q.worldguard.util.task.progress.Progress completed() { + return COMPLETED; + } + + /** + * Create a new progress object with the given percentage. + * + * @param value the percentage, which will be clamped to [0, 1] + * @return a progress object + */ + public static com.sk89q.worldguard.util.task.progress.Progress of(double value) { + if (value < 0) { + value = 0; + } else if (value > 1) { + value = 1; + } + + final double finalValue = value; + return new com.sk89q.worldguard.util.task.progress.Progress() { + @Override + public boolean isIndeterminate() { + return false; + } + + @Override + public double getProgress() { + return finalValue; + } + }; + } + + /** + * Create a new progress object with progress split equally between the + * given progress objects. + * + * @param objects an array of progress objects + * @return a new progress value + */ + public static com.sk89q.worldguard.util.task.progress.Progress split(com.sk89q.worldguard.util.task.progress.Progress... objects) { + return split(Arrays.asList(objects)); + } + + /** + * Create a new progress object with progress split equally between the + * given progress objects. + * + * @param progress a collection of progress objects + * @return a new progress value + */ + public static com.sk89q.worldguard.util.task.progress.Progress split(Collection progress) { + int count = 0; + double total = 0; + + for (com.sk89q.worldguard.util.task.progress.Progress p : progress) { + if (p.isIndeterminate()) { + return indeterminate(); + } + total += p.getProgress(); + } + + return of(total / count); + } + + /** + * Create a new progress object with progress split equally between the + * given {@link com.sk89q.worldguard.util.task.progress.ProgressObservable}s. + * + * @param observables an array of observables + * @return a new progress value + */ + public static com.sk89q.worldguard.util.task.progress.Progress splitObservables(com.sk89q.worldguard.util.task.progress.ProgressObservable... observables) { + return splitObservables(Arrays.asList(observables)); + } + + /** + * Create a new progress object with progress split equally between the + * given {@link com.sk89q.worldguard.util.task.progress.ProgressObservable}s. + * + * @param observables a collection of observables + * @return a new progress value + */ + public static com.sk89q.worldguard.util.task.progress.Progress splitObservables(Collection observables) { + int count = 0; + double total = 0; + + for (ProgressObservable observable : observables) { + com.sk89q.worldguard.util.task.progress.Progress p = observable.getProgress(); + if (p.isIndeterminate()) { + return indeterminate(); + } + total += p.getProgress(); + } + + return of(total / count); + } + + private static final com.sk89q.worldguard.util.task.progress.Progress COMPLETED = of(1); + + private static final com.sk89q.worldguard.util.task.progress.Progress INDETERMINATE = new com.sk89q.worldguard.util.task.progress.Progress() { + @Override + public boolean isIndeterminate() { + return true; + } + + @Override + public double getProgress() { + return 0; + } + }; + +} diff --git a/src/main/java/com/sk89q/worldguard/util/task/progress/ProgressIterator.java b/src/main/java/com/sk89q/worldguard/util/task/progress/ProgressIterator.java new file mode 100644 index 00000000..0500afcc --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/task/progress/ProgressIterator.java @@ -0,0 +1,100 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.task.progress; + +import java.util.Iterator; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An iterator that keeps track of how many entries have been visited and + * calculates a "percent completed" using a provided total count. + * + *

The returned progress percentage will always be between 0 or 1 + * (inclusive). If the iterator returns more entries than the total count, + * then 100% will be returned for the progress.

+ * + * @param the type + */ +public class ProgressIterator implements Iterator, ProgressObservable { + + private final Iterator iterator; + private final int count; + private int visited = 0; + + /** + * Create a new instance. + * + * @param iterator the iterator + * @param count the count + */ + private ProgressIterator(Iterator iterator, int count) { + checkNotNull(iterator); + this.iterator = iterator; + this.count = count; + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public V next() { + V value = iterator.next(); + visited++; + return value; + } + + @Override + public void remove() { + iterator.remove(); + } + + @Override + public Progress getProgress() { + return Progress.of(count > 0 ? Math.min(1, Math.max(0, (visited / (double) count))) : 1); + } + + /** + * Create a new instance. + * + * @param iterator the iterator + * @param count the number of objects + * @param the type + * @return an instance + */ + public static com.sk89q.worldguard.util.task.progress.ProgressIterator create(Iterator iterator, int count) { + return new com.sk89q.worldguard.util.task.progress.ProgressIterator(iterator, count); + } + + /** + * Create a new instance from a list. + * + * @param list a list + * @param the type + * @return an instance + */ + public static com.sk89q.worldguard.util.task.progress.ProgressIterator create(List list) { + return create(list.iterator(), list.size()); + } + +} diff --git a/src/main/java/com/sk89q/worldguard/util/task/progress/ProgressObservable.java b/src/main/java/com/sk89q/worldguard/util/task/progress/ProgressObservable.java new file mode 100644 index 00000000..b43dc44d --- /dev/null +++ b/src/main/java/com/sk89q/worldguard/util/task/progress/ProgressObservable.java @@ -0,0 +1,34 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.util.task.progress; + +/** + * An object that is able to report on its progress. + */ +public interface ProgressObservable { + + /** + * Get the current percentage of completion. + * + * @return a progress object + */ + Progress getProgress(); + +} From bfb3f9e84015c5845305ea473fe0292d93cdd605 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 16 Aug 2014 13:45:16 -0700 Subject: [PATCH 060/133] Change state flag defaults to true. --- .../com/sk89q/worldguard/protection/flags/DefaultFlag.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/flags/DefaultFlag.java b/src/main/java/com/sk89q/worldguard/protection/flags/DefaultFlag.java index 2b083949..c936150a 100644 --- a/src/main/java/com/sk89q/worldguard/protection/flags/DefaultFlag.java +++ b/src/main/java/com/sk89q/worldguard/protection/flags/DefaultFlag.java @@ -44,12 +44,12 @@ public final class DefaultFlag { public static final StateFlag FIRE_SPREAD = new StateFlag("fire-spread", true); public static final StateFlag LAVA_FIRE = new StateFlag("lava-fire", true); public static final StateFlag LIGHTNING = new StateFlag("lightning", true); - public static final StateFlag CHEST_ACCESS = new StateFlag("chest-access", false); + public static final StateFlag CHEST_ACCESS = new StateFlag("chest-access", true); public static final StateFlag WATER_FLOW = new StateFlag("water-flow", true); public static final StateFlag LAVA_FLOW = new StateFlag("lava-flow", true); public static final StateFlag USE = new StateFlag("use", true); - public static final StateFlag PLACE_VEHICLE = new StateFlag("vehicle-place", false); - public static final StateFlag DESTROY_VEHICLE = new StateFlag("vehicle-destroy", false); + public static final StateFlag PLACE_VEHICLE = new StateFlag("vehicle-place", true); + public static final StateFlag DESTROY_VEHICLE = new StateFlag("vehicle-destroy", true); public static final StateFlag PISTONS = new StateFlag("pistons", true); public static final StateFlag SNOW_FALL = new StateFlag("snow-fall", true); public static final StateFlag SNOW_MELT = new StateFlag("snow-melt", true); From a35aaf96595f3d2075286c259f9147d37eb90359 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 16 Aug 2014 13:54:04 -0700 Subject: [PATCH 061/133] Force __global__ BUILD flag to NONE. This is due to the legacy reason of the global region having not been previously processed as a regular region. --- .../protection/FlagValueCalculator.java | 20 +++++++---- .../protection/FlagValueCalculatorTest.java | 34 +++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java b/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java index 3455ab9d..2514f031 100644 --- a/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java +++ b/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java @@ -407,14 +407,20 @@ public int getPriority(final ProtectedRegion region) { */ @SuppressWarnings("unchecked") public V getEffectiveFlag(final ProtectedRegion region, Flag flag, @Nullable LocalPlayer player) { - // The global region normally does not prevent building so - // PASSTHROUGH has to be ALLOW, except when people use the global - // region as a whitelist - if (region == globalRegion && flag == DefaultFlag.PASSTHROUGH) { - if (region.hasMembersOrOwners()) { + if (region == globalRegion) { + if (flag == DefaultFlag.PASSTHROUGH) { + // Has members/owners -> the global region acts like + // a regular region without PASSTHROUGH + if (region.hasMembersOrOwners()) { + return null; + } else { + return (V) State.ALLOW; + } + + } else if (flag == DefaultFlag.BUILD) { + // Legacy behavior -> we can't let people change BUILD on + // the global region return null; - } else { - return (V) State.ALLOW; } } diff --git a/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java b/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java index 2837cf00..9a116086 100644 --- a/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java @@ -1959,4 +1959,38 @@ public void testGetEffectiveFlagInheritanceAndDifferingGroupsMemberOnParentFlagO assertThat(result.getEffectiveFlag(region, DefaultFlag.FAREWELL_MESSAGE, null), equalTo("everyone")); assertThat(result.getEffectiveFlag(region, DefaultFlag.GREET_MESSAGE, null), equalTo(null)); } + + @Test + public void testGetEffectiveFlagGlobalRegionBuild() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion global = mock.global(); + + FlagValueCalculator result = mock.getFlagCalculator(); + assertThat(result.getEffectiveFlag(global, DefaultFlag.BUILD, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagGlobalRegionBuildDeny() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion global = mock.global(); + global.setFlag(DefaultFlag.BUILD, State.DENY); + + FlagValueCalculator result = mock.getFlagCalculator(); + // Cannot let users override BUILD on GLOBAL + assertThat(result.getEffectiveFlag(global, DefaultFlag.BUILD, null), equalTo(null)); + } + + @Test + public void testGetEffectiveFlagGlobalRegionBuildAllow() throws Exception { + MockApplicableRegionSet mock = new MockApplicableRegionSet(); + + ProtectedRegion global = mock.global(); + global.setFlag(DefaultFlag.BUILD, State.ALLOW); + + FlagValueCalculator result = mock.getFlagCalculator(); + // Cannot let users override BUILD on GLOBAL + assertThat(result.getEffectiveFlag(global, DefaultFlag.BUILD, null), equalTo(null)); + } } \ No newline at end of file From 45978494662634d007e5a48a73b73a8e8c2f7124 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 16 Aug 2014 13:55:54 -0700 Subject: [PATCH 062/133] Allow __global__ BUILD to be set to DENY. --- .../com/sk89q/worldguard/protection/FlagValueCalculator.java | 3 ++- .../sk89q/worldguard/protection/FlagValueCalculatorTest.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java b/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java index 2514f031..7f3bd953 100644 --- a/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java +++ b/src/main/java/com/sk89q/worldguard/protection/FlagValueCalculator.java @@ -420,7 +420,8 @@ public V getEffectiveFlag(final ProtectedRegion region, Flag flag, @Nulla } else if (flag == DefaultFlag.BUILD) { // Legacy behavior -> we can't let people change BUILD on // the global region - return null; + State value = region.getFlag(DefaultFlag.BUILD); + return value != State.ALLOW ? (V) value : null; } } diff --git a/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java b/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java index 9a116086..309901de 100644 --- a/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/FlagValueCalculatorTest.java @@ -1979,7 +1979,7 @@ public void testGetEffectiveFlagGlobalRegionBuildDeny() throws Exception { FlagValueCalculator result = mock.getFlagCalculator(); // Cannot let users override BUILD on GLOBAL - assertThat(result.getEffectiveFlag(global, DefaultFlag.BUILD, null), equalTo(null)); + assertThat(result.getEffectiveFlag(global, DefaultFlag.BUILD, null), equalTo(State.DENY)); } @Test From 5a63115fb2d6c3ad72b1d606fc46bddc92f79e73 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 16 Aug 2014 15:07:22 -0700 Subject: [PATCH 063/133] Don't make membership always override the BUILD flag anymore. --- .../sk89q/worldguard/protection/ApplicableRegionSetTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/sk89q/worldguard/protection/ApplicableRegionSetTest.java b/src/test/java/com/sk89q/worldguard/protection/ApplicableRegionSetTest.java index 04253d80..24b0678c 100644 --- a/src/test/java/com/sk89q/worldguard/protection/ApplicableRegionSetTest.java +++ b/src/test/java/com/sk89q/worldguard/protection/ApplicableRegionSetTest.java @@ -300,7 +300,7 @@ public void testPriorityDisjointBuildDenyFlagAndMembership() { region.getOwners().addPlayer(member); ApplicableRegionSet set = mock.getApplicableSet(); - assertTrue(set.canBuild(member)); + assertFalse(set.canBuild(member)); assertFalse(set.canBuild(nonMember)); } @@ -433,7 +433,7 @@ public void testGlobalRegionBuildFlagDenyWithRegion() { region.getOwners().addPlayer(member); ApplicableRegionSet set = mock.getApplicableSet(); - assertTrue(set.canBuild(member)); + assertFalse(set.canBuild(member)); assertFalse(set.canBuild(nonMember)); } From e7c9703cedae40eac771639fcbade70db5f954d0 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 16 Aug 2014 15:27:16 -0700 Subject: [PATCH 064/133] Fix checkstyle issues. --- .../bukkit/commands/region/RegionCommandsBase.java | 3 +++ .../sk89q/worldguard/internal/util/sql/StatementUtils.java | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommandsBase.java b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommandsBase.java index ce28601c..feffef5d 100644 --- a/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommandsBase.java +++ b/src/main/java/com/sk89q/worldguard/bukkit/commands/region/RegionCommandsBase.java @@ -44,6 +44,9 @@ class RegionCommandsBase { + protected RegionCommandsBase() { + } + /** * Get the permission model to lookup permissions. * diff --git a/src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java b/src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java index 6f3eea39..7ac27ae3 100644 --- a/src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java +++ b/src/main/java/com/sk89q/worldguard/internal/util/sql/StatementUtils.java @@ -22,7 +22,11 @@ import java.sql.PreparedStatement; import java.sql.SQLException; -public class StatementUtils { +public final class StatementUtils { + + private StatementUtils() { + } + /** * Creates a comma separated list of PreparedStatement place holders * From 59c7197229f7895a154ff79a074ce0e7dcf95457 Mon Sep 17 00:00:00 2001 From: sk89q Date: Sat, 16 Aug 2014 15:28:32 -0700 Subject: [PATCH 065/133] Fix Maven accessing dead repositories. --- pom.xml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index f935799b..00388e94 100644 --- a/pom.xml +++ b/pom.xml @@ -65,10 +65,6 @@ sk89q-repo http://maven.sk89q.com/repo/ - - zml-repo - http://files.zachsthings.com/repo - bukkit-repo http://repo.bukkit.org/content/groups/public @@ -82,6 +78,16 @@ 5.5.8 compile jar + + + com.zachsthings.libcomponents + libcomponents-bukkit + + + org.bukkit + bukkit + +
From b7d8562eceb3653011c57eff63c881219273b285 Mon Sep 17 00:00:00 2001 From: sk89q Date: Fri, 22 Aug 2014 20:43:03 -0700 Subject: [PATCH 133/133] Use a region deny message indicating region data load failure. --- .../sk89q/worldguard/protection/FailedLoadRegionSet.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/sk89q/worldguard/protection/FailedLoadRegionSet.java b/src/main/java/com/sk89q/worldguard/protection/FailedLoadRegionSet.java index 1437b203..402377e7 100644 --- a/src/main/java/com/sk89q/worldguard/protection/FailedLoadRegionSet.java +++ b/src/main/java/com/sk89q/worldguard/protection/FailedLoadRegionSet.java @@ -28,6 +28,7 @@ import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.flags.StateFlag.State; import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import org.bukkit.ChatColor; import javax.annotation.Nullable; import java.util.Collection; @@ -43,6 +44,10 @@ public class FailedLoadRegionSet extends AbstractRegionSet { private static final FailedLoadRegionSet INSTANCE = new FailedLoadRegionSet(); + private final String denyMessage = ChatColor.RED + "Region data for WorldGuard failed to load for this world, so " + + "everything has been protected as a precaution. Please inform a server administrator."; + private final Collection denyMessageCollection = ImmutableList.of(denyMessage); + private FailedLoadRegionSet() { } @@ -62,6 +67,8 @@ public boolean testBuild(RegionAssociable subject, StateFlag... flags) { public V queryValue(@Nullable RegionAssociable subject, Flag flag) { if (flag == DefaultFlag.BUILD) { return (V) State.DENY; + } else if (flag == DefaultFlag.DENY_MESSAGE) { + return (V) denyMessage; } return flag.getDefault(); } @@ -71,6 +78,8 @@ public V queryValue(@Nullable RegionAssociable subject, Flag flag) { public Collection queryAllValues(@Nullable RegionAssociable subject, Flag flag) { if (flag == DefaultFlag.BUILD) { return (Collection) ImmutableList.of(State.DENY); + } else if (flag == DefaultFlag.DENY_MESSAGE) { + return (Collection) denyMessageCollection; } V fallback = flag.getDefault(); return fallback != null ? ImmutableList.of(fallback) : (Collection) ImmutableList.of();