mirror of
https://github.com/EngineHub/WorldGuard.git
synced 2024-11-24 03:25:24 +01:00
Add a hash table index for regions that hashes on chunk coordinates.
This commit is contained in:
parent
eb23e28c16
commit
38587a1c61
@ -19,6 +19,7 @@
|
||||
|
||||
package com.sk89q.worldguard.bukkit.listener;
|
||||
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
import com.sk89q.worldguard.bukkit.event.block.BreakBlockEvent;
|
||||
import com.sk89q.worldguard.bukkit.event.block.PlaceBlockEvent;
|
||||
@ -30,13 +31,19 @@
|
||||
import com.sk89q.worldguard.bukkit.util.Materials;
|
||||
import com.sk89q.worldguard.bukkit.util.RegionQuery;
|
||||
import com.sk89q.worldguard.protection.flags.DefaultFlag;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
import org.bukkit.event.world.WorldLoadEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
|
||||
/**
|
||||
* Handle events that need to be processed by region protection.
|
||||
@ -56,6 +63,34 @@ private void tellErrorMessage(CommandSender sender, Object subject) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "You don't have permission for this area.");
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldLoad(WorldLoadEvent event) {
|
||||
getPlugin().getGlobalRegionManager().load(event.getWorld());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldUnload(WorldUnloadEvent event) {
|
||||
getPlugin().getGlobalRegionManager().unload(event.getWorld());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onChunkLoad(ChunkLoadEvent event) {
|
||||
RegionManager manager = getPlugin().getGlobalRegionManager().get(event.getWorld());
|
||||
if (manager != null) {
|
||||
Chunk chunk = event.getChunk();
|
||||
manager.loadChunk(new Vector2D(chunk.getX(), chunk.getZ()));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onChunkUnload(ChunkUnloadEvent event) {
|
||||
RegionManager manager = getPlugin().getGlobalRegionManager().get(event.getWorld());
|
||||
if (manager != null) {
|
||||
Chunk chunk = event.getChunk();
|
||||
manager.unloadChunk(new Vector2D(chunk.getX(), chunk.getZ()));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPlaceBlock(PlaceBlockEvent event) {
|
||||
Player player = event.getCause().getPlayerRootCause();
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
package com.sk89q.worldguard.protection;
|
||||
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.bukkit.BukkitUtil;
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
@ -26,14 +27,17 @@
|
||||
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.sk89q.worldguard.bukkit.BukkitUtil.toVector;
|
||||
|
||||
public class GlobalRegionManager {
|
||||
@ -50,7 +54,16 @@ public GlobalRegionManager(WorldGuardPlugin plugin) {
|
||||
|
||||
@Nullable
|
||||
public RegionManager load(World world) {
|
||||
return container.load(world.getName());
|
||||
checkNotNull(world);
|
||||
RegionManager manager = container.load(world.getName());
|
||||
if (manager != null) {
|
||||
List<Vector2D> positions = new ArrayList<Vector2D>();
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
positions.add(new Vector2D(chunk.getX(), chunk.getZ()));
|
||||
}
|
||||
manager.loadChunks(positions);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
public void preload() {
|
||||
@ -59,6 +72,10 @@ public void preload() {
|
||||
}
|
||||
}
|
||||
|
||||
public void unload(World world) {
|
||||
unload(world.getName());
|
||||
}
|
||||
|
||||
public void unload(String name) {
|
||||
container.unload(name);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
import com.google.common.base.Supplier;
|
||||
import com.sk89q.worldguard.bukkit.ConfigurationManager;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.managers.index.ChunkHashTable;
|
||||
import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex;
|
||||
import com.sk89q.worldguard.protection.managers.index.PriorityRTreeIndex;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
@ -57,7 +58,7 @@ class ManagerContainer {
|
||||
private final Object lock = new Object();
|
||||
private final EnumMap<DriverType, RegionStoreDriver> drivers = new EnumMap<DriverType, RegionStoreDriver>(DriverType.class);
|
||||
private final RegionStoreDriver defaultDriver;
|
||||
private final Supplier<? extends ConcurrentRegionIndex> indexFactory = new PriorityRTreeIndex.Factory();
|
||||
private final Supplier<? extends ConcurrentRegionIndex> indexFactory = new ChunkHashTable.Factory(new PriorityRTreeIndex.Factory());
|
||||
private final Timer timer = new Timer();
|
||||
|
||||
ManagerContainer(ConfigurationManager config) {
|
||||
|
@ -22,6 +22,7 @@
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldguard.LocalPlayer;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex;
|
||||
@ -29,6 +30,7 @@
|
||||
import com.sk89q.worldguard.protection.managers.storage.DifferenceSaveException;
|
||||
import com.sk89q.worldguard.protection.managers.storage.RegionStore;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.util.RegionCollectionConsumer;
|
||||
import com.sk89q.worldguard.util.Normal;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -113,6 +115,33 @@ public void saveChanges() throws IOException {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the regions for a chunk.
|
||||
*
|
||||
* @param position the position
|
||||
*/
|
||||
public void loadChunk(Vector2D position) {
|
||||
index.bias(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the regions for a chunk.
|
||||
*
|
||||
* @param positions a collection of positions
|
||||
*/
|
||||
public void loadChunks(Collection<Vector2D> positions) {
|
||||
index.biasAll(positions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload the regions for a chunk.
|
||||
*
|
||||
* @param position the position
|
||||
*/
|
||||
public void unloadChunk(Vector2D position) {
|
||||
index.forget(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmodifiable map of regions containing the state of the
|
||||
* index at the time of call.
|
||||
|
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* 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.index;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.sk89q.odeum.concurrent.EvenMoreExecutors;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldguard.protection.managers.RegionDifference;
|
||||
import com.sk89q.worldguard.protection.managers.RemovalStrategy;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
import com.sk89q.worldguard.protection.util.RegionCollectionConsumer;
|
||||
import com.sk89q.worldguard.util.collect.LongHashTable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Maintains a hash table for each chunk containing a list of regions that
|
||||
* are contained within that chunk, allowing for fast spatial lookup.
|
||||
*/
|
||||
public class ChunkHashTable implements ConcurrentRegionIndex {
|
||||
|
||||
private ListeningExecutorService executor = createExecutor();
|
||||
private LongHashTable<ChunkState> states = new LongHashTable<ChunkState>();
|
||||
private final RegionIndex index;
|
||||
private final Object lock = new Object();
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param index the index
|
||||
*/
|
||||
public ChunkHashTable(RegionIndex index) {
|
||||
checkNotNull(index);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an executor.
|
||||
*
|
||||
* @return an executor service
|
||||
*/
|
||||
private ListeningExecutorService createExecutor() {
|
||||
return MoreExecutors.listeningDecorator(
|
||||
EvenMoreExecutors.newBoundedCachedThreadPool(0, 4, Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a state object at the given position.
|
||||
*
|
||||
* @param position the position
|
||||
* @param create true to create an entry if one does not exist
|
||||
* @return a chunk state object, or {@code null} (only if {@code create} is false)
|
||||
*/
|
||||
@Nullable
|
||||
private ChunkState get(Vector2D position, boolean create) {
|
||||
ChunkState state;
|
||||
synchronized (lock) {
|
||||
state = states.get(position.getBlockX(), position.getBlockZ());
|
||||
if (state == null && create) {
|
||||
state = new ChunkState(position);
|
||||
states.put(position.getBlockX(), position.getBlockZ(), state);
|
||||
executor.submit(new EnumerateRegions(position));
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a state at the given position or create a new entry if one does
|
||||
* not exist.
|
||||
*
|
||||
* @param position the position
|
||||
* @return a state
|
||||
*/
|
||||
private ChunkState getOrCreate(Vector2D position) {
|
||||
return get(position, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the current hash table and rebuild it in the background.
|
||||
*/
|
||||
private void rebuild() {
|
||||
synchronized (lock) {
|
||||
ListeningExecutorService previousExecutor = executor;
|
||||
LongHashTable<ChunkState> previousStates = states;
|
||||
|
||||
previousExecutor.shutdownNow();
|
||||
states = new LongHashTable<ChunkState>();
|
||||
executor = createExecutor();
|
||||
|
||||
List<Vector2D> positions = new ArrayList<Vector2D>();
|
||||
for (ChunkState state : previousStates.values()) {
|
||||
Vector2D position = state.getPosition();
|
||||
positions.add(position);
|
||||
states.put(position.getBlockX(), position.getBlockZ(), new ChunkState(position));
|
||||
}
|
||||
|
||||
if (!positions.isEmpty()) {
|
||||
executor.submit(new EnumerateRegions(positions));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bias(Vector2D chunkPosition) {
|
||||
checkNotNull(chunkPosition);
|
||||
getOrCreate(chunkPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void biasAll(Collection<Vector2D> chunkPositions) {
|
||||
synchronized (lock) {
|
||||
for (Vector2D position : chunkPositions) {
|
||||
bias(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forget(Vector2D chunkPosition) {
|
||||
checkNotNull(chunkPosition);
|
||||
synchronized (lock) {
|
||||
states.remove(chunkPosition.getBlockX(), chunkPosition.getBlockZ());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forgetAll() {
|
||||
synchronized (lock) {
|
||||
executor.shutdownNow();
|
||||
states = new LongHashTable<ChunkState>();
|
||||
executor = createExecutor();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ProtectedRegion region) {
|
||||
index.add(region);
|
||||
rebuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(Collection<ProtectedRegion> regions) {
|
||||
index.addAll(regions);
|
||||
rebuild();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProtectedRegion> remove(String id, RemovalStrategy strategy) {
|
||||
Set<ProtectedRegion> removed = index.remove(id, strategy);
|
||||
rebuild();
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String id) {
|
||||
return index.contains(id);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ProtectedRegion get(String id) {
|
||||
return index.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Predicate<ProtectedRegion> consumer) {
|
||||
index.apply(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyContaining(Vector position, Predicate<ProtectedRegion> consumer) {
|
||||
checkNotNull(position);
|
||||
checkNotNull(consumer);
|
||||
|
||||
ChunkState state = get(new Vector2D(position.getBlockX() >> 4, position.getBlockZ() >> 4), false);
|
||||
if (state != null && state.isLoaded()) {
|
||||
for (ProtectedRegion region : state.getRegions()) {
|
||||
if (region.contains(position)) {
|
||||
consumer.apply(region);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
index.applyContaining(position, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyIntersecting(ProtectedRegion region, Predicate<ProtectedRegion> consumer) {
|
||||
index.applyIntersecting(region, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return index.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegionDifference getAndClearDifference() {
|
||||
return index.getAndClearDifference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ProtectedRegion> values() {
|
||||
return index.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return index.isDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirty(boolean dirty) {
|
||||
index.setDirty(dirty);
|
||||
}
|
||||
|
||||
/**
|
||||
* A task to enumerate the regions for a list of provided chunks.
|
||||
*/
|
||||
private class EnumerateRegions implements Runnable {
|
||||
private final List<Vector2D> positions;
|
||||
|
||||
private EnumerateRegions(Vector2D position) {
|
||||
this(Arrays.asList(checkNotNull(position)));
|
||||
}
|
||||
|
||||
private EnumerateRegions(List<Vector2D> positions) {
|
||||
checkNotNull(positions);
|
||||
checkArgument(!positions.isEmpty(), "List of positions can't be empty");
|
||||
this.positions = positions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (Vector2D position : positions) {
|
||||
ChunkState state = get(position, false);
|
||||
|
||||
if (state != null) {
|
||||
List<ProtectedRegion> regions = new ArrayList<ProtectedRegion>();
|
||||
ProtectedRegion chunkRegion = new ProtectedCuboidRegion(
|
||||
"_",
|
||||
position.toVector(0).toBlockVector(),
|
||||
position.add(16, 16).toVector(Integer.MAX_VALUE).toBlockVector());
|
||||
index.applyIntersecting(chunkRegion, new RegionCollectionConsumer(regions, false));
|
||||
|
||||
state.setRegions(Collections.unmodifiableList(regions));
|
||||
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a cache of region data for a chunk.
|
||||
*/
|
||||
private class ChunkState {
|
||||
private final Vector2D position;
|
||||
private boolean loaded = false;
|
||||
private List<ProtectedRegion> regions = Collections.emptyList();
|
||||
|
||||
private ChunkState(Vector2D position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public Vector2D getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public List<ProtectedRegion> getRegions() {
|
||||
return regions;
|
||||
}
|
||||
|
||||
public void setRegions(List<ProtectedRegion> regions) {
|
||||
this.regions = regions;
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory for instances of {@code ChunkHashCache}.
|
||||
*/
|
||||
public static class Factory implements Supplier<ChunkHashTable> {
|
||||
private final Supplier<? extends ConcurrentRegionIndex> supplier;
|
||||
|
||||
public Factory(Supplier<? extends ConcurrentRegionIndex> supplier) {
|
||||
checkNotNull(supplier);
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkHashTable get() {
|
||||
return new ChunkHashTable(supplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldguard.protection.managers.RegionDifference;
|
||||
import com.sk89q.worldguard.protection.managers.RemovalStrategy;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
@ -102,6 +103,26 @@ public void addAll(Collection<ProtectedRegion> regions) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bias(Vector2D chunkPosition) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void biasAll(Collection<Vector2D> chunkPositions) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forget(Vector2D chunkPosition) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forgetAll() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ProtectedRegion region) {
|
||||
synchronized (lock) {
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldguard.protection.managers.RegionDifference;
|
||||
import com.sk89q.worldguard.protection.managers.RemovalStrategy;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
@ -39,6 +40,37 @@
|
||||
*/
|
||||
public interface RegionIndex extends ChangeTracked {
|
||||
|
||||
/**
|
||||
* Bias the given chunk for faster lookups (put it in a hash table, etc.).
|
||||
*
|
||||
* <p>Implementations may choose to do nothing.</p>
|
||||
*
|
||||
* @param chunkPosition the chunk position
|
||||
*/
|
||||
void bias(Vector2D chunkPosition);
|
||||
|
||||
/**
|
||||
* Bias the given chunk for faster lookups (put it in a hash table, etc.).
|
||||
*
|
||||
* <p>Implementations may choose to do nothing.</p>
|
||||
*
|
||||
* @param chunkPosition the chunk position
|
||||
*/
|
||||
void biasAll(Collection<Vector2D> chunkPosition);
|
||||
|
||||
/**
|
||||
* No longer bias the given chunk for faster lookup.
|
||||
*
|
||||
* @param chunkPosition the chunk position
|
||||
*/
|
||||
void forget(Vector2D chunkPosition);
|
||||
|
||||
/**
|
||||
* Clearly all extra cache data created by any calls to
|
||||
* {@link #bias(Vector2D)}.
|
||||
*/
|
||||
void forgetAll();
|
||||
|
||||
/**
|
||||
* Add a region to this index, replacing any existing one with the same
|
||||
* name (equality determined using {@link Normal}).
|
||||
|
@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldguard.protection.managers;
|
||||
package com.sk89q.worldguard.protection.util;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
|
||||
@ -33,7 +33,7 @@
|
||||
* to the collection, although it may result in duplicates in the collection
|
||||
* if the collection is not a set.</p>
|
||||
*/
|
||||
class RegionCollectionConsumer implements Predicate<ProtectedRegion> {
|
||||
public class RegionCollectionConsumer implements Predicate<ProtectedRegion> {
|
||||
|
||||
private final Collection<ProtectedRegion> collection;
|
||||
private final boolean addParents;
|
||||
@ -44,7 +44,7 @@ class RegionCollectionConsumer implements Predicate<ProtectedRegion> {
|
||||
* @param collection the collection to add regions to
|
||||
* @param addParents true to also add the parents to the collection
|
||||
*/
|
||||
RegionCollectionConsumer(Collection<ProtectedRegion> collection, boolean addParents) {
|
||||
public RegionCollectionConsumer(Collection<ProtectedRegion> collection, boolean addParents) {
|
||||
checkNotNull(collection);
|
||||
|
||||
this.collection = collection;
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.util.collect;
|
||||
|
||||
public class EntryBase {
|
||||
|
||||
protected long key;
|
||||
|
||||
public EntryBase(long key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.util.collect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class LongBaseHashTable extends LongHash {
|
||||
|
||||
EntryBase[][][] values = new EntryBase[256][][];
|
||||
EntryBase cache = null;
|
||||
|
||||
public void put(int msw, int lsw, EntryBase entry) {
|
||||
put(entry);
|
||||
}
|
||||
|
||||
public EntryBase getEntry(int msw, int lsw) {
|
||||
return getEntry(toLong(msw, lsw));
|
||||
}
|
||||
|
||||
public synchronized void put(EntryBase entry) {
|
||||
int mainIdx = (int) (entry.key & 255);
|
||||
EntryBase[][] outer = this.values[mainIdx];
|
||||
if (outer == null) this.values[mainIdx] = outer = new EntryBase[256][];
|
||||
|
||||
int outerIdx = (int) ((entry.key >> 32) & 255);
|
||||
EntryBase[] inner = outer[outerIdx];
|
||||
|
||||
if (inner == null) {
|
||||
outer[outerIdx] = inner = new EntryBase[5];
|
||||
inner[0] = this.cache = entry;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < inner.length; i++) {
|
||||
if (inner[i] == null || inner[i].key == entry.key) {
|
||||
inner[i] = this.cache = entry;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
outer[outerIdx] = inner = Arrays.copyOf(inner, i + i);
|
||||
inner[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized EntryBase getEntry(long key) {
|
||||
return containsKey(key) ? cache : null;
|
||||
}
|
||||
|
||||
public synchronized boolean containsKey(long key) {
|
||||
if (this.cache != null && cache.key == key) return true;
|
||||
|
||||
int outerIdx = (int) ((key >> 32) & 255);
|
||||
EntryBase[][] outer = this.values[(int) (key & 255)];
|
||||
if (outer == null) return false;
|
||||
|
||||
EntryBase[] inner = outer[outerIdx];
|
||||
if (inner == null) return false;
|
||||
|
||||
for (int i = 0; i < inner.length; i++) {
|
||||
EntryBase e = inner[i];
|
||||
if (e == null) {
|
||||
return false;
|
||||
} else if (e.key == key) {
|
||||
this.cache = e;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized void remove(long key) {
|
||||
EntryBase[][] outer = this.values[(int) (key & 255)];
|
||||
if (outer == null) return;
|
||||
|
||||
EntryBase[] inner = outer[(int) ((key >> 32) & 255)];
|
||||
if (inner == null) return;
|
||||
|
||||
for (int i = 0; i < inner.length; i++) {
|
||||
if (inner[i] == null) continue;
|
||||
|
||||
if (inner[i].key == key) {
|
||||
for (i++; i < inner.length; i++) {
|
||||
if (inner[i] == null) break;
|
||||
inner[i - 1] = inner[i];
|
||||
}
|
||||
|
||||
inner[i-1] = null;
|
||||
this.cache = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized ArrayList<EntryBase> entries() {
|
||||
ArrayList<EntryBase> ret = new ArrayList<EntryBase>();
|
||||
|
||||
for (EntryBase[][] outer : this.values) {
|
||||
if (outer == null) continue;
|
||||
|
||||
for (EntryBase[] inner : outer) {
|
||||
if (inner == null) continue;
|
||||
|
||||
for (EntryBase entry : inner) {
|
||||
if (entry == null) break;
|
||||
|
||||
ret.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.util.collect;
|
||||
|
||||
public abstract class LongHash {
|
||||
|
||||
public static long toLong(int msw, int lsw) {
|
||||
return ((long) msw << 32) + lsw - Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
public static int msw(long l) {
|
||||
return (int) (l >> 32);
|
||||
}
|
||||
|
||||
public static int lsw(long l) {
|
||||
return (int) (l & 0xFFFFFFFF) + Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
public boolean containsKey(int msw, int lsw) {
|
||||
return containsKey(toLong(msw, lsw));
|
||||
}
|
||||
|
||||
public void remove(int msw, int lsw) {
|
||||
remove(toLong(msw, lsw));
|
||||
}
|
||||
|
||||
public abstract boolean containsKey(long key);
|
||||
|
||||
public abstract void remove(long key);
|
||||
|
||||
}
|
199
src/main/java/com/sk89q/worldguard/util/collect/LongHashSet.java
Normal file
199
src/main/java/com/sk89q/worldguard/util/collect/LongHashSet.java
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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.util.collect;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
|
||||
public class LongHashSet extends LongHash {
|
||||
|
||||
protected long[][][] values = new long[256][][];
|
||||
protected int count = 0;
|
||||
protected ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
|
||||
protected ReadLock rl = rwl.readLock();
|
||||
protected WriteLock wl = rwl.writeLock();
|
||||
|
||||
public boolean isEmpty() {
|
||||
rl.lock();
|
||||
try {
|
||||
return this.count == 0;
|
||||
} finally {
|
||||
rl.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void add(int msw, int lsw) {
|
||||
add(toLong(msw, lsw));
|
||||
}
|
||||
|
||||
public void add(long key) {
|
||||
wl.lock();
|
||||
try {
|
||||
int mainIdx = (int) (key & 255);
|
||||
long outer[][] = this.values[mainIdx];
|
||||
if (outer == null) this.values[mainIdx] = outer = new long[256][];
|
||||
|
||||
int outerIdx = (int) ((key >> 32) & 255);
|
||||
long inner[] = outer[outerIdx];
|
||||
|
||||
if (inner == null) {
|
||||
synchronized (this) {
|
||||
outer[outerIdx] = inner = new long[1];
|
||||
inner[0] = key;
|
||||
this.count++;
|
||||
}
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < inner.length; i++) {
|
||||
if (inner[i] == key) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
inner = Arrays.copyOf(inner, i + 1);
|
||||
outer[outerIdx] = inner;
|
||||
inner[i] = key;
|
||||
this.count++;
|
||||
}
|
||||
} finally {
|
||||
wl.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean containsKey(long key) {
|
||||
rl.lock();
|
||||
try {
|
||||
long[][] outer = this.values[(int) (key & 255)];
|
||||
if (outer == null) return false;
|
||||
|
||||
long[] inner = outer[(int) ((key >> 32) & 255)];
|
||||
if (inner == null) return false;
|
||||
|
||||
for (long entry : inner) {
|
||||
if (entry == key) return true;
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
rl.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(long key) {
|
||||
wl.lock();
|
||||
try {
|
||||
long[][] outer = this.values[(int) (key & 255)];
|
||||
if (outer == null) return;
|
||||
|
||||
long[] inner = outer[(int) ((key >> 32) & 255)];
|
||||
if (inner == null) return;
|
||||
|
||||
int max = inner.length - 1;
|
||||
for (int i = 0; i <= max; i++) {
|
||||
if (inner[i] == key) {
|
||||
this.count--;
|
||||
if (i != max) {
|
||||
inner[i] = inner[max];
|
||||
}
|
||||
|
||||
outer[(int) ((key >> 32) & 255)] = (max == 0 ? null : Arrays.copyOf(inner, max));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
wl.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public long popFirst() {
|
||||
wl.lock();
|
||||
try {
|
||||
for (long[][] outer: this.values) {
|
||||
if (outer == null) continue;
|
||||
|
||||
for (int i = 0; i < outer.length; i++) {
|
||||
long[] inner = outer[i];
|
||||
if (inner == null || inner.length == 0) continue;
|
||||
|
||||
this.count--;
|
||||
long ret = inner[inner.length - 1];
|
||||
outer[i] = Arrays.copyOf(inner, inner.length - 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
wl.unlock();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long[] popAll() {
|
||||
int index = 0;
|
||||
wl.lock();
|
||||
try {
|
||||
long[] ret = new long[this.count];
|
||||
for (long[][] outer : this.values) {
|
||||
if (outer == null) continue;
|
||||
|
||||
for (int oIdx = outer.length - 1; oIdx >= 0; oIdx--) {
|
||||
long[] inner = outer[oIdx];
|
||||
if (inner == null) continue;
|
||||
|
||||
for (long entry: inner) {
|
||||
ret[index++] = entry;
|
||||
}
|
||||
outer[oIdx] = null;
|
||||
}
|
||||
}
|
||||
count = 0;
|
||||
return ret;
|
||||
} finally {
|
||||
wl.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public long[] keys() {
|
||||
int index = 0;
|
||||
rl.lock();
|
||||
try {
|
||||
long[] ret = new long[this.count];
|
||||
for (long[][] outer : this.values) {
|
||||
if (outer == null) continue;
|
||||
|
||||
for (long[] inner : outer) {
|
||||
if (inner == null) continue;
|
||||
|
||||
for (long entry : inner) {
|
||||
ret[index++] = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
} finally {
|
||||
rl.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.util.collect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class LongHashTable<V> extends LongBaseHashTable {
|
||||
|
||||
public void put(int msw, int lsw, V value) {
|
||||
put(toLong(msw, lsw), value);
|
||||
}
|
||||
|
||||
public V get(int msw, int lsw) {
|
||||
return get(toLong(msw, lsw));
|
||||
}
|
||||
|
||||
public synchronized void put(long key, V value) {
|
||||
put(new Entry(key, value));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized V get(long key) {
|
||||
Entry entry = ((Entry) getEntry(key));
|
||||
return entry != null ? entry.value : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized ArrayList<V> values() {
|
||||
ArrayList<V> ret = new ArrayList<V>();
|
||||
|
||||
ArrayList<EntryBase> entries = entries();
|
||||
|
||||
for (EntryBase entry : entries) {
|
||||
ret.add(((Entry) entry).value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private class Entry extends EntryBase {
|
||||
V value;
|
||||
|
||||
Entry(long k, V v) {
|
||||
super(k);
|
||||
this.value = v;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user