Merge remote-tracking branch 'upstream/main'
# Conflicts: # build.gradle.kts # settings.gradle.kts # worldedit-bukkit/build.gradle.kts
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,6 @@ package com.fastasyncworldedit.bukkit.adapter;
|
||||
|
||||
public interface BukkitGetBlocks {
|
||||
|
||||
void send(int mask, boolean lighting);
|
||||
void send();
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
|
||||
@@ -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())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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())));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,10 @@ public class Refraction {
|
||||
return IS_MOJANG_MAPPED ? mojangName : spigotName;
|
||||
}
|
||||
|
||||
public static boolean isMojangMapped() {
|
||||
return IS_MOJANG_MAPPED;
|
||||
}
|
||||
|
||||
private Refraction() {
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user