Merge remote-tracking branch 'upstream/main'

# Conflicts:
#	build.gradle.kts
#	settings.gradle.kts
#	worldedit-bukkit/build.gradle.kts
This commit is contained in:
2024-11-27 23:59:14 +01:00
783 changed files with 30462 additions and 15592 deletions

View File

@@ -108,14 +108,6 @@ public class FaweBukkit implements IFawe, Listener {
if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) {
LOGGER.warn("Small-edits enabled (maximum y range of 0 -> 256) with 1.18 world heights. Are you sure?");
}
if (version.isEqualOrLowerThan(MinecraftVersion.ONE_DOT_SIXTEEN_EOL)) {
LOGGER.warn("You are running Minecraft 1.16.5. This version has been released over two years ago (January 2021).");
LOGGER.warn("FastAsyncWorldEdit will stop operating on this version in the near future.");
LOGGER.warn("Neither Mojang, nor Spigot or other software vendors support this version anymore." +
"Please update your server to a newer version of Minecraft (1.20+) to continue receiving updates and " +
"support.");
}
}
@Override

View File

@@ -0,0 +1,67 @@
package com.fastasyncworldedit.bukkit;
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
import com.fastasyncworldedit.bukkit.util.WorldUnloadedException;
import com.fastasyncworldedit.core.math.IntPair;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import org.bukkit.Bukkit;
import org.bukkit.World;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
public class FaweBukkitWorld extends BukkitWorld {
private static final Map<World, FaweBukkitWorld> CACHE = Collections.synchronizedMap(new WeakHashMap<>());
private final ConcurrentHashMap<IntPair, NMSAdapter.ChunkSendLock> SENDING_CHUNKS = new ConcurrentHashMap<>();
/**
* Construct the object.
*
* @param world the world
*/
private FaweBukkitWorld(final World world) {
super(world);
}
public static FaweBukkitWorld of(World world) {
return CACHE.compute(world, (__, val) -> {
if (val == null) {
return new FaweBukkitWorld(world);
}
val.updateReference();
return val;
});
}
public static FaweBukkitWorld of(String worldName) {
World world = Bukkit.getWorld(worldName);
if (world == null) {
throw new UnsupportedOperationException("Unable to find org.bukkit.World instance for " + worldName + ". Is it loaded?");
}
return of(world);
}
public static ConcurrentHashMap<IntPair, NMSAdapter.ChunkSendLock> getWorldSendingChunksMap(FaweBukkitWorld world) {
return world.SENDING_CHUNKS;
}
public static ConcurrentHashMap<IntPair, NMSAdapter.ChunkSendLock> getWorldSendingChunksMap(String worldName) {
return of(worldName).SENDING_CHUNKS;
}
private void updateReference() {
World world = getWorld();
World bukkitWorld = Bukkit.getWorld(worldNameRef);
if (bukkitWorld == null) {
throw new WorldUnloadedException(worldNameRef);
} else if (bukkitWorld != world) {
worldRef = new WeakReference<>(bukkitWorld);
}
}
}

View File

@@ -2,6 +2,6 @@ package com.fastasyncworldedit.bukkit.adapter;
public interface BukkitGetBlocks {
void send(int mask, boolean lighting);
void send();
}

View File

@@ -0,0 +1,85 @@
package com.fastasyncworldedit.bukkit.adapter;
import com.fastasyncworldedit.core.util.TaskManager;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.TreeGenerator;
import org.bukkit.Material;
import org.bukkit.TreeType;
import org.bukkit.World;
import org.bukkit.block.BlockState;
import java.util.List;
import java.util.Map;
/**
* A base class for version-specific implementations of the BukkitImplAdapter
*
* @param <TAG> the version-specific NBT tag type
* @param <SERVER_LEVEL> the version-specific ServerLevel type
*/
public abstract class FaweAdapter<TAG, SERVER_LEVEL> extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter<TAG> {
protected final BukkitImplAdapter<TAG> parent;
protected char[] ibdToStateOrdinal = null;
protected int[] ordinalToIbdID = null;
protected boolean initialised = false;
protected Map<String, List<Property<?>>> allBlockProperties = null;
protected FaweAdapter(final BukkitImplAdapter<TAG> parent) {
this.parent = parent;
}
@Override
public boolean generateTree(
final TreeGenerator.TreeType treeType,
final EditSession editSession,
BlockVector3 blockVector3,
final World world
) {
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
if (bukkitType == TreeType.CHORUS_PLANT) {
// bukkit skips the feature gen which does this offset normally, so we have to add it back
blockVector3 = blockVector3.add(BlockVector3.UNIT_Y);
}
BlockVector3 target = blockVector3;
SERVER_LEVEL serverLevel = getServerLevel(world);
List<BlockState> placed = TaskManager.taskManager().sync(() -> {
preCaptureStates(serverLevel);
try {
if (!world.generateTree(BukkitAdapter.adapt(world, target), bukkitType)) {
return null;
}
return getCapturedBlockStatesCopy(serverLevel);
} finally {
postCaptureBlockStates(serverLevel);
}
});
if (placed == null || placed.isEmpty()) {
return false;
}
for (BlockState blockState : placed) {
if (blockState == null || blockState.getType() == Material.AIR) {
continue;
}
editSession.setBlock(blockState.getX(), blockState.getY(), blockState.getZ(),
BukkitAdapter.adapt(blockState.getBlockData())
);
}
return true;
}
protected abstract void preCaptureStates(SERVER_LEVEL serverLevel);
protected abstract List<BlockState> getCapturedBlockStatesCopy(SERVER_LEVEL serverLevel);
protected abstract void postCaptureBlockStates(SERVER_LEVEL serverLevel);
protected abstract SERVER_LEVEL getServerLevel(World world);
}

View File

@@ -31,6 +31,8 @@ import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.TreeType;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
@@ -97,7 +99,7 @@ public interface IBukkitAdapter {
checkNotNull(position);
return new org.bukkit.Location(
world,
position.getX(), position.getY(), position.getZ()
position.x(), position.y(), position.z()
);
}
@@ -117,7 +119,7 @@ public interface IBukkitAdapter {
checkNotNull(location);
return new org.bukkit.Location(
world,
location.getX(), location.getY(), location.getZ(),
location.x(), location.y(), location.z(),
location.getYaw(),
location.getPitch()
);
@@ -164,10 +166,10 @@ public interface IBukkitAdapter {
*/
default Material adapt(ItemType itemType) {
checkNotNull(itemType);
if (!itemType.getId().startsWith("minecraft:")) {
if (!itemType.id().startsWith("minecraft:")) {
throw new IllegalArgumentException("Bukkit only supports Minecraft items");
}
return Material.getMaterial(itemType.getId().substring(10).toUpperCase(Locale.ROOT));
return Material.getMaterial(itemType.id().substring(10).toUpperCase(Locale.ROOT));
}
/**
@@ -178,18 +180,20 @@ public interface IBukkitAdapter {
*/
default Material adapt(BlockType blockType) {
checkNotNull(blockType);
if (!blockType.getId().startsWith("minecraft:")) {
if (!blockType.id().startsWith("minecraft:")) {
throw new IllegalArgumentException("Bukkit only supports Minecraft blocks");
}
String id = blockType.getId().substring(10).toUpperCase(Locale.ROOT);
String id = blockType.id().substring(10).toUpperCase(Locale.ROOT);
return Material.getMaterial(id);
}
default org.bukkit.entity.EntityType adapt(EntityType entityType) {
if (!entityType.getId().startsWith("minecraft:")) {
throw new IllegalArgumentException("Bukkit only supports vanilla entities");
NamespacedKey entityKey = NamespacedKey.fromString(entityType.toString());
if (entityKey == null) {
throw new IllegalArgumentException("Entity key '" + entityType + "' does not map to Bukkit");
}
return org.bukkit.entity.EntityType.fromName(entityType.getId().substring(10).toLowerCase(Locale.ROOT));
return Registry.ENTITY_TYPE.get(entityKey);
}
/**
@@ -289,11 +293,11 @@ public interface IBukkitAdapter {
}
default Biome adapt(BiomeType biomeType) {
if (!biomeType.getId().startsWith("minecraft:")) {
if (!biomeType.id().startsWith("minecraft:")) {
throw new IllegalArgumentException("Bukkit only supports vanilla biomes");
}
try {
return Biome.valueOf(biomeType.getId().substring(10).toUpperCase(Locale.ROOT));
return Biome.valueOf(biomeType.id().substring(10).toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
return null;
}
@@ -343,7 +347,7 @@ public interface IBukkitAdapter {
* @return WorldEdit EntityType
*/
default EntityType adapt(org.bukkit.entity.EntityType entityType) {
return EntityTypes.get(entityType.getName().toLowerCase(Locale.ROOT));
return EntityTypes.get(entityType.getKey().toString());
}
/**

View File

@@ -8,15 +8,13 @@ import com.sk89q.worldedit.bukkit.BukkitPlayer;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.nbt.BinaryTag;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
@@ -33,6 +31,8 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinTag;
import javax.annotation.Nullable;
import java.util.Map;
@@ -81,7 +81,7 @@ public interface IDelegateBukkitImplAdapter<T> extends BukkitImplAdapter<T> {
}
@Override
default void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData) {
default void sendFakeNBT(Player player, BlockVector3 pos, LinCompoundTag nbtData) {
getParent().sendFakeNBT(player, pos, nbtData);
}
@@ -115,6 +115,26 @@ public interface IDelegateBukkitImplAdapter<T> extends BukkitImplAdapter<T> {
return getParent().getInternalBlockStateId(state);
}
@Override
default boolean clearContainerBlockContents(World world, BlockVector3 pt) {
return getParent().clearContainerBlockContents(world, pt);
}
@Override
default void setBiome(Location location, BiomeType biome) {
getParent().setBiome(location, biome);
}
@Override
default BiomeType getBiome(Location location) {
return getParent().getBiome(location);
}
@Override
default void sendBiomeUpdates(World world, Iterable<BlockVector2> chunks) {
getParent().sendBiomeUpdates(world, chunks);
}
@Override
default BlockMaterial getMaterial(BlockType blockType) {
return getParent().getMaterial(blockType);
@@ -131,8 +151,8 @@ public interface IDelegateBukkitImplAdapter<T> extends BukkitImplAdapter<T> {
}
@Override
default BinaryTag toNativeBinary(T foreign) {
return getParent().toNativeBinary(foreign);
default LinTag<?> toNativeLin(T foreign) {
return getParent().toNativeLin(foreign);
}
@Override
@@ -141,8 +161,8 @@ public interface IDelegateBukkitImplAdapter<T> extends BukkitImplAdapter<T> {
}
@Override
default T fromNativeBinary(BinaryTag foreign) {
return getParent().fromNativeBinary(foreign);
default T fromNativeLin(LinTag foreign) {
return getParent().fromNativeLin(foreign);
}
@Override

View File

@@ -1,12 +1,17 @@
package com.fastasyncworldedit.bukkit.adapter;
import com.fastasyncworldedit.bukkit.FaweBukkitWorld;
import com.fastasyncworldedit.core.FAWEPlatformAdapterImpl;
import com.fastasyncworldedit.core.math.IntPair;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Function;
public class NMSAdapter implements FAWEPlatformAdapterImpl {
@@ -137,7 +142,121 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl {
if (!(chunk instanceof BukkitGetBlocks)) {
throw new IllegalArgumentException("(IChunkGet) chunk not of type BukkitGetBlocks");
}
((BukkitGetBlocks) chunk).send(mask, lighting);
((BukkitGetBlocks) chunk).send();
}
/**
* Atomically set the given chunk section to the chunk section array stored in the chunk, given the expected existing chunk
* section instance at the given layer position.
* <p>
* Acquires a (FAWE-implemented only) write-lock on the chunk packet lock, waiting if required before writing, then freeing
* the lock. Also sets a boolean to indicate a write is waiting and therefore reads should not occur.
* <p>
* Utilises ConcurrentHashMap#compute for easy synchronisation for all of the above. Only tryWriteLock is used in blocks
* synchronised using ConcurrentHashMap methods.
*
* @since 2.12.0
*/
protected static <LevelChunkSection> boolean setSectionAtomic(
String worldName,
IntPair pair,
LevelChunkSection[] sections,
LevelChunkSection expected,
LevelChunkSection value,
int layer
) {
if (layer < 0 || layer >= sections.length) {
return false;
}
StampLockHolder holder = new StampLockHolder();
ConcurrentHashMap<IntPair, ChunkSendLock> chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
chunks.compute(pair, (k, lock) -> {
if (lock == null) {
lock = new ChunkSendLock();
} else if (lock.writeWaiting) {
throw new IllegalStateException("Attempting to write chunk section when write is already ongoing?!");
}
holder.stamp = lock.lock.tryWriteLock();
holder.chunkLock = lock;
lock.writeWaiting = true;
return lock;
});
try {
if (holder.stamp == 0) {
holder.stamp = holder.chunkLock.lock.writeLock();
}
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
} finally {
chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
chunks.computeIfPresent(pair, (k, lock) -> {
if (lock != holder.chunkLock) {
throw new IllegalStateException("SENDING_CHUNKS stored lock does not equal lock attempted to be unlocked?!");
}
lock.lock.unlockWrite(holder.stamp);
lock.writeWaiting = false;
// Keep the lock, etc. in the map as we're going to be accessing again later when sending
return lock;
});
}
}
/**
* Called before sending a chunk packet, filling the given stamp and stampedLock arrays' zeroth indices if the chunk packet
* send should go ahead.
* <p>
* Chunk packets should be sent if both of the following are met:
* - There is no more than one current packet send ongoing
* - There is no chunk section "write" waiting or ongoing,
* which are determined by the number of readers currently locking the StampedLock (i.e. the number of sends), if the
* stamped lock is currently write-locked and if the boolean for waiting write is true.
* <p>
* Utilises ConcurrentHashMap#compute for easy synchronisation
*
* @since 2.12.0
*/
protected static void beginChunkPacketSend(String worldName, IntPair pair, StampLockHolder stampedLock) {
ConcurrentHashMap<IntPair, ChunkSendLock> chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
chunks.compute(pair, (k, lock) -> {
if (lock == null) {
lock = new ChunkSendLock();
}
// Allow twice-read-locking, so if the packets have been created but not sent, we can queue another read
if (lock.writeWaiting || lock.lock.getReadLockCount() > 1 || lock.lock.isWriteLocked()) {
return lock;
}
stampedLock.stamp = lock.lock.readLock();
stampedLock.chunkLock = lock;
return lock;
});
}
/**
* Releases the read lock acquired when sending a chunk packet for a chunk
*
* @since 2.12.0
*/
protected static void endChunkPacketSend(String worldName, IntPair pair, StampLockHolder lockHolder) {
ConcurrentHashMap<IntPair, ChunkSendLock> chunks = FaweBukkitWorld.getWorldSendingChunksMap(worldName);
chunks.computeIfPresent(pair, (k, lock) -> {
if (lock.lock != lockHolder.chunkLock.lock) {
throw new IllegalStateException("SENDING_CHUNKS stored lock does not equal lock attempted to be unlocked?!");
}
lock.lock.unlockRead(lockHolder.stamp);
// Do not continue to store the lock if we may not need it (i.e. chunk has been sent, may not be sent again)
return null;
});
}
public static final class StampLockHolder {
public long stamp;
public ChunkSendLock chunkLock = null;
}
public static final class ChunkSendLock {
public final StampedLock lock = new StampedLock();
public boolean writeWaiting = false;
}
}

View File

@@ -5,7 +5,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.world.World;
@@ -14,8 +13,7 @@ import javax.annotation.Nonnull;
public class NMSRelighterFactory implements RelighterFactory {
@Override
public @Nonnull
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
return new NMSRelighter(
queue,
relightMode != null ? relightMode : RelightMode.valueOf(Settings.settings().LIGHTING.MODE)

View File

@@ -1,60 +1,32 @@
package com.fastasyncworldedit.bukkit.adapter;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.queue.IChunkCache;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.SingleThreadQueueExtent;
import com.fastasyncworldedit.core.util.MathMan;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.fastasyncworldedit.core.util.TaskManager;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.RegenOptions;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import org.apache.logging.log4j.Logger;
import org.bukkit.World;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.WorldInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Represents an abstract regeneration handler.
*
* @param <IChunkAccess> the type of the {@code IChunkAccess} of the current Minecraft implementation
* @param <ProtoChunk> the type of the {@code ProtoChunk} of the current Minecraft implementation
* @param <Chunk> the type of the {@code Chunk} of the current Minecraft implementation
* @param <ChunkStatus> the type of the {@code ChunkStatusWrapper} wrapping the {@code ChunkStatus} enum
*/
public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess, Chunk extends IChunkAccess, ChunkStatus extends Regenerator.ChunkStatusWrapper<IChunkAccess>> {
private static final Logger LOGGER = LogManagerCompat.getLogger();
public abstract class Regenerator {
protected final org.bukkit.World originalBukkitWorld;
protected final Region region;
@@ -62,13 +34,8 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
protected final RegenOptions options;
//runtime
protected final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
private final Long2ObjectLinkedOpenHashMap<ProtoChunk> protoChunks = new Long2ObjectLinkedOpenHashMap<>();
private final Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
protected boolean generateConcurrent = true;
protected long seed;
private ExecutorService executor;
private SingleThreadQueueExtent source;
protected SingleThreadQueueExtent source;
/**
* Initializes an abstract regeneration handler.
@@ -85,19 +52,10 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
this.options = options;
}
private static Random getChunkRandom(long worldseed, int x, int z) {
Random random = new Random();
random.setSeed(worldseed);
long xRand = random.nextLong() / 2L * 2L + 1L;
long zRand = random.nextLong() / 2L * 2L + 1L;
random.setSeed((long) x * xRand + (long) z * zRand ^ worldseed);
return random;
}
/**
* Regenerates the selected {@code Region}.
*
* @return whether or not the regeneration process was successful
* @return whether the regeneration process was successful
* @throws Exception when something goes terribly wrong
*/
public boolean regenerate() throws Exception {
@@ -115,16 +73,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
throw e;
}
try {
if (!generate()) {
cleanup0();
return false;
}
} catch (Exception e) {
cleanup0();
throw e;
}
try {
copyToWorld();
} catch (Exception e) {
@@ -137,172 +85,26 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
}
/**
* Returns the {@code ProtoChunk} at the given chunk coordinates.
*
* @param x the chunk x coordinate
* @param z the chunk z coordinate
* @return the {@code ProtoChunk} at the given chunk coordinates or null if it is not part of the regeneration process or has not been initialized yet.
* Execute tasks on the main thread during regen.
*/
protected ProtoChunk getProtoChunkAt(int x, int z) {
return protoChunks.get(MathMan.pairInt(x, z));
}
protected abstract void runTasks(BooleanSupplier shouldKeepTicking);
/**
* Returns the {@code Chunk} at the given chunk coordinates.
*
* @param x the chunk x coordinate
* @param z the chunk z coordinate
* @return the {@code Chunk} at the given chunk coordinates or null if it is not part of the regeneration process or has not been converted yet.
*/
protected Chunk getChunkAt(int x, int z) {
return chunks.get(MathMan.pairInt(x, z));
}
private void createSource() {
private boolean generate() throws Exception {
if (generateConcurrent) {
//Using concurrent chunk generation
executor = Executors.newFixedThreadPool(Settings.settings().QUEUE.PARALLEL_THREADS, new ThreadFactoryBuilder()
.setNameFormat("fawe-regen-%d")
.build()
);
} else { // else using sequential chunk generation, concurrent not supported
executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
.setNameFormat("fawe-regen-%d")
.build());
}
//TODO: can we get that required radius down without affecting chunk generation (e.g. strucures, features, ...)?
//for now it is working well and fast, if we are bored in the future we could do the research (a lot of it) to reduce the border radius
//generate chunk coords lists with a certain radius
Int2ObjectOpenHashMap<List<Long>> chunkCoordsForRadius = new Int2ObjectOpenHashMap<>();
chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> {
if (radius == -1) { //ignore ChunkStatus.EMPTY
return;
}
int border = 10 - radius; //9 = 8 + 1, 8: max border radius used in chunk stages, 1: need 1 extra chunk for chunk
// features to generate at the border of the region
chunkCoordsForRadius.put(radius, getChunkCoordsRegen(region, border));
});
//create chunks
for (Long xz : chunkCoordsForRadius.get(0)) {
ProtoChunk chunk = createProtoChunk(MathMan.unpairIntX(xz), MathMan.unpairIntY(xz));
protoChunks.put(xz, chunk);
}
//generate lists for RegionLimitedWorldAccess, need to be square with odd length (e.g. 17x17), 17 = 1 middle chunk + 8 border chunks * 2
Int2ObjectOpenHashMap<Long2ObjectOpenHashMap<List<IChunkAccess>>> worldlimits = new Int2ObjectOpenHashMap<>();
chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> {
if (radius == -1) { //ignore ChunkStatus.EMPTY
return;
}
Long2ObjectOpenHashMap<List<IChunkAccess>> map = new Long2ObjectOpenHashMap<>();
for (Long xz : chunkCoordsForRadius.get(radius)) {
int x = MathMan.unpairIntX(xz);
int z = MathMan.unpairIntY(xz);
List<IChunkAccess> l = new ArrayList<>((radius + 1 + radius) * (radius + 1 + radius));
for (int zz = z - radius; zz <= z + radius; zz++) { //order is important, first z then x
for (int xx = x - radius; xx <= x + radius; xx++) {
l.add(protoChunks.get(MathMan.pairInt(xx, zz)));
}
}
map.put(xz, l);
}
worldlimits.put(radius, map);
});
//run generation tasks excluding FULL chunk status
for (Map.Entry<ChunkStatus, Concurrency> entry : chunkStati.entrySet()) {
ChunkStatus chunkStatus = entry.getKey();
int radius = chunkStatus.requiredNeighborChunkRadius0();
List<Long> coords = chunkCoordsForRadius.get(radius);
if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) {
SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> tasks = getChunkStatusTaskRows(coords, radius);
for (ConcurrentTasks<SequentialTasks<Long>> para : tasks) {
List<Runnable> scheduled = new ArrayList<>(tasks.size());
for (SequentialTasks<Long> row : para) {
scheduled.add(() -> {
for (Long xz : row) {
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
}
});
}
try {
List<Future<?>> futures = new ArrayList<>();
scheduled.forEach(task -> futures.add(executor.submit(task)));
for (Future<?> future : futures) {
future.get();
}
} catch (Exception e) {
e.printStackTrace();
}
}
} else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) {
// every chunk can be processed individually
List<Runnable> scheduled = new ArrayList<>(coords.size());
for (long xz : coords) {
scheduled.add(() -> {
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
});
}
try {
List<Future<?>> futures = new ArrayList<>();
scheduled.forEach(task -> futures.add(executor.submit(task)));
for (Future<?> future : futures) {
future.get();
}
} catch (Exception e) {
e.printStackTrace();
}
} else { // Concurrency.NONE or generateConcurrent == false
// run sequential but submit to different thread
// running regen on the main thread otherwise triggers async-only events on the main thread
executor.submit(() -> {
for (long xz : coords) {
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
}
}).get(); // wait until finished this step
}
}
//convert to proper chunks
for (Long xz : chunkCoordsForRadius.get(0)) {
ProtoChunk proto = protoChunks.get(xz);
chunks.put(xz, createChunk(proto));
}
//final chunkstatus
ChunkStatus FULL = getFullChunkStatus();
for (Long xz : chunkCoordsForRadius.get(0)) { //FULL.requiredNeighbourChunkRadius() == 0!
Chunk chunk = chunks.get(xz);
FULL.processChunkSave(xz, Arrays.asList(chunk));
}
//populate
List<BlockPopulator> populators = getBlockPopulators();
for (Long xz : chunkCoordsForRadius.get(0)) {
int x = MathMan.unpairIntX(xz);
int z = MathMan.unpairIntY(xz);
//prepare chunk seed
Random random = getChunkRandom(seed, x, z);
//actually populate
Chunk c = chunks.get(xz);
populators.forEach(pop -> {
populate(c, random, pop);
});
}
source = new SingleThreadQueueExtent(BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMinHeight() : 0,
BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMaxHeight() : 256);
source = new SingleThreadQueueExtent(
BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMinHeight() : 0,
BukkitWorld.HAS_MIN_Y ? originalBukkitWorld.getMaxHeight() : 256
);
source.init(target, initSourceQueueCache(), null);
return true;
}
private void copyToWorld() {
createSource();
final long timeoutPerTick = TimeUnit.MILLISECONDS.toNanos(10);
int taskId = TaskManager.taskManager().repeat(() -> {
final long startTime = System.nanoTime();
runTasks(() -> System.nanoTime() - startTime < timeoutPerTick);
}, 1);
//Setting Blocks
boolean genbiomes = options.shouldRegenBiomes();
boolean hasBiome = options.hasBiomeType();
@@ -315,6 +117,7 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
} else if (genbiomes) {
target.setBlocks(region, new WithBiomePlacementPattern(vec -> source.getBiome(vec)));
}
TaskManager.taskManager().cancel(taskId);
}
private class PlacementPattern implements Pattern {
@@ -326,7 +129,7 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
@Override
public boolean apply(final Extent extent, final BlockVector3 get, final BlockVector3 set) throws WorldEditException {
return extent.setBlock(set.getX(), set.getY(), set.getZ(), source.getFullBlock(get.getX(), get.getY(), get.getZ()));
return extent.setBlock(set.x(), set.y(), set.z(), source.getFullBlock(get.x(), get.y(), get.z()));
}
}
@@ -346,17 +149,14 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
@Override
public boolean apply(final Extent extent, final BlockVector3 get, final BlockVector3 set) throws WorldEditException {
return extent.setBlock(set.getX(), set.getY(), set.getZ(), source.getFullBlock(get.getX(), get.getY(), get.getZ()))
&& extent.setBiome(set.getX(), set.getY(), set.getZ(), biomeGetter.apply(get));
return extent.setBlock(set.x(), set.y(), set.z(), source.getFullBlock(get.x(), get.y(), get.z()))
&& extent.setBiome(set.x(), set.y(), set.z(), biomeGetter.apply(get));
}
}
//functions to be implemented by sub class
private void cleanup0() {
if (executor != null) {
executor.shutdownNow();
}
cleanup();
}
@@ -388,47 +188,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
*/
protected abstract void cleanup();
/**
* Implement the initialization of a {@code ProtoChunk} here.
*
* @param x the x coorinate of the {@code ProtoChunk} to create
* @param z the z coorinate of the {@code ProtoChunk} to create
* @return an initialized {@code ProtoChunk}
*/
protected abstract ProtoChunk createProtoChunk(int x, int z);
/**
* Implement the convertion of a {@code ProtoChunk} to a {@code Chunk} here.
*
* @param protoChunk the {@code ProtoChunk} to be converted to a {@code Chunk}
* @return the converted {@code Chunk}
*/
protected abstract Chunk createChunk(ProtoChunk protoChunk);
/**
* Return the {@code ChunkStatus.FULL} here.
* ChunkStatus.FULL is the last step of vanilla chunk generation.
*
* @return {@code ChunkStatus.FULL}
*/
protected abstract ChunkStatus getFullChunkStatus();
/**
* Return a list of {@code BlockPopulator} used to populate the original world here.
*
* @return {@code ChunkStatus.FULL}
*/
protected abstract List<BlockPopulator> getBlockPopulators();
/**
* Implement the population of the {@code Chunk} with the given chunk random and {@code BlockPopulator} here.
*
* @param chunk the {@code Chunk} to populate
* @param random the chunk random to use for population
* @param pop the {@code BlockPopulator} to use
*/
protected abstract void populate(Chunk chunk, Random random, BlockPopulator pop);
/**
* Implement the initialization an {@code IChunkCache<IChunkGet>} here. Use will need the {@code getChunkAt} function
*
@@ -436,103 +195,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
*/
protected abstract IChunkCache<IChunkGet> initSourceQueueCache();
//algorithms
private List<Long> getChunkCoordsRegen(Region region, int border) { //needs to be square num of chunks
BlockVector3 oldMin = region.getMinimumPoint();
BlockVector3 newMin = BlockVector3.at(
(oldMin.getX() >> 4 << 4) - border * 16,
oldMin.getY(),
(oldMin.getZ() >> 4 << 4) - border * 16
);
BlockVector3 oldMax = region.getMaximumPoint();
BlockVector3 newMax = BlockVector3.at(
(oldMax.getX() >> 4 << 4) + (border + 1) * 16 - 1,
oldMax.getY(),
(oldMax.getZ() >> 4 << 4) + (border + 1) * 16 - 1
);
Region adjustedRegion = new CuboidRegion(newMin, newMax);
return adjustedRegion.getChunks().stream()
.sorted(Comparator
.comparingInt(BlockVector2::getZ)
.thenComparingInt(BlockVector2::getX)) //needed for RegionLimitedWorldAccess
.map(c -> MathMan.pairInt(c.getX(), c.getZ()))
.collect(Collectors.toList());
}
/**
* Creates a list of chunkcoord rows that may be executed concurrently
*
* @param allcoords the coords that should be sorted into rows, must be sorted by z and x
* @param requiredNeighborChunkRadius the radius of neighbor chunks that may not be written to concurrently (ChunkStatus
* .requiredNeighborRadius)
* @return a list of chunkcoords rows that may be executed concurrently
*/
private SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> getChunkStatusTaskRows(
List<Long> allcoords,
int requiredNeighborChunkRadius
) {
int requiredneighbors = Math.max(0, requiredNeighborChunkRadius);
int minx = allcoords.isEmpty() ? 0 : MathMan.unpairIntX(allcoords.get(0));
int maxx = allcoords.isEmpty() ? 0 : MathMan.unpairIntX(allcoords.get(allcoords.size() - 1));
int minz = allcoords.isEmpty() ? 0 : MathMan.unpairIntY(allcoords.get(0));
int maxz = allcoords.isEmpty() ? 0 : MathMan.unpairIntY(allcoords.get(allcoords.size() - 1));
SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> tasks;
if (maxz - minz > maxx - minx) {
int numlists = Math.min(requiredneighbors * 2 + 1, maxx - minx + 1);
Int2ObjectOpenHashMap<SequentialTasks<Long>> byx = new Int2ObjectOpenHashMap();
int expectedListLength = (allcoords.size() + 1) / (maxx - minx);
//init lists
for (int i = minx; i <= maxx; i++) {
byx.put(i, new SequentialTasks(expectedListLength));
}
//sort into lists by x coord
for (Long xz : allcoords) {
byx.get(MathMan.unpairIntX(xz)).add(xz);
}
//create parallel tasks
tasks = new SequentialTasks(numlists);
for (int offset = 0; offset < numlists; offset++) {
ConcurrentTasks<SequentialTasks<Long>> para = new ConcurrentTasks((maxz - minz + 1) / numlists + 1);
for (int i = 0; minx + i * numlists + offset <= maxx; i++) {
para.add(byx.get(minx + i * numlists + offset));
}
tasks.add(para);
}
} else {
int numlists = Math.min(requiredneighbors * 2 + 1, maxz - minz + 1);
Int2ObjectOpenHashMap<SequentialTasks<Long>> byz = new Int2ObjectOpenHashMap();
int expectedListLength = (allcoords.size() + 1) / (maxz - minz);
//init lists
for (int i = minz; i <= maxz; i++) {
byz.put(i, new SequentialTasks(expectedListLength));
}
//sort into lists by x coord
for (Long xz : allcoords) {
byz.get(MathMan.unpairIntY(xz)).add(xz);
}
//create parallel tasks
tasks = new SequentialTasks(numlists);
for (int offset = 0; offset < numlists; offset++) {
ConcurrentTasks<SequentialTasks<Long>> para = new ConcurrentTasks((maxx - minx + 1) / numlists + 1);
for (int i = 0; minz + i * numlists + offset <= maxz; i++) {
para.add(byz.get(minz + i * numlists + offset));
}
tasks.add(para);
}
}
return tasks;
}
protected BiomeProvider getBiomeProvider() {
if (options.hasBiomeType()) {
return new SingleBiomeProvider();
@@ -548,101 +210,6 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
NONE
}
/**
* This class is used to wrap the ChunkStatus of the current Minecraft implementation and as the implementation to execute a chunk generation step.
*
* @param <IChunkAccess> the IChunkAccess class of the current Minecraft implementation
*/
public static abstract class ChunkStatusWrapper<IChunkAccess> {
/**
* Return the required neighbor chunk radius the wrapped {@code ChunkStatus} requires.
*
* @return the radius of required neighbor chunks
*/
public abstract int requiredNeighborChunkRadius();
int requiredNeighborChunkRadius0() {
return Math.max(0, requiredNeighborChunkRadius());
}
/**
* Return the name of the wrapped {@code ChunkStatus}.
*
* @return the radius of required neighbor chunks
*/
public abstract String name();
/**
* Return the name of the wrapped {@code ChunkStatus}.
*
* @param xz represents the chunk coordinates of the chunk to process as denoted by {@code MathMan}
* @param accessibleChunks a list of chunks that will be used during the execution of the wrapped {@code ChunkStatus}.
* This list is order in the correct order required by the {@code ChunkStatus}, unless Mojang suddenly decides to do things differently.
*/
public abstract CompletableFuture<?> processChunk(Long xz, List<IChunkAccess> accessibleChunks);
void processChunkSave(Long xz, List<IChunkAccess> accessibleChunks) {
try {
processChunk(xz, accessibleChunks).get();
} catch (Exception e) {
LOGGER.error(
"Error while running " + name() + " on chunk " + MathMan.unpairIntX(xz) + "/" + MathMan.unpairIntY(xz),
e
);
}
}
}
public static class SequentialTasks<T> extends Tasks<T> {
public SequentialTasks(int expectedsize) {
super(expectedsize);
}
}
public static class ConcurrentTasks<T> extends Tasks<T> {
public ConcurrentTasks(int expectedsize) {
super(expectedsize);
}
}
public static class Tasks<T> implements Iterable<T> {
private final List<T> tasks;
public Tasks(int expectedsize) {
tasks = new ArrayList(expectedsize);
}
public void add(T task) {
tasks.add(task);
}
public List<T> list() {
return tasks;
}
public int size() {
return tasks.size();
}
@Override
public Iterator<T> iterator() {
return tasks.iterator();
}
@Override
public String toString() {
return tasks.toString();
}
}
public class SingleBiomeProvider extends BiomeProvider {
private final org.bukkit.block.Biome biome = BukkitAdapter.adapt(options.getBiomeType());

View File

@@ -0,0 +1,196 @@
package com.fastasyncworldedit.bukkit.adapter;
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.TaskManager;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongSet;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
/**
* A base class for version-specific implementations of the starlight relighting mechanism
*
* @param <SERVER_LEVEL> the version-specific ServerLevel type
* @param <CHUNK_POS> the version-specific ChunkPos type
* @since 2.8.2
*/
public abstract class StarlightRelighter<SERVER_LEVEL, CHUNK_POS> implements Relighter {
protected static final Logger LOGGER = LogManagerCompat.getLogger();
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32
private static final int CHUNKS_PER_BATCH_SQRT_LOG2 = 5; // for shifting
private final ReentrantLock lock = new ReentrantLock();
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
private final ReentrantLock areaLock = new ReentrantLock();
private final NMSRelighter delegate;
protected final SERVER_LEVEL serverLevel;
protected StarlightRelighter(SERVER_LEVEL serverLevel, IQueueExtent<?> queue) {
this.serverLevel = serverLevel;
this.delegate = new NMSRelighter(queue);
}
protected Set<CHUNK_POS> convertChunkKeysToChunkPos(LongSet chunks) {
// convert from long keys to ChunkPos
Set<CHUNK_POS> coords = new HashSet<>();
LongIterator iterator = chunks.iterator();
while (iterator.hasNext()) {
coords.add(createChunkPos(iterator.nextLong()));
}
return coords;
}
protected abstract CHUNK_POS createChunkPos(long chunkKey);
protected abstract long asLong(int chunkX, int chunkZ);
protected abstract CompletableFuture<?> chunkLoadFuture(CHUNK_POS pos);
protected List<CompletableFuture<?>> chunkLoadFutures(Set<CHUNK_POS> coords) {
List<CompletableFuture<?>> futures = new ArrayList<>();
for (final CHUNK_POS coord : coords) {
futures.add(chunkLoadFuture(coord));
}
return futures;
}
@NotNull
protected IntConsumer postProcessCallback(Runnable andThen, Set<CHUNK_POS> coords) {
return i -> {
if (i != coords.size()) {
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
}
// post process chunks on main thread
TaskManager.taskManager().task(() -> postProcessChunks(coords));
// call callback on our own threads
TaskManager.taskManager().async(andThen);
};
}
protected abstract void invokeRelight(
Set<CHUNK_POS> coords,
Consumer<CHUNK_POS> chunkCallback,
IntConsumer processCallback
);
protected abstract void postProcessChunks(Set<CHUNK_POS> coords);
/*
* Processes a set of chunks and runs an action afterwards.
* The action is run async, the chunks are partly processed on the main thread
* (as required by the server).
*/
protected void fixLighting(LongSet chunks, Runnable andThen) {
Set<CHUNK_POS> coords = convertChunkKeysToChunkPos(chunks);
TaskManager.taskManager().task(() -> {
// trigger chunk load and apply ticket on main thread
List<CompletableFuture<?>> futures = chunkLoadFutures(coords);
// collect futures and trigger relight once all chunks are loaded
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v ->
invokeRelight(
coords,
c -> {
}, // no callback for single chunks required
postProcessCallback(andThen, coords)
)
);
});
}
@Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
areaLock.lock();
try {
long key = MathMan.pairInt(cx >> CHUNKS_PER_BATCH_SQRT_LOG2, cz >> CHUNKS_PER_BATCH_SQRT_LOG2);
// TODO probably submit here already if chunks.size == CHUNKS_PER_BATCH?
LongSet chunks = this.regions.computeIfAbsent(key, k -> new LongArraySet(CHUNKS_PER_BATCH >> 2));
chunks.add(asLong(cx, cz));
} finally {
areaLock.unlock();
}
return true;
}
/*
* This method is called "recursively", iterating and removing elements
* from the regions linked map. This way, chunks are loaded in batches to avoid
* OOMEs.
*/
@Override
public void fixLightingSafe(boolean sky) {
this.areaLock.lock();
try {
if (regions.isEmpty()) {
return;
}
LongSet first = regions.removeFirst();
fixLighting(first, () -> fixLightingSafe(true));
} finally {
this.areaLock.unlock();
}
}
@Override
public void addLightUpdate(int x, int y, int z) {
this.delegate.addLightUpdate(x, y, z);
}
@Override
public void clear() {
}
@Override
public void removeLighting() {
this.delegate.removeLighting();
}
@Override
public void fixBlockLighting() {
fixLightingSafe(true);
}
@Override
public void fixSkyLighting() {
fixLightingSafe(true);
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public ReentrantLock getLock() {
return this.lock;
}
@Override
public boolean isFinished() {
return false;
}
@Override
public void close() throws Exception {
fixLightingSafe(true);
}
}

View File

@@ -135,7 +135,7 @@ public abstract class ChunkListener implements Listener {
@Deprecated(since = "2.0.0")
public void cleanup(Chunk chunk) {
for (Entity entity : chunk.getEntities()) {
if (entity.getType() == EntityType.DROPPED_ITEM) {
if (entity.getType() == EntityType.ITEM) {
entity.remove();
}
}

View File

@@ -3,7 +3,9 @@ package com.fastasyncworldedit.bukkit.regions;
import com.fastasyncworldedit.core.regions.FaweMask;
import com.griefdefender.api.GriefDefender;
import com.griefdefender.api.claim.Claim;
import com.griefdefender.api.claim.ClaimManager;
import com.griefdefender.api.claim.TrustTypes;
import com.griefdefender.lib.flowpowered.math.vector.Vector3i;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
@@ -17,8 +19,8 @@ public class GriefDefenderFeature extends BukkitMaskManager implements Listener
private static final Logger LOGGER = LogManagerCompat.getLogger();
public GriefDefenderFeature(final Plugin GriefDefenderPlugin) {
super(GriefDefenderPlugin.getName());
public GriefDefenderFeature(final Plugin plugin) {
super(plugin.getName());
LOGGER.info("Plugin 'GriefDefender' found. Using it now.");
}
@@ -44,9 +46,14 @@ public class GriefDefenderFeature extends BukkitMaskManager implements Listener
);
return new FaweMask(new CuboidRegion(pos1, pos2)) {
private final int[] bounds = new int[]{
pos1.x(), pos1.y(), pos1.z(),
pos2.x(), pos2.y(), pos2.z()
};
@Override
public boolean isValid(com.sk89q.worldedit.entity.Player wePlayer, MaskType type) {
return isAllowed(player, claim, type);
return validateClaimAgainstCache(claim, bounds) && isAllowed(player, claim, type);
}
};
}
@@ -54,4 +61,20 @@ public class GriefDefenderFeature extends BukkitMaskManager implements Listener
return null;
}
private static boolean validateClaimAgainstCache(Claim claim, int[] bounds) {
Vector3i min = claim.getLesserBoundaryCorner();
Vector3i max = claim.getGreaterBoundaryCorner();
if (min.getX() != bounds[0] || min.getY() != bounds[1] || min.getZ() != bounds[2]) {
return false;
}
if (max.getX() != bounds[3] || max.getY() != bounds[4] || max.getZ() != bounds[5]) {
return false;
}
final ClaimManager manager = GriefDefender.getCore().getClaimManager(claim.getWorldUniqueId());
if (manager == null) {
return false;
}
return manager.getClaimByUUID(claim.getUniqueId()) != null;
}
}

View File

@@ -1,5 +1,6 @@
package com.fastasyncworldedit.bukkit.regions;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.regions.FaweMask;
import com.fastasyncworldedit.core.regions.RegionWrapper;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
@@ -56,7 +57,7 @@ public class WorldGuardFeature extends BukkitMaskManager implements Listener {
if (region instanceof ProtectedPolygonalRegion casted) {
BlockVector3 max = region.getMaximumPoint();
BlockVector3 min = region.getMinimumPoint();
return new Polygonal2DRegion(null, casted.getPoints(), min.getBlockY(), max.getBlockY());
return new Polygonal2DRegion(null, casted.getPoints(), min.y(), max.y());
}
return new AdaptedRegion(region);
}
@@ -158,18 +159,30 @@ public class WorldGuardFeature extends BukkitMaskManager implements Listener {
@Override
public FaweMask getMask(com.sk89q.worldedit.entity.Player wePlayer, MaskType type, boolean isWhitelist) {
if (isWhitelist && Settings.settings().REGION_RESTRICTIONS_OPTIONS.WORLDGUARD_REGION_BLACKLIST) {
return new FaweMask(RegionWrapper.GLOBAL());
}
final Player player = BukkitAdapter.adapt(wePlayer);
final LocalPlayer localplayer = this.worldguard.wrapPlayer(player);
final Location location = player.getLocation();
final Set<ProtectedRegion> regions = this.getRegions(localplayer, location, isWhitelist);
if (!regions.isEmpty()) {
RegionManager manager = WorldGuard
.getInstance()
.getPlatform()
.getRegionContainer()
.get(BukkitAdapter.adapt(location.getWorld()));
if (manager == null) {
return null;
}
Set<Region> result = new HashSet<>();
for (ProtectedRegion myRegion : regions) {
if (myRegion.getId().equals("__global__")) {
return new FaweMask(RegionWrapper.GLOBAL()) {
@Override
public boolean isValid(com.sk89q.worldedit.entity.Player player, MaskType type) {
return isAllowed(worldguard.wrapPlayer(BukkitAdapter.adapt(player)), myRegion);
return manager.hasRegion(myRegion.getId())
&& isAllowed(worldguard.wrapPlayer(BukkitAdapter.adapt(player)), myRegion);
}
};
} else {
@@ -185,7 +198,7 @@ public class WorldGuardFeature extends BukkitMaskManager implements Listener {
public boolean isValid(com.sk89q.worldedit.entity.Player player, MaskType type) {
final LocalPlayer localplayer = worldguard.wrapPlayer(BukkitAdapter.adapt(player));
for (ProtectedRegion myRegion : regions) {
if (!isAllowed(localplayer, myRegion)) {
if (!manager.hasRegion(myRegion.getId()) || !isAllowed(localplayer, myRegion)) {
return false;
}
}

View File

@@ -16,6 +16,7 @@ import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.MaxChangedBlocksException;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
import com.sk89q.worldedit.function.FlatRegionFunction;
@@ -160,6 +161,10 @@ public class FaweDelegateRegionManager {
);
editSession.setBlocks(onTop, air);
}
FlatRegionFunction replace = new BiomeReplace(editSession, biome);
FlatRegionVisitor visitor = new FlatRegionVisitor((CuboidRegion) floorRegion, replace, editSession);
Operations.completeLegacy(visitor);
}
if (hybridPlotWorld.PLOT_SCHEMATIC) {
@@ -213,7 +218,6 @@ public class FaweDelegateRegionManager {
) {
TaskManager.taskManager().async(() -> {
synchronized (FaweDelegateRegionManager.class) {
//todo because of the following code this should probably be in the Bukkit module
World pos1World = BukkitAdapter.adapt(getWorld(pos1.getWorldName()));
World pos3World = BukkitAdapter.adapt(getWorld(swapPos.getWorldName()));
EditSession sessionA = WorldEdit.getInstance().newEditSessionBuilder().world(pos1World)
@@ -232,14 +236,16 @@ public class FaweDelegateRegionManager {
CuboidRegion regionB = new CuboidRegion(
pos3World,
swapPos.getBlockVector3(),
swapPos.getBlockVector3().add(pos2.getBlockVector3()).subtract(pos1.getBlockVector3())
swapPos.getBlockVector3().add(pos2.getBlockVector3().subtract(pos1.getBlockVector3())).withY(pos2.getY())
);
Clipboard clipA = Clipboard.create(regionA, UUID.randomUUID());
Clipboard clipB = Clipboard.create(regionB, UUID.randomUUID());
Clipboard clipA = new BlockArrayClipboard(regionA, UUID.randomUUID());
Clipboard clipB = new BlockArrayClipboard(regionB, UUID.randomUUID());
ForwardExtentCopy copyA = new ForwardExtentCopy(sessionA, regionA, clipA, clipA.getMinimumPoint());
ForwardExtentCopy copyB = new ForwardExtentCopy(sessionB, regionB, clipB, clipB.getMinimumPoint());
copyA.setCopyingBiomes(true);
copyB.setCopyingBiomes(true);
copyA.setCopyingEntities(true);
copyB.setCopyingEntities(true);
try {
Operations.completeLegacy(copyA);
Operations.completeLegacy(copyB);
@@ -253,17 +259,16 @@ public class FaweDelegateRegionManager {
sessionA.close();
sessionB.close();
}
FaweAPI.fixLighting(pos1World, new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3()), null,
FaweAPI.fixLighting(
pos1World,
regionA,
null,
RelightMode.valueOf(com.fastasyncworldedit.core.configuration.Settings.settings().LIGHTING.MODE)
);
FaweAPI.fixLighting(pos1World, new CuboidRegion(
swapPos.getBlockVector3(),
BlockVector3.at(
swapPos.getX() + pos2.getX() - pos1.getX(),
0,
swapPos.getZ() + pos2.getZ() - pos1.getZ()
)
), null,
FaweAPI.fixLighting(
pos1World,
regionB,
null,
RelightMode.valueOf(com.fastasyncworldedit.core.configuration.Settings.settings().LIGHTING.MODE)
);
if (whenDone != null) {

View File

@@ -3,8 +3,8 @@ package com.fastasyncworldedit.bukkit.regions.plotsquared;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweAPI;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReader;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicWriter;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV2;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicWriterV2;
import com.fastasyncworldedit.core.jnbt.CompressedCompoundTag;
import com.fastasyncworldedit.core.jnbt.CompressedSchematicTag;
import com.fastasyncworldedit.core.util.IOUtil;
@@ -29,17 +29,19 @@ import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.MCEditSchematicReader;
import com.sk89q.worldedit.extent.clipboard.io.SpongeSchematicReader;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV3Reader;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.CuboidRegion;
import net.jpountz.lz4.LZ4BlockInputStream;
import org.anarres.parallelgzip.ParallelGZIPOutputStream;
import org.apache.logging.log4j.Logger;
import org.enginehub.linbus.stream.LinBinaryIO;
import javax.annotation.Nonnull;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
@@ -94,15 +96,15 @@ public class FaweDelegateSchematicHandler {
return;
}
BlockVector3 dimension = schematic.getClipboard().getDimensions();
final int WIDTH = dimension.getX();
final int LENGTH = dimension.getZ();
final int HEIGHT = dimension.getY();
final int WIDTH = dimension.x();
final int LENGTH = dimension.z();
final int HEIGHT = dimension.y();
final int worldHeight = plot.getArea().getMaxGenHeight() - plot.getArea().getMinGenHeight() + 1;
// Validate dimensions
CuboidRegion region = plot.getLargestRegion();
boolean sizeMismatch =
((region.getMaximumPoint().getX() - region.getMinimumPoint().getX() + xOffset + 1) < WIDTH) || (
(region.getMaximumPoint().getZ() - region.getMinimumPoint().getZ() + zOffset + 1) < LENGTH) || (HEIGHT
((region.getMaximumPoint().x() - region.getMinimumPoint().x() + xOffset + 1) < WIDTH) || (
(region.getMaximumPoint().z() - region.getMinimumPoint().z() + zOffset + 1) < LENGTH) || (HEIGHT
> worldHeight);
if (!Settings.Schematics.PASTE_MISMATCHES && sizeMismatch) {
if (actor != null) {
@@ -111,8 +113,8 @@ public class FaweDelegateSchematicHandler {
TaskManager.runTask(whenDone);
return;
}
if (((region.getMaximumPoint().getX() - region.getMinimumPoint().getX() + xOffset + 1) < WIDTH) || (
(region.getMaximumPoint().getZ() - region.getMinimumPoint().getZ() + zOffset + 1) < LENGTH) || (HEIGHT
if (((region.getMaximumPoint().x() - region.getMinimumPoint().x() + xOffset + 1) < WIDTH) || (
(region.getMaximumPoint().z() - region.getMinimumPoint().z() + zOffset + 1) < LENGTH) || (HEIGHT
> worldHeight)) {
if (whenDone != null) {
TaskManager.runTask(whenDone);
@@ -141,7 +143,7 @@ public class FaweDelegateSchematicHandler {
} else {
y_offset_actual = yOffset + pw.getMinBuildHeight() + editSession.getHighestTerrainBlock(region
.getMinimumPoint()
.getX() + 1, region.getMinimumPoint().getZ() + 1, pw.getMinGenHeight(), pw.getMaxGenHeight()
.x() + 1, region.getMinimumPoint().z() + 1, pw.getMinGenHeight(), pw.getMaxGenHeight()
);
}
}
@@ -150,7 +152,7 @@ public class FaweDelegateSchematicHandler {
}
final BlockVector3 to = BlockVector3
.at(region.getMinimumPoint().getX() + xOffset, y_offset_actual, region.getMinimumPoint().getZ() + zOffset);
.at(region.getMinimumPoint().x() + xOffset, y_offset_actual, region.getMinimumPoint().z() + zOffset);
final Clipboard clipboard = schematic.getClipboard();
clipboard.setOrigin(clipboard.getRegion().getMinimumPoint());
clipboard.paste(editSession, to, true, false, true);
@@ -182,7 +184,7 @@ public class FaweDelegateSchematicHandler {
try (OutputStream stream = new FileOutputStream(tmp);
NBTOutputStream output = new NBTOutputStream(
new BufferedOutputStream(new ParallelGZIPOutputStream(stream)))) {
new FastSchematicWriter(output).write(clipboard);
new FastSchematicWriterV2(output).write(clipboard);
}
} else {
try (OutputStream stream = new FileOutputStream(tmp);
@@ -194,7 +196,7 @@ public class FaweDelegateSchematicHandler {
} else {
try (OutputStream stream = new FileOutputStream(tmp);
NBTOutputStream output = new NBTOutputStream(new ParallelGZIPOutputStream(stream))) {
Map<String, Tag> map = tag.getValue();
Map<String, Tag<?, ?>> map = tag.getValue();
output.writeNamedTag("Schematic", map.getOrDefault("Schematic", tag));
}
}
@@ -226,7 +228,7 @@ public class FaweDelegateSchematicHandler {
try {
try (ParallelGZIPOutputStream gzip = new ParallelGZIPOutputStream(output)) {
try (NBTOutputStream nos = new NBTOutputStream(gzip)) {
Map<String, Tag> map = weTag.getValue();
Map<String, Tag<?, ?>> map = weTag.getValue();
nos.writeNamedTag("Schematic", map.getOrDefault("Schematic", weTag));
}
}
@@ -239,7 +241,7 @@ public class FaweDelegateSchematicHandler {
public Schematic getSchematic(@Nonnull InputStream is) {
try {
FastSchematicReader schematicReader = new FastSchematicReader(
FastSchematicReaderV2 schematicReader = new FastSchematicReaderV2(
new NBTInputStream(new BufferedInputStream(new GZIPInputStream(new BufferedInputStream(is)))));
Clipboard clip = schematicReader.read();
return new Schematic(clip);
@@ -249,8 +251,8 @@ public class FaweDelegateSchematicHandler {
return null;
}
try {
SpongeSchematicReader schematicReader =
new SpongeSchematicReader(new NBTInputStream(new GZIPInputStream(is)));
SpongeSchematicV3Reader schematicReader =
new SpongeSchematicV3Reader(LinBinaryIO.read(new DataInputStream(new GZIPInputStream(is))));
Clipboard clip = schematicReader.read();
return new Schematic(clip);
} catch (IOException e2) {

View File

@@ -1,6 +1,5 @@
package com.fastasyncworldedit.bukkit.regions.plotsquared;
import com.fastasyncworldedit.core.FaweAPI;
import com.fastasyncworldedit.core.configuration.Caption;
import com.fastasyncworldedit.core.regions.FaweMask;
import com.fastasyncworldedit.core.regions.FaweMaskManager;
@@ -159,8 +158,8 @@ public class PlotSquaredFeature extends FaweMaskManager {
regions = WEManager.getMask(pp);
if (regions.size() == 1) {
CuboidRegion region = regions.iterator().next();
if (region.getMinimumPoint().getX() == Integer.MIN_VALUE
&& region.getMaximumPoint().getX() == Integer.MAX_VALUE) {
if (region.getMinimumPoint().x() == Integer.MIN_VALUE
&& region.getMaximumPoint().x() == Integer.MAX_VALUE) {
regions.clear();
}
}
@@ -174,22 +173,21 @@ public class PlotSquaredFeature extends FaweMaskManager {
return null;
}
final World world = player.getWorld();
int min = area != null ? area.getMinBuildHeight() : world.getMinY();
// PlotSquared uses exclusive max height, WorldEdit uses inclusive max height -> subtract 1
int max = area != null ? Math.min(world.getMaxY(), area.getMaxBuildHeight() - 1) : world.getMaxY();
Region maskedRegion;
if (regions.size() == 1) {
final World world = player.getWorld();
int min = area != null ? area.getMinBuildHeight() : world.getMinY();
// PlotSquared uses exclusive max height, WorldEdit uses inclusive max height -> subtract 1
int max = area != null ? Math.min(world.getMaxY(), area.getMaxBuildHeight() - 1) : world.getMaxY();
final CuboidRegion region = regions.iterator().next();
final BlockVector3 pos1 = BlockVector3.at(region.getMinimumX(), min, region.getMinimumZ());
final BlockVector3 pos2 = BlockVector3.at(region.getMaximumX(), max, region.getMaximumZ());
maskedRegion = new CuboidRegion(pos1, pos2);
} else {
World world = FaweAPI.getWorld(area.getWorldName());
List<Region> weRegions = regions.stream().map(
r -> new CuboidRegion(world, BlockVector3.at(r.getMinimumX(), r.getMinimumY(), r.getMinimumZ()),
BlockVector3.at(r.getMaximumX(), r.getMaximumY(), r.getMaximumZ())
r -> new CuboidRegion(world, BlockVector3.at(r.getMinimumX(), min, r.getMinimumZ()),
BlockVector3.at(r.getMaximumX(), max, r.getMaximumZ())
)).collect(Collectors.toList());
maskedRegion = new RegionIntersection(world, weRegions);
}

View File

@@ -1,6 +1,8 @@
package com.fastasyncworldedit.bukkit.util;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
import it.unimi.dsi.fastutil.io.FastBufferedOutputStream;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongIterator;
@@ -19,5 +21,7 @@ final class DoNotMiniseThese {
private final LongSet d = null;
private final Int2ObjectMap<?> e = null;
private final Object2ObjectArrayMap<?, ?> f = null;
private final FastBufferedInputStream g = null;
private final FastBufferedOutputStream h = null;
}

View File

@@ -13,7 +13,6 @@ import java.util.regex.Pattern;
public class MinecraftVersion implements Comparable<MinecraftVersion> {
public static final MinecraftVersion NETHER = new MinecraftVersion(1, 16);
public static final MinecraftVersion ONE_DOT_SIXTEEN_EOL = new MinecraftVersion(1, 16, 5);
public static final MinecraftVersion CAVES_17 = new MinecraftVersion(1, 17);
public static final MinecraftVersion CAVES_18 = new MinecraftVersion(1, 18);
private static MinecraftVersion current = null;

View File

@@ -20,6 +20,7 @@
package com.sk89q.bukkit.util;
import com.sk89q.util.ReflectionUtil;
import io.papermc.lib.PaperLib;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
@@ -96,12 +97,11 @@ public class CommandRegistration {
return fallbackCommands;
}
CommandMap commandMap = ReflectionUtil.getField(plugin.getServer().getPluginManager(), "commandMap");
CommandMap commandMap = PaperLib.isPaper() ? Bukkit.getCommandMap() : ReflectionUtil.getField(plugin.getServer().getPluginManager(), "commandMap");
if (commandMap == null) {
Bukkit.getServer().getLogger().severe(plugin.getDescription().getName()
+ ": Could not retrieve server CommandMap, using fallback instead!");
fallbackCommands = commandMap = new SimpleCommandMap(Bukkit.getServer());
Bukkit.getServer().getPluginManager().registerEvents(new FallbackRegistrationListener(fallbackCommands), plugin);
+ ": Could not retrieve server CommandMap");
throw new IllegalStateException("Failed to retrieve command map, make sure you are running supported server software");
} else {
serverCommandMap = commandMap;
}

View File

@@ -240,7 +240,7 @@ public enum BukkitAdapter {
Vector3 position = location;
return new org.bukkit.Location(
adapt((World) location.getExtent()),
position.getX(), position.getY(), position.getZ(),
position.x(), position.y(), position.z(),
location.getYaw(),
location.getPitch()
);
@@ -258,7 +258,7 @@ public enum BukkitAdapter {
checkNotNull(position);
return new org.bukkit.Location(
world,
position.getX(), position.getY(), position.getZ()
position.x(), position.y(), position.z()
);
}
@@ -274,7 +274,7 @@ public enum BukkitAdapter {
checkNotNull(position);
return new org.bukkit.Location(
world,
position.getX(), position.getY(), position.getZ()
position.x(), position.y(), position.z()
);
}
@@ -290,7 +290,7 @@ public enum BukkitAdapter {
checkNotNull(location);
return new org.bukkit.Location(
world,
location.getX(), location.getY(), location.getZ(),
location.x(), location.y(), location.z(),
location.getYaw(),
location.getPitch()
);

View File

@@ -40,7 +40,7 @@ class BukkitBiomeRegistry implements BiomeRegistry {
@Override
public Component getRichName(BiomeType biomeType) {
return TranslatableComponent.of(
TranslationManager.makeTranslationKey("biome", biomeType.getId())
TranslationManager.makeTranslationKey("biome", biomeType.id())
);
}

View File

@@ -45,7 +45,6 @@ import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.util.formatting.text.adapter.bukkit.TextAdapter;
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
@@ -61,6 +60,7 @@ import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.permissions.PermissionAttachment;
import org.enginehub.linbus.tree.LinCompoundTag;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -162,7 +162,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
final PlayerInventory inv = player.getInventory();
ItemStack newItem = BukkitAdapter.adapt(itemStack);
TaskManager.taskManager().sync(() -> {
if (itemStack.getType().getId().equalsIgnoreCase(WorldEdit.getInstance().getConfiguration().wandItem)) {
if (itemStack.getType().id().equalsIgnoreCase(WorldEdit.getInstance().getConfiguration().wandItem)) {
inv.remove(newItem);
}
final ItemStack item = player.getInventory().getItemInMainHand();
@@ -242,9 +242,9 @@ public class BukkitPlayer extends AbstractPlayerActor {
//FAWE end
return TaskManager.taskManager().sync(() -> player.teleport(new Location(
finalWorld,
pos.getX(),
pos.getY(),
pos.getZ(),
pos.x(),
pos.y(),
pos.z(),
yaw,
pitch
)));
@@ -267,7 +267,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
@Override
public void setGameMode(GameMode gameMode) {
player.setGameMode(org.bukkit.GameMode.valueOf(gameMode.getId().toUpperCase(Locale.ROOT)));
player.setGameMode(org.bukkit.GameMode.valueOf(gameMode.id().toUpperCase(Locale.ROOT)));
}
@Override
@@ -422,7 +422,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
@Override
public <B extends BlockStateHolder<B>> void sendFakeBlock(BlockVector3 pos, B block) {
Location loc = new Location(player.getWorld(), pos.getX(), pos.getY(), pos.getZ());
Location loc = new Location(player.getWorld(), pos.x(), pos.y(), pos.z());
if (block == null) {
player.sendBlockChange(loc, player.getWorld().getBlockAt(loc).getBlockData());
} else {
@@ -430,7 +430,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
if (block.getBlockType() == BlockTypes.STRUCTURE_BLOCK && block instanceof BaseBlock) {
CompoundBinaryTag nbt = ((BaseBlock) block).getNbt();
LinCompoundTag nbt = ((BaseBlock) block).getNbt();
if (nbt != null) {
adapter.sendFakeNBT(player, pos, nbt);
adapter.sendFakeOP(player);

View File

@@ -192,6 +192,9 @@ public class BukkitPlayerBlockBag extends BlockBag implements SlottableBlockBag
@Override
public BaseItem getItem(int slot) {
loadInventory();
if (items[slot] == null) {
return null;
}
return BukkitAdapter.adapt(items[slot]);
}

View File

@@ -42,6 +42,7 @@ import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.lifecycle.Lifecycled;
import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.registry.Registries;
import io.papermc.lib.PaperLib;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Server;
@@ -229,7 +230,7 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
//FAWE start
@Override
public String getId() {
public String id() {
return "intellectualsites:bukkit";
}
//FAWE end
@@ -258,6 +259,14 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
return SUPPORTED_SIDE_EFFECTS;
}
@Override
public long getTickCount() {
if (PaperLib.isPaper()) {
return Bukkit.getCurrentTick();
}
return super.getTickCount();
}
public void unregisterCommands() {
dynamicCommands.unregisterCommands();
}
@@ -298,9 +307,6 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
if (!tickFluid) {
return null;
}
if (Settings.settings().QUEUE.NO_TICK_FASTMODE && fastMode) {
return null;
}
return this.plugin.getBukkitImplAdapter().getTickingPostProcessor();
}
//FAWE end

View File

@@ -24,12 +24,12 @@ import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.fastasyncworldedit.core.util.TaskManager;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseItem;
@@ -118,9 +118,9 @@ public class BukkitWorld extends AbstractWorld {
HAS_MIN_Y = temp;
}
private WeakReference<World> worldRef;
protected WeakReference<World> worldRef;
//FAWE start
private final String worldNameRef;
protected final String worldNameRef;
//FAWE end
private final WorldNativeAccess<?, ?, ?> worldNativeAccess;
@@ -224,7 +224,7 @@ public class BukkitWorld extends AbstractWorld {
//FAWE end
@Override
public String getId() {
public String id() {
return getWorld().getName().replace(" ", "_").toLowerCase(Locale.ROOT);
}
@@ -247,7 +247,7 @@ public class BukkitWorld extends AbstractWorld {
//FAWE start - safe edit region
testCoords(pt);
//FAWE end
return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getLightLevel();
return getWorld().getBlockAt(pt.x(), pt.y(), pt.z()).getLightLevel();
}
@Override
@@ -284,7 +284,7 @@ public class BukkitWorld extends AbstractWorld {
return false;
}
Block block = getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
Block block = getWorld().getBlockAt(pt.x(), pt.y(), pt.z());
BlockState state = PaperLib.getBlockState(block, false).getState();
if (!(state instanceof InventoryHolder)) {
return false;
@@ -327,12 +327,12 @@ public class BukkitWorld extends AbstractWorld {
treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_MUSHROOM, TreeType.BROWN_MUSHROOM);
for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
if (treeTypeMapping.get(type) == null) {
LOGGER.error("No TreeType mapping for TreeGenerator.TreeType." + type);
//FAWE start
LOGGER.info("No TreeType mapping for TreeGenerator.TreeType." + type);
LOGGER.info("The above message is displayed because your FAWE version is newer than {}" +
" and contains features of future minecraft versions which do not exist in {} hence the tree type" +
"{} is not available. This is not an error. This version will work on your version of Minecraft." +
"This is an informative message only.", Bukkit.getVersion(), Bukkit.getVersion(), type);
" {} is not available. This is not an error. This version of FAWE will work on your version of " +
" Minecraft. This is an informative message only.", Bukkit.getVersion(), Bukkit.getVersion(), type);
//FAWE end
}
}
@@ -363,8 +363,8 @@ public class BukkitWorld extends AbstractWorld {
//FAWE end
World world = getWorld();
//FAWE start
int X = pt.getBlockX() >> 4;
int Z = pt.getBlockZ() >> 4;
int X = pt.x() >> 4;
int Z = pt.z() >> 4;
if (Fawe.isMainThread()) {
world.getChunkAt(X, Z);
} else if (PaperLib.isPaper()) {
@@ -413,7 +413,7 @@ public class BukkitWorld extends AbstractWorld {
public void fixAfterFastMode(Iterable<BlockVector2> chunks) {
World world = getWorld();
for (BlockVector2 chunkPos : chunks) {
world.refreshChunk(chunkPos.getBlockX(), chunkPos.getBlockZ());
world.refreshChunk(chunkPos.x(), chunkPos.z());
}
}
@@ -495,13 +495,13 @@ public class BukkitWorld extends AbstractWorld {
//FAWE start - safe edit region
testCoords(pt);
//FAWE end
getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).breakNaturally();
getWorld().getBlockAt(pt.x(), pt.y(), pt.z()).breakNaturally();
}
//FAWE start
@Override
public Collection<BaseItemStack> getBlockDrops(BlockVector3 position) {
return getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()).getDrops().stream()
return getWorld().getBlockAt(position.x(), position.y(), position.z()).getDrops().stream()
.map(BukkitAdapter::adapt).collect(Collectors.toList());
}
//FAWE end
@@ -538,7 +538,7 @@ public class BukkitWorld extends AbstractWorld {
}
}
if (WorldEditPlugin.getInstance().getLocalConfiguration().unsupportedVersionEditing) {
Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ());
Block bukkitBlock = getWorld().getBlockAt(position.x(), position.y(), position.z());
return BukkitAdapter.adapt(bukkitBlock.getBlockData());
} else {
throw new RuntimeException(new UnsupportedVersionEditException());
@@ -562,7 +562,7 @@ public class BukkitWorld extends AbstractWorld {
}
}
}
Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ());
Block bukkitBlock = getWorld().getBlockAt(position.x(), position.y(), position.z());
bukkitBlock.setBlockData(BukkitAdapter.adapt(block), sideEffects.doesApplyAny());
return true;
}
@@ -584,8 +584,8 @@ public class BukkitWorld extends AbstractWorld {
if (!Settings.settings().REGION_RESTRICTIONS_OPTIONS.RESTRICT_TO_SAFE_RANGE) {
return;
}
int x = position.getX();
int z = position.getZ();
int x = position.x();
int z = position.z();
if (x > 30000000 || z > 30000000 || x < -30000000 || z < -30000000) {
throw FaweCache.OUTSIDE_SAFE_REGION;
}
@@ -636,9 +636,9 @@ public class BukkitWorld extends AbstractWorld {
testCoords(position);
//FAWE end
if (HAS_3D_BIOMES) {
return BukkitAdapter.adapt(getWorld().getBiome(position.getBlockX(), position.getBlockY(), position.getBlockZ()));
return BukkitAdapter.adapt(getWorld().getBiome(position.x(), position.y(), position.z()));
} else {
return BukkitAdapter.adapt(getWorld().getBiome(position.getBlockX(), position.getBlockZ()));
return BukkitAdapter.adapt(getWorld().getBiome(position.x(), position.z()));
}
}
@@ -649,9 +649,9 @@ public class BukkitWorld extends AbstractWorld {
testCoords(position);
//FAWE end
if (HAS_3D_BIOMES) {
getWorld().setBiome(position.getBlockX(), position.getBlockY(), position.getBlockZ(), BukkitAdapter.adapt(biome));
getWorld().setBiome(position.x(), position.y(), position.z(), BukkitAdapter.adapt(biome));
} else {
getWorld().setBiome(position.getBlockX(), position.getBlockZ(), BukkitAdapter.adapt(biome));
getWorld().setBiome(position.x(), position.z(), BukkitAdapter.adapt(biome));
}
return true;
}
@@ -665,7 +665,7 @@ public class BukkitWorld extends AbstractWorld {
}
@Override
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
public boolean tile(int x, int y, int z, FaweCompoundTag tile) throws WorldEditException {
return false;
}

View File

@@ -27,6 +27,7 @@ import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.platform.SessionIdleEvent;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.internal.event.InteractionDebouncer;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World;
@@ -55,6 +56,7 @@ import java.util.Optional;
public class WorldEditListener implements Listener {
private final WorldEditPlugin plugin;
private final InteractionDebouncer debouncer;
/**
* Construct the object.
@@ -63,6 +65,7 @@ public class WorldEditListener implements Listener {
*/
public WorldEditListener(WorldEditPlugin plugin) {
this.plugin = plugin;
debouncer = new InteractionDebouncer(plugin.getInternalPlatform());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@@ -128,62 +131,58 @@ public class WorldEditListener implements Listener {
*/
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
if (!plugin.getInternalPlatform().isHookingEvents()) {
return;
}
if (event.useItemInHand() == Result.DENY) {
return;
}
if (event.getHand() == EquipmentSlot.OFF_HAND) {
if (!plugin.getInternalPlatform().isHookingEvents()
|| event.useItemInHand() == Result.DENY
|| event.getHand() == EquipmentSlot.OFF_HAND
|| event.getAction() == Action.PHYSICAL) {
return;
}
final Player player = plugin.wrapPlayer(event.getPlayer());
if (event.getAction() != Action.LEFT_CLICK_BLOCK) {
Optional<Boolean> previousResult = debouncer.getDuplicateInteractionResult(player);
if (previousResult.isPresent()) {
if (previousResult.get()) {
event.setCancelled(true);
}
return;
}
}
final World world = player.getWorld();
final WorldEdit we = plugin.getWorldEdit();
final Direction direction = BukkitAdapter.adapt(event.getBlockFace());
final Block clickedBlock = event.getClickedBlock();
final Location pos = clickedBlock == null ? null : new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
Action action = event.getAction();
if (action == Action.LEFT_CLICK_BLOCK) {
final Block clickedBlock = event.getClickedBlock();
final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
if (we.handleBlockLeftClick(player, pos, direction)) {
event.setCancelled(true);
}
if (we.handleArmSwing(player)) {
event.setCancelled(true);
}
} else if (action == Action.LEFT_CLICK_AIR) {
if (we.handleArmSwing(player)) {
event.setCancelled(true);
}
} else if (action == Action.RIGHT_CLICK_BLOCK) {
final Block clickedBlock = event.getClickedBlock();
final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
if (we.handleBlockRightClick(player, pos, direction)) {
event.setCancelled(true);
}
if (we.handleRightClick(player)) {
event.setCancelled(true);
}
} else if (action == Action.RIGHT_CLICK_AIR) {
if (we.handleRightClick(player)) {
event.setCancelled(true);
}
boolean result = false;
switch (event.getAction()) {
case LEFT_CLICK_BLOCK:
result = we.handleBlockLeftClick(player, pos, direction) || we.handleArmSwing(player);
break;
case LEFT_CLICK_AIR:
result = we.handleArmSwing(player);
break;
case RIGHT_CLICK_BLOCK:
result = we.handleBlockRightClick(player, pos, direction) || we.handleRightClick(player);
break;
case RIGHT_CLICK_AIR:
result = we.handleRightClick(player);
break;
default:
break;
}
debouncer.setLastInteraction(player, result);
if (result) {
event.setCancelled(true);
}
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
debouncer.clearInteraction(plugin.wrapPlayer(event.getPlayer()));
plugin.getWorldEdit().getEventBus().post(new SessionIdleEvent(new BukkitPlayer.SessionKeyImpl(event.getPlayer())));
}

View File

@@ -32,9 +32,11 @@ import com.sk89q.wepif.PermissionsResolverManager;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditManifest;
import com.sk89q.worldedit.bukkit.adapter.AdapterLoadException;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplLoader;
import com.sk89q.worldedit.bukkit.adapter.Refraction;
import com.sk89q.worldedit.event.platform.CommandEvent;
import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
@@ -90,7 +92,9 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.jar.Attributes;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.internal.anvil.ChunkDeleter.DELCHUNKS_FILE_NAME;
@@ -120,7 +124,6 @@ public class WorldEditPlugin extends JavaPlugin {
public void onLoad() {
//FAWE start
this.bukkitConsoleCommandSender = new BukkitCommandSender(this, Bukkit.getConsoleSender());
// This is already covered by Spigot, however, a more pesky warning with a proper explanation over "Ambiguous plugin name..." can't hurt.
Plugin[] plugins = Bukkit.getServer().getPluginManager().getPlugins();
for (Plugin p : plugins) {
@@ -132,30 +135,82 @@ public class WorldEditPlugin extends JavaPlugin {
}
}
//FAWE end
//FAWE start
final Attributes attributes = WorldEditManifest.readAttributes();
Objects.requireNonNull(attributes, "Could not retrieve manifest attributes");
final String type = attributes.getValue("FAWE-Plugin-Jar-Type");
Objects.requireNonNull(type, "Could not determine plugin jar type");
if (PaperLib.isPaper()) {
if (PaperLib.getMinecraftVersion() < 20 || (PaperLib.getMinecraftVersion() == 20 && PaperLib.getMinecraftPatchVersion() < 5)) {
if (type.equals("mojang") && !Refraction.isMojangMapped()) {
throw new IllegalStateException(
"""
**********************************************
** You are using the wrong FAWE jar for your Minecraft version.
** Download the correct FAWE jar from Modrinth: https://modrinth.com/plugin/fastasyncworldedit/
**********************************************"""
);
}
} else if (PaperLib.getMinecraftVersion() > 20 || (PaperLib.getMinecraftVersion() == 20 && PaperLib.getMinecraftPatchVersion() >= 5)) {
if (type.equals("spigot")) {
LOGGER.warn(
"""
**********************************************
** You are using the Spigot-mapped FAWE jar on a modern Paper version.
** This will result in slower first-run times and wasted disk space from plugin remapping.
** Download the Paper FAWE jar from Modrinth to avoid this: https://modrinth.com/plugin/fastasyncworldedit/
**********************************************"""
);
}
}
} else {
if (type.equals("mojang")) {
throw new IllegalStateException(
"""
**********************************************
** You are attempting to run the Paper FAWE jar on a Spigot server.
** Either switch to Paper (https://papermc.io), or download the correct FAWE jar for your platform
** from Modrinth: https://modrinth.com/plugin/fastasyncworldedit/
**********************************************"""
);
}
}
//FAWE end
INSTANCE = this;
//noinspection ResultOfMethodCallIgnored
getDataFolder().mkdirs();
WorldEdit worldEdit = WorldEdit.getInstance();
// Setup platform
platform = new BukkitServerInterface(this, getServer());
worldEdit.getPlatformManager().register(platform);
//FAWE start - Migrate from config-legacy to worldedit-config
migrateLegacyConfig();
//FAWE end
//FAWE start - Modify WorldEdit config name
config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "worldedit-config.yml"), true), this);
// Load config before we say we've loaded platforms as it is used in listeners of the event
// Load config in onLoad to ensure it is loaded before FAWE settings to allow (inelegant) copying of values across
// where needed
config.load();
//FAWE end
WorldEdit worldEdit = WorldEdit.getInstance();
// Setup platform
platform = new BukkitServerInterface(this, getServer());
worldEdit.getPlatformManager().register(platform);
//FAWE start - Setup permission attachments
permissionAttachmentManager = new BukkitPermissionAttachmentManager(this);
//FAWE end
//FAWE start - initialise bukkitConsoleCommandSender later
this.bukkitConsoleCommandSender = new BukkitCommandSender(this, Bukkit.getConsoleSender());
//FAWE end
Path delChunks = Paths.get(getDataFolder().getPath(), DELCHUNKS_FILE_NAME);
if (Files.exists(delChunks)) {
ChunkDeleter.runFromFile(delChunks, true);
@@ -189,8 +244,6 @@ public class WorldEditPlugin extends JavaPlugin {
new FaweBukkit(this);
//FAWE end
config.load(); // Load config before we say we've loaded platforms as it is used in listeners of the event
WorldEdit.getInstance().getEventBus().post(new PlatformsRegisteredEvent());
PermissionsResolverManager.initialize(this); // Setup permission resolver
@@ -559,17 +612,17 @@ public class WorldEditPlugin extends JavaPlugin {
public BukkitPlayer wrapPlayer(Player player) {
//FAWE start - Use cache over returning a direct BukkitPlayer
BukkitPlayer wePlayer = getCachedPlayer(player);
if (wePlayer == null) {
synchronized (player) {
wePlayer = getCachedPlayer(player);
if (wePlayer == null) {
wePlayer = new BukkitPlayer(this, player);
player.setMetadata("WE", new FixedMetadataValue(this, wePlayer));
return wePlayer;
}
}
if (wePlayer != null) {
return wePlayer;
}
synchronized (player) {
BukkitPlayer bukkitPlayer = getCachedPlayer(player);
if (bukkitPlayer == null) {
bukkitPlayer = new BukkitPlayer(this, player);
player.setMetadata("WE", new FixedMetadataValue(this, bukkitPlayer));
}
return bukkitPlayer;
}
return wePlayer;
//FAWE end
}

View File

@@ -27,7 +27,7 @@ import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IBatchProcessor;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
import com.sk89q.jnbt.AdventureNBTConverter;
import com.sk89q.jnbt.LinBusConverter;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack;
@@ -35,14 +35,13 @@ import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.nbt.BinaryTag;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.RegenOptions;
import com.sk89q.worldedit.world.biome.BiomeType;
@@ -61,6 +60,8 @@ import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.enginehub.linbus.tree.LinCompoundTag;
import org.enginehub.linbus.tree.LinTag;
import javax.annotation.Nullable;
import java.util.Arrays;
@@ -110,7 +111,7 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
BlockState getBlock(Location location);
/**
* Get the block at the given location.
* Get the block with NBT data at the given location.
*
* @param location the location
* @return the block
@@ -183,7 +184,7 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
* @param pos The position
* @param nbtData The NBT Data
*/
void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData);
void sendFakeNBT(Player player, BlockVector3 pos, LinCompoundTag nbtData);
/**
* Make the client think it has operator status.
@@ -280,6 +281,46 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
throw new UnsupportedOperationException("This adapter does not support clearing block contents.");
}
/**
* Set the biome at a location.
*
* @param location the location
* @param biome the new biome
*/
default void setBiome(Location location, BiomeType biome) {
throw new UnsupportedOperationException("This adapter does not support custom biomes.");
}
/**
* Gets the current biome at a location.
*
* @param location the location
* @return the biome
*/
default BiomeType getBiome(Location location) {
throw new UnsupportedOperationException("This adapter does not support custom biomes.");
}
/**
* Initialize registries that require NMS access.
*/
default void initializeRegistries() {
}
/**
* Sends biome updates for the given chunks.
*
* <p>This doesn't modify biomes at all, it just sends the current state of the biomes
* in the world to all of the nearby players, updating the visual representation of the
* biomes on their clients.</p>
*
* @param world the world
* @param chunks a list of chunk coordinates to send biome updates for
*/
default void sendBiomeUpdates(World world, Iterable<BlockVector2> chunks) {
}
//FAWE start
default BlockMaterial getMaterial(BlockType blockType) {
return getMaterial(blockType.getDefaultState());
@@ -291,11 +332,11 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
@Deprecated
default Tag toNative(T foreign) {
return AdventureNBTConverter.fromAdventure(toNativeBinary(foreign));
return LinBusConverter.toJnbtTag(toNativeLin(foreign));
}
default BinaryTag toNativeBinary(T foreign) {
return toNative(foreign).asBinaryTag();
default LinTag<?> toNativeLin(T foreign) {
return toNative(foreign).toLinTag();
}
@Deprecated
@@ -303,14 +344,14 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
if (foreign == null) {
return null;
}
return fromNativeBinary(foreign.asBinaryTag());
return fromNativeLin(foreign.toLinTag());
}
default T fromNativeBinary(BinaryTag foreign) {
default T fromNativeLin(LinTag<?> foreign) {
if (foreign == null) {
return null;
}
return fromNative(AdventureNBTConverter.fromAdventure(foreign));
return fromNative(LinBusConverter.toJnbtTag(foreign));
}
@Nullable

View File

@@ -165,9 +165,11 @@ public class BukkitImplLoader {
* @throws AdapterLoadException thrown if no adapter could be found
*/
public BukkitImplAdapter loadAdapter() throws AdapterLoadException {
// FAWE - do not initialize classes on lookup
final ClassLoader classLoader = this.getClass().getClassLoader();
for (String className : adapterCandidates) {
try {
Class<?> cls = Class.forName(className);
Class<?> cls = Class.forName(className, false, classLoader);
if (cls.isSynthetic()) {
continue;
}

View File

@@ -42,6 +42,10 @@ public class Refraction {
return IS_MOJANG_MAPPED ? mojangName : spigotName;
}
public static boolean isMojangMapped() {
return IS_MOJANG_MAPPED;
}
private Refraction() {
}

View File

@@ -24,11 +24,13 @@ limits:
max-polygonal-points:
default: -1
maximum: 20
# radius, superpickaxe, brush radius are ignored, use FAWE config limits
max-radius: -1
max-super-pickaxe-size: 5
max-brush-radius: 100
butcher-radius:
default: -1
# Ignored, use FAWE config limits
maximum: -1
disallowed-blocks:
- "minecraft:wheat"

View File

@@ -21,7 +21,7 @@ permissions:
default: op
children:
fawe.bypass.regions: true
fawe.limit.*: true
fawe.limit.unlimited: true
fawe.tips:
default: op
fawe.admin:
@@ -135,6 +135,7 @@ permissions:
worldedit.brush.options.transform: true
worldedit.brush.options.scroll: true
worldedit.brush.options.visualize: true
worldedit.tool.none: true
worldedit.tool.deltree: true
worldedit.tool.farwand: true
worldedit.tool.lrbuild: true

View File

@@ -20,6 +20,7 @@
package com.sk89q.wepif;
import com.destroystokyo.paper.profile.PlayerProfile;
import io.papermc.paper.persistence.PersistentDataContainerView;
import org.bukkit.BanEntry;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -144,6 +145,11 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible {
return false;
}
@Override
public boolean isConnected() {
return false;
}
@Override
public String getName() {
return "Tester";
@@ -240,6 +246,11 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public @Nullable Location getRespawnLocation() {
return null;
}
@Override
public void incrementStatistic(@Nonnull Statistic statistic) throws IllegalArgumentException {
@@ -360,4 +371,9 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible {
return null;
}
@Override
public @Nullable Location getLocation() {
return null;
}
}