mirror of
https://github.com/EngineHub/WorldGuard.git
synced 2024-11-24 03:25:24 +01:00
Handle region data load / save failure more gracefully.
Also remove use of BoneCP and reorganize the classes.
This commit is contained in:
parent
3045dc0293
commit
4d4e1c6c26
14
pom.xml
14
pom.xml
@ -149,15 +149,6 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.jolbox</groupId>
|
||||
<artifactId>bonecp</artifactId>
|
||||
<version>0.8.0.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
<type>jar</type>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
@ -398,16 +389,11 @@
|
||||
<includes>
|
||||
<include>net.sf.opencsv:opencsv</include>
|
||||
<include>org.khelekore:prtree</include>
|
||||
<include>com.jolbox:bonecp</include>
|
||||
<include>org.flywaydb:flyway-core</include>
|
||||
<include>com.sk89q:squirrelid</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>com.jolbox.bonecp</pattern>
|
||||
<shadedPattern>com.sk89q.worldguard.internal.bonecp</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.flywaydb</pattern>
|
||||
<shadedPattern>com.sk89q.worldguard.internal.flywaydb</shadedPattern>
|
||||
|
@ -25,10 +25,10 @@
|
||||
import com.sk89q.util.yaml.YAMLFormat;
|
||||
import com.sk89q.util.yaml.YAMLProcessor;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.DirectoryYamlDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.DriverType;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.SQLDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.file.DirectoryYamlDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.DriverType;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.sql.SQLDriver;
|
||||
import com.sk89q.worldguard.util.sql.DataSourceConfig;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -99,8 +99,8 @@ public class ConfigurationManager {
|
||||
/**
|
||||
* Region Storage Configuration method, and config values
|
||||
*/
|
||||
public RegionStoreDriver selectedRegionStoreDriver;
|
||||
public Map<DriverType, RegionStoreDriver> regionStoreDriverMap;
|
||||
public RegionDriver selectedRegionStoreDriver;
|
||||
public Map<DriverType, RegionDriver> regionStoreDriverMap;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
@ -187,7 +187,7 @@ public void load() {
|
||||
SQLDriver sqlDriver = new SQLDriver(dataSourceConfig);
|
||||
DirectoryYamlDriver yamlDriver = new DirectoryYamlDriver(getWorldsDataFolder(), "regions.yml");
|
||||
|
||||
this.regionStoreDriverMap = ImmutableMap.<DriverType, RegionStoreDriver>builder()
|
||||
this.regionStoreDriverMap = ImmutableMap.<DriverType, RegionDriver>builder()
|
||||
.put(DriverType.MYSQL, sqlDriver)
|
||||
.put(DriverType.YAML, yamlDriver)
|
||||
.build();
|
||||
|
@ -20,6 +20,7 @@
|
||||
package com.sk89q.worldguard.bukkit;
|
||||
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.RegionResultSet;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
@ -30,7 +31,7 @@
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Keeps a cache of {@link ApplicableRegionSet}s. The contents of the cache
|
||||
* Keeps a cache of {@link RegionResultSet}s. The contents of the cache
|
||||
* must be externally invalidated occasionally (and frequently).
|
||||
*
|
||||
* <p>This class is fully concurrent.</p>
|
||||
|
@ -20,12 +20,12 @@
|
||||
package com.sk89q.worldguard.bukkit;
|
||||
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldguard.protection.managers.ManagerContainer;
|
||||
import com.sk89q.worldguard.protection.managers.RegionContainerImpl;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.migration.Migration;
|
||||
import com.sk89q.worldguard.protection.managers.migration.MigrationException;
|
||||
import com.sk89q.worldguard.protection.managers.migration.UUIDMigration;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
@ -69,7 +69,7 @@ public class RegionContainer {
|
||||
private final Object lock = new Object();
|
||||
private final WorldGuardPlugin plugin;
|
||||
private final QueryCache cache = new QueryCache();
|
||||
private ManagerContainer container;
|
||||
private RegionContainerImpl container;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
@ -85,7 +85,7 @@ public class RegionContainer {
|
||||
*/
|
||||
void initialize() {
|
||||
ConfigurationManager config = plugin.getGlobalStateManager();
|
||||
container = new ManagerContainer(config.selectedRegionStoreDriver);
|
||||
container = new RegionContainerImpl(config.selectedRegionStoreDriver);
|
||||
|
||||
// Migrate to UUIDs
|
||||
autoMigrate();
|
||||
@ -144,7 +144,7 @@ void unload() {
|
||||
*
|
||||
* @return the driver
|
||||
*/
|
||||
public RegionStoreDriver getDriver() {
|
||||
public RegionDriver getDriver() {
|
||||
return container.getDriver();
|
||||
}
|
||||
|
||||
@ -287,7 +287,7 @@ private void autoMigrate() {
|
||||
ConfigurationManager config = plugin.getGlobalStateManager();
|
||||
|
||||
if (config.migrateRegionsToUuid) {
|
||||
RegionStoreDriver driver = getDriver();
|
||||
RegionDriver driver = getDriver();
|
||||
UUIDMigration migrator = new UUIDMigration(driver, plugin.getProfileService());
|
||||
migrator.setKeepUnresolvedNames(config.keepUnresolvedNames);
|
||||
try {
|
||||
|
@ -21,7 +21,10 @@
|
||||
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.FailedLoadRegionSet;
|
||||
import com.sk89q.worldguard.protection.GlobalRegionManager;
|
||||
import com.sk89q.worldguard.protection.PermissiveRegionSet;
|
||||
import com.sk89q.worldguard.protection.RegionResultSet;
|
||||
import com.sk89q.worldguard.protection.association.RegionAssociable;
|
||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
@ -70,7 +73,7 @@ public class RegionQuery {
|
||||
/**
|
||||
* Query for regions containing the given location.
|
||||
*
|
||||
* <p>An instance of {@link ApplicableRegionSet} will always be returned,
|
||||
* <p>An instance of {@link RegionResultSet} will always be returned,
|
||||
* even if regions are disabled or region data failed to load. An
|
||||
* appropriate "virtual" set will be returned in such a case
|
||||
* (for example, if regions are disabled, the returned set
|
||||
@ -86,14 +89,14 @@ public ApplicableRegionSet getApplicableRegions(Location location) {
|
||||
WorldConfiguration worldConfig = config.get(world);
|
||||
|
||||
if (!worldConfig.useRegions) {
|
||||
return ApplicableRegionSet.getEmpty();
|
||||
return PermissiveRegionSet.getInstance();
|
||||
}
|
||||
|
||||
RegionManager manager = globalManager.get(location.getWorld());
|
||||
if (manager != null) {
|
||||
return cache.queryContains(manager, location);
|
||||
} else {
|
||||
return ApplicableRegionSet.getEmpty();
|
||||
return FailedLoadRegionSet.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +112,7 @@ public ApplicableRegionSet getApplicableRegions(Location location) {
|
||||
* @param player the player
|
||||
* @param flags zero or more flags
|
||||
* @return true if permission is granted
|
||||
* @see ApplicableRegionSet#testBuild(RegionAssociable, StateFlag...)
|
||||
* @see RegionResultSet#testBuild(RegionAssociable, StateFlag...)
|
||||
*/
|
||||
public boolean testBuild(Location location, Player player, StateFlag... flags) {
|
||||
checkNotNull(location);
|
||||
@ -137,7 +140,7 @@ public boolean testBuild(Location location, Player player, StateFlag... flags) {
|
||||
* @param subject the subject
|
||||
* @param flags zero or more flags
|
||||
* @return true if permission is granted
|
||||
* @see ApplicableRegionSet#testBuild(RegionAssociable, StateFlag...)
|
||||
* @see RegionResultSet#testBuild(RegionAssociable, StateFlag...)
|
||||
*/
|
||||
public boolean testBuild(Location location, RegionAssociable subject, StateFlag... flags) {
|
||||
checkNotNull(location);
|
||||
@ -165,7 +168,7 @@ public boolean testBuild(Location location, RegionAssociable subject, StateFlag.
|
||||
* @param player an optional player, which would be used to determine the region group to apply
|
||||
* @param flag the flag
|
||||
* @return true if the result was {@code ALLOW}
|
||||
* @see ApplicableRegionSet#queryValue(RegionAssociable, Flag)
|
||||
* @see RegionResultSet#queryValue(RegionAssociable, Flag)
|
||||
*/
|
||||
public boolean testState(Location location, @Nullable Player player, StateFlag... flag) {
|
||||
return StateFlag.test(queryState(location, player, flag));
|
||||
@ -186,7 +189,7 @@ public boolean testState(Location location, @Nullable Player player, StateFlag..
|
||||
* @param player an optional player, which would be used to determine the region groups that apply
|
||||
* @param flags a list of flags to check
|
||||
* @return a state
|
||||
* @see ApplicableRegionSet#queryState(RegionAssociable, StateFlag...)
|
||||
* @see RegionResultSet#queryState(RegionAssociable, StateFlag...)
|
||||
*/
|
||||
@Nullable
|
||||
public State queryState(Location location, @Nullable Player player, StateFlag... flags) {
|
||||
@ -216,7 +219,7 @@ public State queryState(Location location, @Nullable Player player, StateFlag...
|
||||
* @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 ApplicableRegionSet#queryValue(RegionAssociable, Flag)
|
||||
* @see RegionResultSet#queryValue(RegionAssociable, Flag)
|
||||
*/
|
||||
@Nullable
|
||||
public <V> V queryValue(Location location, @Nullable Player player, Flag<V> flag) {
|
||||
@ -239,7 +242,7 @@ public <V> V queryValue(Location location, @Nullable Player player, Flag<V> flag
|
||||
* @param player an optional player, which would be used to determine the region group to apply
|
||||
* @param flag the flag
|
||||
* @return a collection of values
|
||||
* @see ApplicableRegionSet#queryAllValues(RegionAssociable, Flag)
|
||||
* @see RegionResultSet#queryAllValues(RegionAssociable, Flag)
|
||||
*/
|
||||
public <V> Collection<V> queryAllValues(Location location, @Nullable Player player, Flag<V> flag) {
|
||||
LocalPlayer localPlayer = player != null ? plugin.wrapPlayer(player) : null;
|
||||
|
@ -63,6 +63,7 @@
|
||||
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.managers.storage.StorageException;
|
||||
import com.sk89q.worldguard.protection.util.UnresolvedNamesException;
|
||||
import com.sk89q.worldguard.util.FatalConfigurationLoadingException;
|
||||
import com.sk89q.worldguard.util.concurrent.EvenMoreExecutors;
|
||||
@ -320,6 +321,9 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String
|
||||
public String convertThrowable(@Nullable Throwable throwable) {
|
||||
if (throwable instanceof NumberFormatException) {
|
||||
return "Number expected, string received instead.";
|
||||
} else if (throwable instanceof StorageException) {
|
||||
log.log(Level.WARNING, "Error loading/saving regions", throwable);
|
||||
return "Region data could not be loaded/saved: " + throwable.getMessage();
|
||||
} 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) {
|
||||
|
@ -151,9 +151,9 @@ public void listRunningTasks(CommandContext args, CommandSender sender) throws C
|
||||
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("\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("\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");
|
||||
|
@ -54,8 +54,8 @@
|
||||
import com.sk89q.worldguard.protection.managers.migration.DriverMigration;
|
||||
import com.sk89q.worldguard.protection.managers.migration.MigrationException;
|
||||
import com.sk89q.worldguard.protection.managers.migration.UUIDMigration;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.DriverType;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.DriverType;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion.CircularInheritanceException;
|
||||
import com.sk89q.worldguard.util.Enums;
|
||||
@ -864,8 +864,8 @@ public void migrateDB(CommandContext args, CommandSender sender) throws CommandE
|
||||
}
|
||||
|
||||
ConfigurationManager config = plugin.getGlobalStateManager();
|
||||
RegionStoreDriver fromDriver = config.regionStoreDriverMap.get(from);
|
||||
RegionStoreDriver toDriver = config.regionStoreDriverMap.get(to);
|
||||
RegionDriver fromDriver = config.regionStoreDriverMap.get(from);
|
||||
RegionDriver toDriver = config.regionStoreDriverMap.get(to);
|
||||
|
||||
if (fromDriver == null) {
|
||||
throw new CommandException("The driver specified as 'from' does not seem to be supported in your version of WorldGuard.");
|
||||
@ -933,7 +933,7 @@ public void migrateUuid(CommandContext args, CommandSender sender) throws Comman
|
||||
try {
|
||||
ConfigurationManager config = plugin.getGlobalStateManager();
|
||||
RegionContainer container = plugin.getRegionContainer();
|
||||
RegionStoreDriver driver = container.getDriver();
|
||||
RegionDriver driver = container.getDriver();
|
||||
UUIDMigration migration = new UUIDMigration(driver, plugin.getProfileService());
|
||||
migration.setKeepUnresolvedNames(config.keepUnresolvedNames);
|
||||
sender.sendMessage(ChatColor.YELLOW + "Now performing migration... this may take a while.");
|
||||
|
@ -20,8 +20,8 @@
|
||||
package com.sk89q.worldguard.bukkit.commands.task;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Callable;
|
||||
@ -42,7 +42,7 @@ public RegionManagerReloader(RegionManager... manager) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RegionManager> call() throws IOException {
|
||||
public Collection<RegionManager> call() throws StorageException {
|
||||
for (RegionManager manager : managers) {
|
||||
manager.load();
|
||||
}
|
||||
|
@ -20,8 +20,8 @@
|
||||
package com.sk89q.worldguard.bukkit.commands.task;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Callable;
|
||||
@ -42,7 +42,7 @@ public RegionManagerSaver(RegionManager... manager) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<RegionManager> call() throws IOException {
|
||||
public Collection<RegionManager> call() throws StorageException {
|
||||
for (RegionManager manager : managers) {
|
||||
manager.save();
|
||||
}
|
||||
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.association.RegionAssociable;
|
||||
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 javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldguard.protection.flags.StateFlag.test;
|
||||
|
||||
public abstract class AbstractRegionSet implements ApplicableRegionSet {
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean canBuild(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
return test(queryState(player, DefaultFlag.BUILD));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean canConstruct(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
final RegionGroup flag = getFlag(DefaultFlag.CONSTRUCT, player);
|
||||
return RegionGroupFlag.isMember(this, flag, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testState(@Nullable RegionAssociable subject, StateFlag... flags) {
|
||||
return test(queryState(subject, flags));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public State queryState(@Nullable RegionAssociable subject, StateFlag... flags) {
|
||||
State value = null;
|
||||
|
||||
for (StateFlag flag : flags) {
|
||||
value = StateFlag.combine(value, queryValue(subject, flag));
|
||||
if (value == State.DENY) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean allows(StateFlag flag) {
|
||||
checkNotNull(flag);
|
||||
|
||||
if (flag == DefaultFlag.BUILD) {
|
||||
throw new IllegalArgumentException("Can't use build flag with allows()");
|
||||
}
|
||||
|
||||
return test(queryState(null, flag));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean allows(StateFlag flag, @Nullable LocalPlayer player) {
|
||||
checkNotNull(flag);
|
||||
|
||||
if (flag == DefaultFlag.BUILD) {
|
||||
throw new IllegalArgumentException("Can't use build flag with allows()");
|
||||
}
|
||||
|
||||
return test(queryState(player, flag));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public <T extends Flag<V>, V> V getFlag(T flag, @Nullable LocalPlayer groupPlayer) {
|
||||
return queryValue(groupPlayer, flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public <T extends Flag<V>, V> V getFlag(T flag) {
|
||||
return getFlag(flag, null);
|
||||
}
|
||||
|
||||
}
|
@ -20,11 +20,11 @@
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.bukkit.RegionQuery;
|
||||
import com.sk89q.worldguard.protection.association.RegionAssociable;
|
||||
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;
|
||||
@ -32,15 +32,8 @@
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldguard.protection.flags.StateFlag.test;
|
||||
|
||||
/**
|
||||
* Represents the effective set of flags, owners, and members for a given
|
||||
* spatial query.
|
||||
@ -48,45 +41,25 @@
|
||||
* <p>An instance of this can be created using the spatial query methods
|
||||
* available on {@link RegionManager}.</p>
|
||||
*/
|
||||
public class ApplicableRegionSet implements Iterable<ProtectedRegion> {
|
||||
public interface ApplicableRegionSet extends Iterable<ProtectedRegion> {
|
||||
|
||||
/**
|
||||
* A static instance of an empty set.
|
||||
*/
|
||||
private static final ApplicableRegionSet EMPTY = new ApplicableRegionSet(Collections.<ProtectedRegion>emptyList(), null);
|
||||
|
||||
private final List<ProtectedRegion> applicable;
|
||||
private final FlagValueCalculator flagValueCalculator;
|
||||
@Nullable
|
||||
private Set<ProtectedRegion> regionSet;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
* Return whether this region set is a virtual set. A virtual set
|
||||
* does not contain real results.
|
||||
*
|
||||
* <p>A sorted set will be created to include the collection of regions.</p>
|
||||
* <p>A virtual result may be returned if region data failed to load or
|
||||
* there was some special exception (i.e. the region bypass permission).
|
||||
* </p>
|
||||
*
|
||||
* @param applicable the regions contained in this set
|
||||
* @param globalRegion the global region, set aside for special handling.
|
||||
*/
|
||||
public ApplicableRegionSet(List<ProtectedRegion> applicable, @Nullable ProtectedRegion globalRegion) {
|
||||
this(applicable, globalRegion, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
* <p>Be sure to check the value of this flag if an instance of this
|
||||
* interface is being retrieved from {@link RegionQuery} as it may
|
||||
* return an instance of {@link PermissiveRegionSet} or
|
||||
* {@link FailedLoadRegionSet}, among other possibilities.</p>
|
||||
*
|
||||
* @param applicable the regions contained in this set
|
||||
* @param globalRegion the global region, set aside for special handling.
|
||||
* @param sorted true if the list is already sorted
|
||||
* @return true if loaded
|
||||
* @see FailedLoadRegionSet
|
||||
*/
|
||||
private ApplicableRegionSet(List<ProtectedRegion> applicable, @Nullable ProtectedRegion globalRegion, boolean sorted) {
|
||||
checkNotNull(applicable);
|
||||
if (!sorted) {
|
||||
Collections.sort(applicable);
|
||||
}
|
||||
this.applicable = applicable;
|
||||
this.flagValueCalculator = new FlagValueCalculator(applicable, globalRegion);
|
||||
}
|
||||
boolean isVirtual();
|
||||
|
||||
/**
|
||||
* Tests whether the {@link DefaultFlag#BUILD} flag or membership
|
||||
@ -102,10 +75,7 @@ private ApplicableRegionSet(List<ProtectedRegion> applicable, @Nullable Protecte
|
||||
* @deprecated use {@link #testBuild(RegionAssociable, StateFlag...)}
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean canBuild(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
return test(flagValueCalculator.queryState(player, DefaultFlag.BUILD));
|
||||
}
|
||||
boolean canBuild(LocalPlayer player);
|
||||
|
||||
/**
|
||||
* Test whether the given flags evaluate to {@code ALLOW}, implicitly also
|
||||
@ -120,10 +90,7 @@ public boolean canBuild(LocalPlayer player) {
|
||||
* @return true if permission is granted
|
||||
* @see #queryState(RegionAssociable, StateFlag...)
|
||||
*/
|
||||
public boolean testBuild(RegionAssociable subject, StateFlag... flags) {
|
||||
checkNotNull(subject);
|
||||
return test(flagValueCalculator.queryState(subject, DefaultFlag.BUILD, flags));
|
||||
}
|
||||
boolean testBuild(RegionAssociable subject, StateFlag... flags);
|
||||
|
||||
/**
|
||||
* Test whether the (effective) value for a list of state flags equals
|
||||
@ -140,9 +107,7 @@ public boolean testBuild(RegionAssociable subject, StateFlag... flags) {
|
||||
* @return true if the result was {@code ALLOW}
|
||||
* @see #queryState(RegionAssociable, StateFlag...)
|
||||
*/
|
||||
public boolean testState(@Nullable RegionAssociable subject, StateFlag... flags) {
|
||||
return test(flagValueCalculator.queryState(subject, flags));
|
||||
}
|
||||
boolean testState(@Nullable RegionAssociable subject, StateFlag... flags);
|
||||
|
||||
/**
|
||||
* Get the (effective) value for a list of state flags. The rules of
|
||||
@ -160,9 +125,7 @@ public boolean testState(@Nullable RegionAssociable subject, StateFlag... flags)
|
||||
* @return a state
|
||||
*/
|
||||
@Nullable
|
||||
public State queryState(@Nullable RegionAssociable subject, StateFlag... flags) {
|
||||
return flagValueCalculator.queryState(subject, flags);
|
||||
}
|
||||
State queryState(@Nullable RegionAssociable subject, StateFlag... flags);
|
||||
|
||||
/**
|
||||
* Get the effective value for a flag. If there are multiple values
|
||||
@ -187,9 +150,7 @@ public State queryState(@Nullable RegionAssociable subject, StateFlag... flags)
|
||||
* @return a value, which could be {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public <V> V queryValue(@Nullable RegionAssociable subject, Flag<V> flag) {
|
||||
return flagValueCalculator.queryValue(subject, flag);
|
||||
}
|
||||
<V> V queryValue(@Nullable RegionAssociable subject, Flag<V> flag);
|
||||
|
||||
/**
|
||||
* Get the effective values for a flag, returning a collection of all
|
||||
@ -206,9 +167,7 @@ public <V> V queryValue(@Nullable RegionAssociable subject, Flag<V> flag) {
|
||||
* @param flag the flag
|
||||
* @return a collection of values
|
||||
*/
|
||||
public <V> Collection<V> queryAllValues(@Nullable RegionAssociable subject, Flag<V> flag) {
|
||||
return flagValueCalculator.queryAllValues(subject, flag);
|
||||
}
|
||||
<V> Collection<V> queryAllValues(@Nullable RegionAssociable subject, Flag<V> flag);
|
||||
|
||||
/**
|
||||
* Test whether the construct flag evaluates true for the given player.
|
||||
@ -219,11 +178,7 @@ public <V> Collection<V> queryAllValues(@Nullable RegionAssociable subject, Flag
|
||||
* needed because flags now support groups assigned to them.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean canConstruct(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
final RegionGroup flag = getFlag(DefaultFlag.CONSTRUCT, player);
|
||||
return RegionGroupFlag.isMember(this, flag, player);
|
||||
}
|
||||
boolean canConstruct(LocalPlayer player);
|
||||
|
||||
/**
|
||||
* Gets the state of a state flag. This cannot be used for the build flag.
|
||||
@ -234,15 +189,7 @@ public boolean canConstruct(LocalPlayer player) {
|
||||
* @deprecated use {@link #queryState(RegionAssociable, StateFlag...)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean allows(StateFlag flag) {
|
||||
checkNotNull(flag);
|
||||
|
||||
if (flag == DefaultFlag.BUILD) {
|
||||
throw new IllegalArgumentException("Can't use build flag with allows()");
|
||||
}
|
||||
|
||||
return test(flagValueCalculator.queryState(null, flag));
|
||||
}
|
||||
boolean allows(StateFlag flag);
|
||||
|
||||
/**
|
||||
* Gets the state of a state flag. This cannot be used for the build flag.
|
||||
@ -254,15 +201,7 @@ public boolean allows(StateFlag flag) {
|
||||
* @deprecated use {@link #queryState(RegionAssociable, StateFlag...)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean allows(StateFlag flag, @Nullable LocalPlayer player) {
|
||||
checkNotNull(flag);
|
||||
|
||||
if (flag == DefaultFlag.BUILD) {
|
||||
throw new IllegalArgumentException("Can't use build flag with allows()");
|
||||
}
|
||||
|
||||
return test(flagValueCalculator.queryState(player, flag));
|
||||
}
|
||||
boolean allows(StateFlag flag, @Nullable LocalPlayer player);
|
||||
|
||||
/**
|
||||
* Test whether a player is an owner of all regions in this set.
|
||||
@ -270,17 +209,7 @@ public boolean allows(StateFlag flag, @Nullable LocalPlayer 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;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
boolean isOwnerOfAll(LocalPlayer player);
|
||||
|
||||
/**
|
||||
* Test whether a player is an owner or member of all regions in this set.
|
||||
@ -288,17 +217,7 @@ public boolean isOwnerOfAll(LocalPlayer 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;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
boolean isMemberOfAll(LocalPlayer player);
|
||||
|
||||
/**
|
||||
* Gets the value of a flag. Do not use this for state flags
|
||||
@ -311,9 +230,7 @@ public boolean isMemberOfAll(LocalPlayer player) {
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public <T extends Flag<V>, V> V getFlag(T flag) {
|
||||
return getFlag(flag, null);
|
||||
}
|
||||
<T extends Flag<V>, V> V getFlag(T flag);
|
||||
|
||||
/**
|
||||
* Gets the value of a flag. Do not use this for state flags
|
||||
@ -328,54 +245,20 @@ public <T extends Flag<V>, V> V getFlag(T flag) {
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public <T extends Flag<V>, V> V getFlag(T flag, @Nullable LocalPlayer groupPlayer) {
|
||||
return flagValueCalculator.queryValue(groupPlayer, flag);
|
||||
}
|
||||
<T extends Flag<V>, V> V getFlag(T flag, @Nullable LocalPlayer groupPlayer);
|
||||
|
||||
/**
|
||||
* Get the number of regions that are included.
|
||||
*
|
||||
* @return the number of contained regions
|
||||
*/
|
||||
public int size() {
|
||||
return applicable.size();
|
||||
}
|
||||
int size();
|
||||
|
||||
/**
|
||||
* Get an immutable set of regions that are included in this set.
|
||||
*
|
||||
* @return a set of regions
|
||||
*/
|
||||
public Set<ProtectedRegion> getRegions() {
|
||||
if (regionSet != null) {
|
||||
return regionSet;
|
||||
}
|
||||
regionSet = Collections.unmodifiableSet(new HashSet<ProtectedRegion>(applicable));
|
||||
return regionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ProtectedRegion> iterator() {
|
||||
return applicable.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance that contains no regions and has no global region.
|
||||
*/
|
||||
public static ApplicableRegionSet getEmpty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using a list of regions that is known to
|
||||
* already be sorted by priority descending.
|
||||
*
|
||||
* @param regions a list of regions
|
||||
* @param globalRegion a global region
|
||||
* @return
|
||||
*/
|
||||
public static ApplicableRegionSet fromSortedList(List<ProtectedRegion> regions, @Nullable ProtectedRegion globalRegion) {
|
||||
return new ApplicableRegionSet(regions, globalRegion, true);
|
||||
}
|
||||
Set<ProtectedRegion> getRegions();
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.association.RegionAssociable;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
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.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A region set that is to be used when region data has failed. Operations
|
||||
* are blocked.
|
||||
*/
|
||||
public class FailedLoadRegionSet extends AbstractRegionSet {
|
||||
|
||||
private static final FailedLoadRegionSet INSTANCE = new FailedLoadRegionSet();
|
||||
|
||||
private FailedLoadRegionSet() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testBuild(RegionAssociable subject, StateFlag... flags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
@Override
|
||||
public <V> V queryValue(@Nullable RegionAssociable subject, Flag<V> flag) {
|
||||
if (flag instanceof StateFlag) {
|
||||
return ((StateFlag) flag).getDefault() ? (V) State.DENY : (V) State.ALLOW; // Inverse default
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Collection<V> queryAllValues(@Nullable RegionAssociable subject, Flag<V> flag) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwnerOfAll(LocalPlayer player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMemberOfAll(LocalPlayer player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> getRegions() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ProtectedRegion> iterator() {
|
||||
return Iterators.emptyIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance.
|
||||
*
|
||||
* @return an instance
|
||||
*/
|
||||
public static FailedLoadRegionSet getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.association.RegionAssociable;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
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.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A virtual region result set that is highly permissive, considering everyone
|
||||
* a member. Returned flag values are default values (when available).
|
||||
*/
|
||||
public class PermissiveRegionSet extends AbstractRegionSet {
|
||||
|
||||
private static final PermissiveRegionSet INSTANCE = new PermissiveRegionSet();
|
||||
|
||||
private PermissiveRegionSet() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testBuild(RegionAssociable subject, StateFlag... flags) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
@Override
|
||||
public <V> V queryValue(@Nullable RegionAssociable subject, Flag<V> flag) {
|
||||
if (flag instanceof StateFlag) {
|
||||
return ((StateFlag) flag).getDefault() ? (V) State.ALLOW : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Collection<V> queryAllValues(@Nullable RegionAssociable subject, Flag<V> flag) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwnerOfAll(LocalPlayer player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMemberOfAll(LocalPlayer player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> getRegions() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ProtectedRegion> iterator() {
|
||||
return Iterators.emptyIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance.
|
||||
*
|
||||
* @return an instance
|
||||
*/
|
||||
public static PermissiveRegionSet getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.association.RegionAssociable;
|
||||
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.flags.StateFlag.State;
|
||||
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.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldguard.protection.flags.StateFlag.test;
|
||||
|
||||
/**
|
||||
* An implementation that calculates flags using a list of regions.
|
||||
*/
|
||||
public class RegionResultSet extends AbstractRegionSet {
|
||||
|
||||
private final List<ProtectedRegion> applicable;
|
||||
private final FlagValueCalculator flagValueCalculator;
|
||||
@Nullable
|
||||
private Set<ProtectedRegion> regionSet;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
*
|
||||
* <p>A sorted set will be created to include the collection of regions.</p>
|
||||
*
|
||||
* @param applicable the regions contained in this set
|
||||
* @param globalRegion the global region, set aside for special handling.
|
||||
*/
|
||||
public RegionResultSet(List<ProtectedRegion> applicable, @Nullable ProtectedRegion globalRegion) {
|
||||
this(applicable, globalRegion, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
*
|
||||
* @param applicable the regions contained in this set
|
||||
* @param globalRegion the global region, set aside for special handling.
|
||||
* @param sorted true if the list is already sorted
|
||||
*/
|
||||
private RegionResultSet(List<ProtectedRegion> applicable, @Nullable ProtectedRegion globalRegion, boolean sorted) {
|
||||
checkNotNull(applicable);
|
||||
if (!sorted) {
|
||||
Collections.sort(applicable);
|
||||
}
|
||||
this.applicable = applicable;
|
||||
this.flagValueCalculator = new FlagValueCalculator(applicable, globalRegion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testBuild(RegionAssociable subject, StateFlag... flags) {
|
||||
checkNotNull(subject);
|
||||
return test(flagValueCalculator.queryState(subject, DefaultFlag.BUILD, flags));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public State queryState(@Nullable RegionAssociable subject, StateFlag... flags) {
|
||||
return flagValueCalculator.queryState(subject, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <V> V queryValue(@Nullable RegionAssociable subject, Flag<V> flag) {
|
||||
return flagValueCalculator.queryValue(subject, flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Collection<V> queryAllValues(@Nullable RegionAssociable subject, Flag<V> flag) {
|
||||
return flagValueCalculator.queryAllValues(subject, flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwnerOfAll(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
|
||||
for (ProtectedRegion region : applicable) {
|
||||
if (!region.isOwner(player)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMemberOfAll(LocalPlayer player) {
|
||||
checkNotNull(player);
|
||||
|
||||
for (ProtectedRegion region : applicable) {
|
||||
if (!region.isMember(player)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return applicable.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> getRegions() {
|
||||
if (regionSet != null) {
|
||||
return regionSet;
|
||||
}
|
||||
regionSet = Collections.unmodifiableSet(new HashSet<ProtectedRegion>(applicable));
|
||||
return regionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ProtectedRegion> iterator() {
|
||||
return applicable.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using a list of regions that is known to
|
||||
* already be sorted by priority descending.
|
||||
*
|
||||
* @param regions a list of regions
|
||||
* @param globalRegion a global region
|
||||
* @return an instance
|
||||
*/
|
||||
public static RegionResultSet fromSortedList(List<ProtectedRegion> regions, @Nullable ProtectedRegion globalRegion) {
|
||||
return new RegionResultSet(regions, globalRegion, true);
|
||||
}
|
||||
|
||||
}
|
@ -23,15 +23,16 @@
|
||||
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;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
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.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -47,18 +48,22 @@
|
||||
|
||||
/**
|
||||
* Manages different {@link RegionManager}s for different worlds or dimensions.
|
||||
*
|
||||
* <p>This is an internal class. Do not use it.</p>
|
||||
*/
|
||||
public class ManagerContainer {
|
||||
public class RegionContainerImpl {
|
||||
|
||||
private static final Logger log = Logger.getLogger(ManagerContainer.class.getCanonicalName());
|
||||
private static final Logger log = Logger.getLogger(RegionContainerImpl.class.getCanonicalName());
|
||||
private static final int LOAD_ATTEMPT_INTERVAL = 1000 * 30;
|
||||
private static final int SAVE_INTERVAL = 1000 * 30;
|
||||
|
||||
private final ConcurrentMap<Normal, RegionManager> mapping = new ConcurrentHashMap<Normal, RegionManager>();
|
||||
private final Object lock = new Object();
|
||||
private final RegionStoreDriver driver;
|
||||
private final RegionDriver driver;
|
||||
private final Supplier<? extends ConcurrentRegionIndex> indexFactory = new ChunkHashTable.Factory(new PriorityRTreeIndex.Factory());
|
||||
private final Timer timer = new Timer();
|
||||
|
||||
private final Set<Normal> failingLoads = new HashSet<Normal>();
|
||||
private final Set<RegionManager> failingSaves = Collections.synchronizedSet(
|
||||
Collections.newSetFromMap(new WeakHashMap<RegionManager, Boolean>()));
|
||||
|
||||
@ -67,9 +72,10 @@ public class ManagerContainer {
|
||||
*
|
||||
* @param driver the region store driver
|
||||
*/
|
||||
public ManagerContainer(RegionStoreDriver driver) {
|
||||
public RegionContainerImpl(RegionDriver driver) {
|
||||
checkNotNull(driver);
|
||||
this.driver = driver;
|
||||
timer.schedule(new BackgroundLoader(), LOAD_ATTEMPT_INTERVAL, LOAD_ATTEMPT_INTERVAL);
|
||||
timer.schedule(new BackgroundSaver(), SAVE_INTERVAL, SAVE_INTERVAL);
|
||||
}
|
||||
|
||||
@ -78,7 +84,7 @@ public ManagerContainer(RegionStoreDriver driver) {
|
||||
*
|
||||
* @return the driver
|
||||
*/
|
||||
public RegionStoreDriver getDriver() {
|
||||
public RegionDriver getDriver() {
|
||||
return driver;
|
||||
}
|
||||
|
||||
@ -103,9 +109,11 @@ public RegionManager load(String name) {
|
||||
try {
|
||||
manager = createAndLoad(name);
|
||||
mapping.put(normal, manager);
|
||||
failingLoads.remove(normal);
|
||||
return manager;
|
||||
} catch (IOException e) {
|
||||
log.log(Level.WARNING, "Failed to load the region data for '" + name + "'", e);
|
||||
} catch (StorageException e) {
|
||||
log.log(Level.WARNING, "Failed to load the region data for '" + name + "' (periodic attempts will be made to load the data until success)", e);
|
||||
failingLoads.add(normal);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -117,10 +125,10 @@ public RegionManager load(String name) {
|
||||
*
|
||||
* @param name the name of the world
|
||||
* @return a region manager
|
||||
* @throws IOException thrown if loading fals
|
||||
* @throws StorageException thrown if loading fals
|
||||
*/
|
||||
private RegionManager createAndLoad(String name) throws IOException {
|
||||
RegionStore store = driver.get(name);
|
||||
private RegionManager createAndLoad(String name) throws StorageException {
|
||||
RegionDatabase store = driver.get(name);
|
||||
RegionManager manager = new RegionManager(store, indexFactory);
|
||||
manager.load(); // Try loading, although it may fail
|
||||
return manager;
|
||||
@ -144,12 +152,15 @@ public void unload(String name) {
|
||||
if (manager != null) {
|
||||
try {
|
||||
manager.save();
|
||||
} catch (IOException e) {
|
||||
} catch (StorageException e) {
|
||||
log.log(Level.WARNING, "Failed to save the region data for '" + name + "'", e);
|
||||
}
|
||||
|
||||
mapping.remove(normal);
|
||||
failingSaves.remove(manager);
|
||||
}
|
||||
|
||||
failingLoads.remove(normal);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,12 +175,14 @@ public void unloadAll() {
|
||||
RegionManager manager = entry.getValue();
|
||||
try {
|
||||
manager.saveChanges();
|
||||
} catch (IOException e) {
|
||||
} catch (StorageException e) {
|
||||
log.log(Level.WARNING, "Failed to save the region data for '" + name + "' while unloading the data for all worlds", e);
|
||||
}
|
||||
}
|
||||
|
||||
mapping.clear();
|
||||
failingLoads.clear();
|
||||
failingSaves.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +233,7 @@ public void run() {
|
||||
log.info("Region data changes made in '" + name + "' have been background saved");
|
||||
}
|
||||
failingSaves.remove(manager);
|
||||
} catch (IOException e) {
|
||||
} catch (StorageException e) {
|
||||
failingSaves.add(manager);
|
||||
log.log(Level.WARNING, "Failed to save the region data for '" + name + "' during a periodical save", e);
|
||||
} catch (Exception e) {
|
||||
@ -232,4 +245,33 @@ public void run() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A task to re-try loading region data that has not yet been
|
||||
* successfully loaded.
|
||||
*/
|
||||
private class BackgroundLoader extends TimerTask {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (lock) {
|
||||
if (!failingLoads.isEmpty()) {
|
||||
log.info("Attempting to load region data that has previously failed to load...");
|
||||
|
||||
Iterator<Normal> it = failingLoads.iterator();
|
||||
while (it.hasNext()) {
|
||||
Normal normal = it.next();
|
||||
try {
|
||||
RegionManager manager = createAndLoad(normal.toString());
|
||||
mapping.put(normal, manager);
|
||||
it.remove();
|
||||
log.info("Successfully loaded region data for '" + normal.toString() + "'");
|
||||
} catch (StorageException e) {
|
||||
log.log(Level.WARNING, "Region data is still failing to load, at least for the world named '" + normal.toString() + "'", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -25,16 +25,17 @@
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.RegionResultSet;
|
||||
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.managers.storage.RegionDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.util.RegionCollectionConsumer;
|
||||
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;
|
||||
@ -48,9 +49,12 @@
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* A region manager holds the regions for a world.
|
||||
*/
|
||||
public final class RegionManager {
|
||||
|
||||
private final RegionStore store;
|
||||
private final RegionDatabase store;
|
||||
private final Supplier<? extends ConcurrentRegionIndex> indexFactory;
|
||||
private ConcurrentRegionIndex index;
|
||||
|
||||
@ -60,7 +64,7 @@ public final class RegionManager {
|
||||
* @param store the region store
|
||||
* @param indexFactory the factory for creating new instances of the index
|
||||
*/
|
||||
public RegionManager(RegionStore store, Supplier<? extends ConcurrentRegionIndex> indexFactory) {
|
||||
public RegionManager(RegionDatabase store, Supplier<? extends ConcurrentRegionIndex> indexFactory) {
|
||||
checkNotNull(store);
|
||||
checkNotNull(indexFactory);
|
||||
|
||||
@ -85,9 +89,9 @@ public String getName() {
|
||||
* prevent the creation or modification of regions in the index while
|
||||
* a new collection of regions is loaded from storage.</p>
|
||||
*
|
||||
* @throws IOException thrown when loading fails
|
||||
* @throws StorageException thrown when loading fails
|
||||
*/
|
||||
public void load() throws IOException {
|
||||
public void load() throws StorageException {
|
||||
Set<ProtectedRegion> regions = store.loadAll();
|
||||
for (ProtectedRegion region : regions) {
|
||||
region.setDirty(false);
|
||||
@ -98,9 +102,9 @@ public void load() throws IOException {
|
||||
/**
|
||||
* Save a snapshot of all the regions as it is right now to storage.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws StorageException thrown on save error
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
public void save() throws StorageException {
|
||||
store.saveAll(new HashSet<ProtectedRegion>(getValuesCopy()));
|
||||
}
|
||||
|
||||
@ -112,9 +116,9 @@ public void save() throws IOException {
|
||||
* <p>This method does nothing if there are no changes.</p>
|
||||
*
|
||||
* @return true if there were changes to be saved
|
||||
* @throws IOException thrown on save error
|
||||
* @throws StorageException thrown on save error
|
||||
*/
|
||||
public boolean saveChanges() throws IOException {
|
||||
public boolean saveChanges() throws StorageException {
|
||||
RegionDifference diff = index.getAndClearDifference();
|
||||
boolean successful = false;
|
||||
|
||||
@ -314,7 +318,7 @@ public ApplicableRegionSet getApplicableRegions(Vector position) {
|
||||
|
||||
List<ProtectedRegion> regions = new ArrayList<ProtectedRegion>();
|
||||
index.applyContaining(position, new RegionCollectionConsumer(regions, true));
|
||||
return new ApplicableRegionSet(regions, index.get("__global__"));
|
||||
return new RegionResultSet(regions, index.get("__global__"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -329,7 +333,7 @@ public ApplicableRegionSet getApplicableRegions(ProtectedRegion region) {
|
||||
|
||||
List<ProtectedRegion> regions = new ArrayList<ProtectedRegion>();
|
||||
index.applyIntersecting(region, new RegionCollectionConsumer(regions, true));
|
||||
return new ApplicableRegionSet(regions, index.get("__global__"));
|
||||
return new RegionResultSet(regions, index.get("__global__"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,10 +19,10 @@
|
||||
|
||||
package com.sk89q.worldguard.protection.managers.migration;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -36,14 +36,14 @@
|
||||
abstract class AbstractMigration implements Migration {
|
||||
|
||||
private static final Logger log = Logger.getLogger(AbstractMigration.class.getCanonicalName());
|
||||
private final RegionStoreDriver driver;
|
||||
private final RegionDriver driver;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param driver the storage driver
|
||||
*/
|
||||
public AbstractMigration(RegionStoreDriver driver) {
|
||||
public AbstractMigration(RegionDriver driver) {
|
||||
checkNotNull(driver);
|
||||
|
||||
this.driver = driver;
|
||||
@ -52,7 +52,7 @@ public AbstractMigration(RegionStoreDriver driver) {
|
||||
@Override
|
||||
public final void migrate() throws MigrationException {
|
||||
try {
|
||||
for (RegionStore store : driver.getAll()) {
|
||||
for (RegionDatabase store : driver.getAll()) {
|
||||
try {
|
||||
migrate(store);
|
||||
} catch (MigrationException e) {
|
||||
@ -61,7 +61,7 @@ public final void migrate() throws MigrationException {
|
||||
}
|
||||
|
||||
postMigration();
|
||||
} catch (IOException e) {
|
||||
} catch (StorageException e) {
|
||||
throw new MigrationException("Migration failed because the process of getting a list of all the worlds to migrate failed", e);
|
||||
}
|
||||
}
|
||||
@ -72,7 +72,7 @@ public final void migrate() throws MigrationException {
|
||||
* @param store the region store
|
||||
* @throws MigrationException on migration error
|
||||
*/
|
||||
protected abstract void migrate(RegionStore store)throws MigrationException;
|
||||
protected abstract void migrate(RegionDatabase store)throws MigrationException;
|
||||
|
||||
/**
|
||||
* Called after migration has successfully completed.
|
||||
|
@ -19,11 +19,11 @@
|
||||
|
||||
package com.sk89q.worldguard.protection.managers.migration;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
public class DriverMigration extends AbstractMigration {
|
||||
|
||||
private static final Logger log = Logger.getLogger(DriverMigration.class.getCanonicalName());
|
||||
private final RegionStoreDriver target;
|
||||
private final RegionDriver target;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
@ -43,21 +43,21 @@ public class DriverMigration extends AbstractMigration {
|
||||
* @param driver the source storage driver
|
||||
* @param target the target storage driver
|
||||
*/
|
||||
public DriverMigration(RegionStoreDriver driver, RegionStoreDriver target) {
|
||||
public DriverMigration(RegionDriver driver, RegionDriver target) {
|
||||
super(driver);
|
||||
checkNotNull(target);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void migrate(RegionStore store) throws MigrationException {
|
||||
protected void migrate(RegionDatabase store) throws MigrationException {
|
||||
Set<ProtectedRegion> regions;
|
||||
|
||||
log.info("Loading the regions for '" + store.getName() + "' with the old driver...");
|
||||
|
||||
try {
|
||||
regions = store.loadAll();
|
||||
} catch (IOException e) {
|
||||
} catch (StorageException e) {
|
||||
throw new MigrationException("Failed to load region data for the world '" + store.getName() + "'", e);
|
||||
}
|
||||
|
||||
@ -67,17 +67,11 @@ protected void migrate(RegionStore store) throws MigrationException {
|
||||
private void write(String name, Set<ProtectedRegion> regions) throws MigrationException {
|
||||
log.info("Saving the data for '" + name + "' with the new driver...");
|
||||
|
||||
RegionStore store;
|
||||
|
||||
try {
|
||||
store = target.get(name);
|
||||
} catch (IOException e) {
|
||||
throw new MigrationException("The driver to migrate to can't store region data for the world '" + name + "'", e);
|
||||
}
|
||||
RegionDatabase store = target.get(name);
|
||||
|
||||
try {
|
||||
store.saveAll(regions);
|
||||
} catch (IOException e) {
|
||||
} catch (StorageException e) {
|
||||
throw new MigrationException("Failed to save region data for '" + store.getName() + "' to the new driver", e);
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,9 @@
|
||||
import com.sk89q.squirrelid.resolver.ProfileService;
|
||||
import com.sk89q.worldguard.domains.DefaultDomain;
|
||||
import com.sk89q.worldguard.domains.PlayerDomain;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.driver.RegionStoreDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -62,21 +63,21 @@ public class UUIDMigration extends AbstractMigration {
|
||||
* @param driver the storage driver
|
||||
* @param profileService the profile service
|
||||
*/
|
||||
public UUIDMigration(RegionStoreDriver driver, ProfileService profileService) {
|
||||
public UUIDMigration(RegionDriver driver, ProfileService profileService) {
|
||||
super(driver);
|
||||
checkNotNull(profileService);
|
||||
this.profileService = profileService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void migrate(RegionStore store) throws MigrationException {
|
||||
protected void migrate(RegionDatabase store) throws MigrationException {
|
||||
log.log(Level.INFO, "Migrating regions in '" + store.getName() + "' to convert names -> UUIDs...");
|
||||
|
||||
Set<ProtectedRegion> regions;
|
||||
|
||||
try {
|
||||
regions = store.loadAll();
|
||||
} catch (IOException e) {
|
||||
} catch (StorageException e) {
|
||||
throw new MigrationException("Failed to load region data for the world '" + store.getName() + "'", e);
|
||||
}
|
||||
|
||||
@ -84,7 +85,7 @@ protected void migrate(RegionStore store) throws MigrationException {
|
||||
|
||||
try {
|
||||
store.saveAll(regions);
|
||||
} catch (IOException e) {
|
||||
} catch (StorageException e) {
|
||||
throw new MigrationException("Failed to save region data after migration of the world '" + store.getName() + "'", e);
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
/**
|
||||
* Thrown when a partial save is not supported.
|
||||
*/
|
||||
public class DifferenceSaveException extends RegionStoreException {
|
||||
public class DifferenceSaveException extends StorageException {
|
||||
|
||||
public DifferenceSaveException() {
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.managers.storage.driver;
|
||||
package com.sk89q.worldguard.protection.managers.storage;
|
||||
|
||||
/**
|
||||
* An enumeration of supported drivers.
|
@ -22,15 +22,17 @@
|
||||
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.
|
||||
* A region database that saves the memory to an in-memory {@link HashSet}.
|
||||
*
|
||||
* <p>This implementation is thread-safe. Difference saves
|
||||
* are not supported.</p>
|
||||
*/
|
||||
public class MemoryRegionStore implements RegionStore {
|
||||
public class MemoryRegionDatabase implements RegionDatabase {
|
||||
|
||||
private Set<ProtectedRegion> regions = Collections.emptySet();
|
||||
|
||||
@ -40,17 +42,17 @@ public String getName() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> loadAll() throws IOException {
|
||||
public Set<ProtectedRegion> loadAll() {
|
||||
return regions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll(Set<ProtectedRegion> regions) throws IOException {
|
||||
public void saveAll(Set<ProtectedRegion> regions) {
|
||||
this.regions = Collections.unmodifiableSet(new HashSet<ProtectedRegion>(regions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveChanges(RegionDifference difference) throws DifferenceSaveException, IOException {
|
||||
public void saveChanges(RegionDifference difference) throws DifferenceSaveException {
|
||||
throw new DifferenceSaveException();
|
||||
}
|
||||
|
@ -22,13 +22,18 @@
|
||||
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.
|
||||
* A region database stores a set of regions for a single world.
|
||||
*
|
||||
* <p>If there are multiple worlds, then there should be one region database
|
||||
* per world. To manage multiple region databases, consider using an
|
||||
* implementation of a {@link RegionDriver}.</p>
|
||||
*
|
||||
* @see RegionDriver
|
||||
*/
|
||||
public interface RegionStore {
|
||||
public interface RegionDatabase {
|
||||
|
||||
/**
|
||||
* Get a displayable name for this store.
|
||||
@ -48,18 +53,18 @@ public interface RegionStore {
|
||||
* {@code get()} and {@code put()} calls in order to maximize performance.
|
||||
* </p>
|
||||
*
|
||||
* @return a setf loaded regions
|
||||
* @throws IOException thrown on read error
|
||||
* @return a set of loaded regions
|
||||
* @throws StorageException thrown on read error
|
||||
*/
|
||||
Set<ProtectedRegion> loadAll() throws IOException;
|
||||
Set<ProtectedRegion> loadAll() throws StorageException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws StorageException thrown on write error
|
||||
*/
|
||||
void saveAll(Set<ProtectedRegion> regions) throws IOException;
|
||||
void saveAll(Set<ProtectedRegion> regions) throws StorageException;
|
||||
|
||||
/**
|
||||
* Perform a partial save that only commits changes, rather than the
|
||||
@ -67,8 +72,8 @@ public interface RegionStore {
|
||||
*
|
||||
* @param difference the difference
|
||||
* @throws DifferenceSaveException thrown if partial saves are not supported
|
||||
* @throws IOException thrown on write error
|
||||
* @throws StorageException thrown on write error
|
||||
*/
|
||||
void saveChanges(RegionDifference difference) throws DifferenceSaveException, IOException;
|
||||
void saveChanges(RegionDifference difference) throws DifferenceSaveException, StorageException;
|
||||
|
||||
}
|
@ -31,13 +31,16 @@
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Utility methods for region stores.
|
||||
* This class provides utility methods that may be helpful in the
|
||||
* implementation of region databases.
|
||||
*
|
||||
* @see RegionDatabase
|
||||
*/
|
||||
public final class RegionStoreUtils {
|
||||
public final class RegionDatabaseUtils {
|
||||
|
||||
private static final Logger log = Logger.getLogger(RegionStoreUtils.class.getCanonicalName());
|
||||
private static final Logger log = Logger.getLogger(RegionDatabaseUtils.class.getCanonicalName());
|
||||
|
||||
private RegionStoreUtils() {
|
||||
private RegionDatabaseUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,14 +62,14 @@ public static void trySetFlagMap(ProtectedRegion region, Map<String, Object> fla
|
||||
|
||||
Object o = flagData.get(flag.getName());
|
||||
if (o != null) {
|
||||
RegionStoreUtils.trySetFlag(region, flag, o);
|
||||
RegionDatabaseUtils.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);
|
||||
RegionDatabaseUtils.trySetFlag(region, flag.getRegionGroupFlag(), o2);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.managers.storage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A driver manages {@link RegionDatabase}s for several worlds. An instance
|
||||
* can return instances of a database for any given world.
|
||||
*
|
||||
* @see RegionDatabase
|
||||
*/
|
||||
public interface RegionDriver {
|
||||
|
||||
/**
|
||||
* Get a region database for a world.
|
||||
*
|
||||
* <p>The given name should be a unique name for the world. Due to
|
||||
* legacy reasons, there are no stipulations on the case sensitivity
|
||||
* of the name. Historically, however, if the driver is a file-based
|
||||
* driver, case-sensitivity will vary on whether the underlying
|
||||
* filesystem is case-sensitive.</p>
|
||||
*
|
||||
* <p>This method should return quickly.</p>
|
||||
*
|
||||
* @param name the name of the world, which may be case sensitive
|
||||
* @return the world
|
||||
*/
|
||||
RegionDatabase get(String name);
|
||||
|
||||
/**
|
||||
* Fetch all the region databases that have been stored using this driver.
|
||||
* Essentially, return a region database for all worlds that have had
|
||||
* regions saved for it in the past.
|
||||
*
|
||||
* <p>As this may require a query to be performed, this method may block
|
||||
* for a prolonged period of time.</p>
|
||||
*
|
||||
* @return a list of databases
|
||||
* @throws StorageException thrown if the fetch operation fails
|
||||
*/
|
||||
List<RegionDatabase> getAll() throws StorageException;
|
||||
|
||||
}
|
@ -22,20 +22,20 @@
|
||||
/**
|
||||
* Exceptions related to region stores inherit from this exception.
|
||||
*/
|
||||
public class RegionStoreException extends Exception {
|
||||
public class StorageException extends Exception {
|
||||
|
||||
public RegionStoreException() {
|
||||
public StorageException() {
|
||||
}
|
||||
|
||||
public RegionStoreException(String message) {
|
||||
public StorageException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public RegionStoreException(String message, Throwable cause) {
|
||||
public StorageException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public RegionStoreException(Throwable cause) {
|
||||
public StorageException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.managers.storage.driver;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A driver is able to create new {@code RegionStore}s for named worlds.
|
||||
*/
|
||||
public interface RegionStoreDriver {
|
||||
|
||||
/**
|
||||
* Get a region store for the named world.
|
||||
*
|
||||
* @param name the name
|
||||
* @return the world
|
||||
* @throws IOException thrown if the region store can't be created due to an I/O error
|
||||
*/
|
||||
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<RegionStore> getAll() throws IOException;
|
||||
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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? is the SQL server online?)");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RegionStore> getAll() throws IOException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
List<RegionStore> stores = new ArrayList<RegionStore>();
|
||||
Connection connection = closer.register(getConnectionPool().getConnection());
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = closer.register(stmt.executeQuery("SELECT name FROM world"));
|
||||
while (rs.next()) {
|
||||
stores.add(get(rs.getString(1)));
|
||||
}
|
||||
return stores;
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Failed to fetch list of worlds", e);
|
||||
} finally {
|
||||
closer.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -17,10 +17,11 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.managers.storage.driver;
|
||||
package com.sk89q.worldguard.protection.managers.storage.file;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.file.YamlFileStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -31,9 +32,9 @@
|
||||
|
||||
/**
|
||||
* Stores region data in a {root_dir}/{id}/{filename} pattern on disk
|
||||
* using {@link YamlFileStore}.
|
||||
* using {@link YamlRegionFile}.
|
||||
*/
|
||||
public class DirectoryYamlDriver implements RegionStoreDriver {
|
||||
public class DirectoryYamlDriver implements RegionDriver {
|
||||
|
||||
private final File rootDir;
|
||||
private final String filename;
|
||||
@ -70,29 +71,23 @@ private File getPath(String id) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegionStore get(String id) throws IOException {
|
||||
public RegionDatabase get(String id) {
|
||||
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(id, file);
|
||||
return new YamlRegionFile(id, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RegionStore> getAll() throws IOException {
|
||||
List<RegionStore> stores = new ArrayList<RegionStore>();
|
||||
public List<RegionDatabase> getAll() throws StorageException {
|
||||
List<RegionDatabase> stores = new ArrayList<RegionDatabase>();
|
||||
|
||||
File files[] = rootDir.listFiles();
|
||||
if (files != null) {
|
||||
for (File dir : files) {
|
||||
if (dir.isDirectory() && new File(dir, "regions.yml").isFile()) {
|
||||
stores.add(new YamlFileStore(dir.getName(), getPath(dir.getName())));
|
||||
stores.add(new YamlRegionFile(dir.getName(), getPath(dir.getName())));
|
||||
}
|
||||
}
|
||||
}
|
@ -29,8 +29,9 @@
|
||||
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.managers.storage.RegionDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDatabaseUtils;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
@ -61,9 +62,9 @@
|
||||
/**
|
||||
* A store that persists regions in a YAML-encoded file.
|
||||
*/
|
||||
public class YamlFileStore implements RegionStore {
|
||||
public class YamlRegionFile implements RegionDatabase {
|
||||
|
||||
private static final Logger log = Logger.getLogger(YamlFileStore.class.getCanonicalName());
|
||||
private static final Logger log = Logger.getLogger(YamlRegionFile.class.getCanonicalName());
|
||||
private static final Yaml ERROR_DUMP_YAML;
|
||||
|
||||
private static final String FILE_HEADER = "#\r\n" +
|
||||
@ -94,17 +95,12 @@ public class YamlFileStore implements RegionStore {
|
||||
*
|
||||
* @param name the name of this store
|
||||
* @param file the file
|
||||
* @throws IOException thrown if the file cannot be written to
|
||||
*/
|
||||
public YamlFileStore(String name, File file) throws IOException {
|
||||
public YamlRegionFile(String name, File file) {
|
||||
checkNotNull(name);
|
||||
checkNotNull(file);
|
||||
this.name = name;
|
||||
this.file = file;
|
||||
if (!file.exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.createNewFile();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -113,7 +109,7 @@ public String getName() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> loadAll() throws IOException {
|
||||
public Set<ProtectedRegion> loadAll() throws StorageException {
|
||||
Map<String, ProtectedRegion> loaded = new HashMap<String, ProtectedRegion>();
|
||||
|
||||
YAMLProcessor config = createYamlProcessor(file);
|
||||
@ -121,6 +117,8 @@ public Set<ProtectedRegion> loadAll() throws IOException {
|
||||
config.load();
|
||||
} catch (FileNotFoundException e) {
|
||||
return new HashSet<ProtectedRegion>(loaded.values());
|
||||
} catch (IOException e) {
|
||||
throw new StorageException("Failed to load region data from '" + file + "'", e);
|
||||
}
|
||||
|
||||
Map<String, YAMLNode> regionData = config.getNodes("regions");
|
||||
@ -183,13 +181,13 @@ public Set<ProtectedRegion> loadAll() throws IOException {
|
||||
}
|
||||
|
||||
// Relink parents
|
||||
RegionStoreUtils.relinkParents(loaded, parentSets);
|
||||
RegionDatabaseUtils.relinkParents(loaded, parentSets);
|
||||
|
||||
return new HashSet<ProtectedRegion>(loaded.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll(Set<ProtectedRegion> regions) throws IOException {
|
||||
public void saveAll(Set<ProtectedRegion> regions) throws StorageException {
|
||||
checkNotNull(regions);
|
||||
|
||||
File tempFile = new File(file.getParentFile(), file.getName() + ".tmp");
|
||||
@ -248,7 +246,7 @@ public void saveAll(Set<ProtectedRegion> regions) throws IOException {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.delete();
|
||||
if (!tempFile.renameTo(file)) {
|
||||
throw new IOException("Failed to rename temporary regions file to " + file.getAbsolutePath());
|
||||
throw new StorageException("Failed to rename temporary regions file to " + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,7 +295,7 @@ private Map<String, Object> getFlagData(ProtectedRegion region) {
|
||||
|
||||
private void setFlags(ProtectedRegion region, YAMLNode flagsData) {
|
||||
if (flagsData != null) {
|
||||
RegionStoreUtils.trySetFlagMap(region, flagsData.getMap());
|
||||
RegionDatabaseUtils.trySetFlagMap(region, flagsData.getMap());
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
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.managers.storage.RegionDatabaseUtils;
|
||||
import com.sk89q.worldguard.protection.regions.GlobalProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedPolygonalRegion;
|
||||
@ -62,9 +62,9 @@ class DataLoader {
|
||||
|
||||
private final Map<String, ProtectedRegion> loaded = new HashMap<String, ProtectedRegion>();
|
||||
private final Map<ProtectedRegion, String> parentSets = new HashMap<ProtectedRegion, String>();
|
||||
private final Yaml yaml = SQLRegionStore.createYaml();
|
||||
private final Yaml yaml = SQLRegionDatabase.createYaml();
|
||||
|
||||
DataLoader(SQLRegionStore regionStore, Connection conn) throws SQLException {
|
||||
DataLoader(SQLRegionDatabase regionStore, Connection conn) throws SQLException {
|
||||
checkNotNull(regionStore);
|
||||
|
||||
this.conn = conn;
|
||||
@ -81,7 +81,7 @@ public Set<ProtectedRegion> load() throws SQLException {
|
||||
loadDomainUsers();
|
||||
loadDomainGroups();
|
||||
|
||||
RegionStoreUtils.relinkParents(loaded, parentSets);
|
||||
RegionDatabaseUtils.relinkParents(loaded, parentSets);
|
||||
|
||||
return new HashSet<ProtectedRegion>(loaded.values());
|
||||
}
|
||||
@ -238,7 +238,7 @@ private void loadFlags() throws SQLException {
|
||||
for (Map.Entry<String, Map<String, Object>> entry : data.rowMap().entrySet()) {
|
||||
ProtectedRegion region = loaded.get(entry.getKey());
|
||||
if (region != null) {
|
||||
RegionStoreUtils.trySetFlagMap(region, entry.getValue());
|
||||
RegionDatabaseUtils.trySetFlagMap(region, entry.getValue());
|
||||
} else {
|
||||
throw new RuntimeException("An unexpected error occurred (loaded.get() returned null)");
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class DataUpdater {
|
||||
final int worldId;
|
||||
final DomainTableCache domainTableCache;
|
||||
|
||||
DataUpdater(SQLRegionStore regionStore, Connection conn) throws SQLException {
|
||||
DataUpdater(SQLRegionDatabase regionStore, Connection conn) throws SQLException {
|
||||
checkNotNull(regionStore);
|
||||
|
||||
this.conn = conn;
|
||||
|
@ -86,7 +86,7 @@ private void insertRegionTypes() throws SQLException {
|
||||
for (ProtectedRegion region : partition) {
|
||||
stmt.setString(1, region.getId());
|
||||
stmt.setInt(2, worldId);
|
||||
stmt.setString(3, SQLRegionStore.getRegionTypeName(region));
|
||||
stmt.setString(3, SQLRegionDatabase.getRegionTypeName(region));
|
||||
stmt.setInt(4, region.getPriority());
|
||||
stmt.addBatch();
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ class RegionUpdater {
|
||||
private final Set<UUID> userUuids = new HashSet<UUID>();
|
||||
private final Set<String> groupNames = new HashSet<String>();
|
||||
|
||||
private final Yaml yaml = SQLRegionStore.createYaml();
|
||||
private final Yaml yaml = SQLRegionDatabase.createYaml();
|
||||
|
||||
private final List<ProtectedRegion> typesToUpdate = new ArrayList<ProtectedRegion>();
|
||||
private final List<ProtectedRegion> parentsToSet = new ArrayList<ProtectedRegion>();
|
||||
@ -308,7 +308,7 @@ private void updateRegionTypes() throws SQLException {
|
||||
|
||||
for (List<ProtectedRegion> partition : Lists.partition(typesToUpdate, StatementBatch.MAX_BATCH_SIZE)) {
|
||||
for (ProtectedRegion region : partition) {
|
||||
stmt.setString(1, SQLRegionStore.getRegionTypeName(region));
|
||||
stmt.setString(1, SQLRegionDatabase.getRegionTypeName(region));
|
||||
stmt.setInt(2, region.getPriority());
|
||||
stmt.setString(3, region.getId());
|
||||
stmt.addBatch();
|
||||
|
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.managers.storage.sql;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDriver;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
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 java.sql.Connection;
|
||||
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.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Stores regions using a JDBC connection with support for SQL.
|
||||
*
|
||||
* <p>Note, however, that this implementation <strong>only supports MySQL.
|
||||
* </strong></p>
|
||||
*/
|
||||
public class SQLDriver implements RegionDriver {
|
||||
|
||||
private static final Logger log = Logger.getLogger(SQLDriver.class.getCanonicalName());
|
||||
private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool();
|
||||
private static final int CONNECTION_TIMEOUT = 6000;
|
||||
|
||||
private final DataSourceConfig config;
|
||||
private boolean initialized = false;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param config a configuration
|
||||
*/
|
||||
public SQLDriver(DataSourceConfig config) {
|
||||
checkNotNull(config);
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegionDatabase get(String name) {
|
||||
return new SQLRegionDatabase(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RegionDatabase> getAll() throws StorageException {
|
||||
Closer closer = Closer.create();
|
||||
try {
|
||||
List<RegionDatabase> stores = new ArrayList<RegionDatabase>();
|
||||
Connection connection = closer.register(getConnection());
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet rs = closer.register(stmt.executeQuery("SELECT name FROM world"));
|
||||
while (rs.next()) {
|
||||
stores.add(get(rs.getString(1)));
|
||||
}
|
||||
return stores;
|
||||
} catch (SQLException e) {
|
||||
throw new StorageException("Failed to fetch list of worlds", e);
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform initialization if it hasn't been (successfully) performed yet.
|
||||
*
|
||||
* @throws StorageException thrown on error
|
||||
*/
|
||||
synchronized void initialize() throws StorageException {
|
||||
if (!initialized) {
|
||||
try {
|
||||
migrate();
|
||||
} catch (SQLException e) {
|
||||
throw new StorageException("Failed to migrate database tables", e);
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to migrate the tables to the latest version.
|
||||
*
|
||||
* @throws StorageException thrown if migration fails
|
||||
* @throws SQLException thrown on SQL error
|
||||
*/
|
||||
private void migrate() throws SQLException, StorageException {
|
||||
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 StorageException(
|
||||
"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<String, String> placeHolders = new HashMap<String, String>();
|
||||
placeHolders.put("tablePrefix", config.getTablePrefix());
|
||||
|
||||
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(config.getDsn(), config.getUsername(), config.getPassword());
|
||||
flyway.setTable(config.getTablePrefix() + "migrations");
|
||||
flyway.setPlaceholders(placeHolders);
|
||||
flyway.setValidateOnMigrate(false);
|
||||
flyway.migrate();
|
||||
} catch (FlywayException e) {
|
||||
throw new StorageException("Failed to migrate tables", e);
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the folder in migrations/region containing the migration files.
|
||||
*
|
||||
* @return the migration folder name
|
||||
*/
|
||||
public String getMigrationFolderName() {
|
||||
return "mysql";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 database configuration.
|
||||
*
|
||||
* @return the database configuration
|
||||
*/
|
||||
DataSourceConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new connection.
|
||||
*
|
||||
* @return the connection
|
||||
* @throws SQLException raised if the connection cannot be instantiated
|
||||
*/
|
||||
Connection getConnection() throws SQLException {
|
||||
Future<Connection> future = EXECUTOR.submit(new Callable<Connection>() {
|
||||
@Override
|
||||
public Connection call() throws Exception {
|
||||
return config.getConnection();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
return future.get(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new SQLException("Failed to get a SQL connection because the operation was interrupted", e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new SQLException("Failed to get a SQL connection due to an error", e);
|
||||
} catch (TimeoutException e) {
|
||||
future.cancel(true);
|
||||
throw new SQLException("Failed to get a SQL connection within the time limit");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.managers.storage.sql;
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.RegionDifference;
|
||||
import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionDatabase;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
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.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.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Stores region data into a SQL database in a highly normalized fashion.
|
||||
*/
|
||||
class SQLRegionDatabase implements RegionDatabase {
|
||||
|
||||
private final String worldName;
|
||||
private final DataSourceConfig config;
|
||||
private final SQLDriver driver;
|
||||
private int worldId;
|
||||
private boolean initialized = false;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param driver the driver instance
|
||||
* @param worldName the name of the world to store regions by
|
||||
*/
|
||||
SQLRegionDatabase(SQLDriver driver, String worldName) {
|
||||
checkNotNull(driver);
|
||||
checkNotNull(worldName);
|
||||
|
||||
this.config = driver.getConfig();
|
||||
this.worldName = worldName;
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return worldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the database if it hasn't been yet initialized.
|
||||
*
|
||||
* @throws StorageException thrown if initialization fails
|
||||
*/
|
||||
private synchronized void initialize() throws StorageException {
|
||||
if (!initialized) {
|
||||
driver.initialize();
|
||||
|
||||
try {
|
||||
worldId = chooseWorldId(worldName);
|
||||
} catch (SQLException e) {
|
||||
throw new StorageException("Failed to choose the ID for this world", e);
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 driver.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> loadAll() throws StorageException {
|
||||
initialize();
|
||||
|
||||
Closer closer = Closer.create();
|
||||
DataLoader loader;
|
||||
|
||||
try {
|
||||
try {
|
||||
loader = new DataLoader(this, closer.register(getConnection()));
|
||||
} catch (SQLException e) {
|
||||
throw new StorageException("Failed to get a connection to the database", e);
|
||||
}
|
||||
|
||||
try {
|
||||
return loader.load();
|
||||
} catch (SQLException e) {
|
||||
throw new StorageException("Failed to save the region data to the database", e);
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll(Set<ProtectedRegion> regions) throws StorageException {
|
||||
checkNotNull(regions);
|
||||
|
||||
initialize();
|
||||
|
||||
Closer closer = Closer.create();
|
||||
DataUpdater updater;
|
||||
|
||||
try {
|
||||
try {
|
||||
updater = new DataUpdater(this, closer.register(getConnection()));
|
||||
} catch (SQLException e) {
|
||||
throw new StorageException("Failed to get a connection to the database", e);
|
||||
}
|
||||
|
||||
try {
|
||||
updater.saveAll(regions);
|
||||
} catch (SQLException e) {
|
||||
throw new StorageException("Failed to save the region data to the database", e);
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveChanges(RegionDifference difference) throws DifferenceSaveException, StorageException {
|
||||
checkNotNull(difference);
|
||||
|
||||
initialize();
|
||||
|
||||
Closer closer = Closer.create();
|
||||
DataUpdater updater;
|
||||
|
||||
try {
|
||||
try {
|
||||
updater = new DataUpdater(this, closer.register(getConnection()));
|
||||
} catch (SQLException e) {
|
||||
throw new StorageException("Failed to get a connection to the database", e);
|
||||
}
|
||||
|
||||
try {
|
||||
updater.saveChanges(difference.getChanged(), difference.getRemoved());
|
||||
} catch (SQLException e) {
|
||||
throw new StorageException("Failed to save the region data to the database", e);
|
||||
}
|
||||
} finally {
|
||||
closer.closeQuietly();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,399 +0,0 @@
|
||||
/*
|
||||
* WorldGuard, a suite of tools for Minecraft
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
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 String name;
|
||||
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.name = 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String, String> placeHolders = new HashMap<String, String>();
|
||||
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 {
|
||||
Future<Connection> future = connectionPool.getAsyncConnection();
|
||||
try {
|
||||
return future.get(10, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
future.cancel(true);
|
||||
throw new SQLException("Failed to get connection -- interrupted");
|
||||
} catch (ExecutionException e) {
|
||||
future.cancel(true);
|
||||
throw new SQLException("Failed to get connection; an error occurred", e);
|
||||
} catch (TimeoutException e) {
|
||||
future.cancel(true);
|
||||
throw new SQLException("Failed to get connection; took too long to get a valid SQL connection (is the database server down?)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<ProtectedRegion> 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<ProtectedRegion> 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,9 @@
|
||||
|
||||
package com.sk89q.worldguard.util.sql;
|
||||
|
||||
import com.jolbox.bonecp.BoneCPConfig;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@ -130,16 +132,13 @@ public DataSourceConfig setTablePrefix(String tablePrefix) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new BoneCP configuration object.
|
||||
* Create a new connection.
|
||||
*
|
||||
* @return a new configuration object
|
||||
* @return the new connection
|
||||
* @throws SQLException raised if the connection cannot be instantiated
|
||||
*/
|
||||
public BoneCPConfig createBoneCPConfig() {
|
||||
BoneCPConfig config = new BoneCPConfig();
|
||||
config.setJdbcUrl(dsn);
|
||||
config.setUsername(username);
|
||||
config.setPassword(password);
|
||||
return config;
|
||||
public Connection getConnection() throws SQLException {
|
||||
return DriverManager.getConnection(dsn, username, password);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,13 +21,13 @@
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.index.HashMapIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionDatabase;
|
||||
|
||||
public class HashMapIndexPriorityTest extends RegionPriorityTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new RegionManager(new MemoryRegionStore(), new HashMapIndex.Factory());
|
||||
return new RegionManager(new MemoryRegionDatabase(), new HashMapIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,13 +21,13 @@
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.index.HashMapIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionDatabase;
|
||||
|
||||
public class HashMapIndexRegionOverlapTest extends RegionOverlapTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new RegionManager(new MemoryRegionStore(), new HashMapIndex.Factory());
|
||||
return new RegionManager(new MemoryRegionDatabase(), new HashMapIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,13 +21,13 @@
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.index.HashMapIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionDatabase;
|
||||
|
||||
public class HashMapIndexTest extends RegionOverlapTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new RegionManager(new MemoryRegionStore(), new HashMapIndex.Factory());
|
||||
return new RegionManager(new MemoryRegionDatabase(), new HashMapIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -78,11 +78,11 @@ public ProtectedRegion add(int priority, ProtectedRegion parent)
|
||||
}
|
||||
|
||||
public ApplicableRegionSet getApplicableSetInWilderness() {
|
||||
return new ApplicableRegionSet(Collections.<ProtectedRegion>emptyList(), global);
|
||||
return new RegionResultSet(Collections.<ProtectedRegion>emptyList(), global);
|
||||
}
|
||||
|
||||
public ApplicableRegionSet getApplicableSet() {
|
||||
return new ApplicableRegionSet(regions, global);
|
||||
return new RegionResultSet(regions, global);
|
||||
}
|
||||
|
||||
public FlagValueCalculator getFlagCalculator() {
|
||||
|
@ -21,13 +21,13 @@
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionDatabase;
|
||||
|
||||
public class PriorityRTreeIndexTest extends RegionOverlapTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory());
|
||||
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,13 +21,13 @@
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionDatabase;
|
||||
|
||||
public class PriorityRTreeRegionEntryExitTest extends RegionEntryExitTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory());
|
||||
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,13 +21,13 @@
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionDatabase;
|
||||
|
||||
public class PriorityRTreeRegionOverlapTest extends RegionOverlapTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory());
|
||||
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,13 +21,13 @@
|
||||
|
||||
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionStore;
|
||||
import com.sk89q.worldguard.protection.managers.storage.MemoryRegionDatabase;
|
||||
|
||||
public class PriorityRTreeRegionPriorityTest extends RegionPriorityTest {
|
||||
|
||||
@Override
|
||||
protected RegionManager createRegionManager() throws Exception {
|
||||
return new RegionManager(new MemoryRegionStore(), new PriorityRTreeIndex.Factory());
|
||||
return new RegionManager(new MemoryRegionDatabase(), new PriorityRTreeIndex.Factory());
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user