Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-11-19 17:30:08 +01:00
Merge branch 'main' into feat/feature-placement
Dieser Commit ist enthalten in:
Commit
7150f53572
@ -34,7 +34,7 @@ logger.lifecycle("""
|
|||||||
*******************************************
|
*******************************************
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var rootVersion by extra("2.8.2")
|
var rootVersion by extra("2.8.3")
|
||||||
var snapshot by extra("SNAPSHOT")
|
var snapshot by extra("SNAPSHOT")
|
||||||
var revision: String by extra("")
|
var revision: String by extra("")
|
||||||
var buildNumber by extra("")
|
var buildNumber by extra("")
|
||||||
|
@ -22,7 +22,7 @@ val properties = Properties().also { props ->
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(gradleApi())
|
implementation(gradleApi())
|
||||||
implementation("org.ajoberstar.grgit:grgit-gradle:5.2.0")
|
implementation("org.ajoberstar.grgit:grgit-gradle:5.2.1")
|
||||||
implementation("com.github.johnrengelman:shadow:8.1.1")
|
implementation("com.github.johnrengelman:shadow:8.1.1")
|
||||||
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5")
|
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5")
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@ mapmanager = "1.8.0-SNAPSHOT"
|
|||||||
griefprevention = "16.18.1"
|
griefprevention = "16.18.1"
|
||||||
griefdefender = "2.1.0-SNAPSHOT"
|
griefdefender = "2.1.0-SNAPSHOT"
|
||||||
residence = "4.5._13.1"
|
residence = "4.5._13.1"
|
||||||
towny = "0.99.6.0"
|
towny = "0.100.0.1"
|
||||||
plotsquared = "7.0.0"
|
plotsquared = "7.1.0"
|
||||||
|
|
||||||
# Third party
|
# Third party
|
||||||
bstats = "3.0.2"
|
bstats = "3.0.2"
|
||||||
@ -23,7 +23,7 @@ sparsebitset = "1.3"
|
|||||||
parallelgzip = "1.0.5"
|
parallelgzip = "1.0.5"
|
||||||
adventure = "4.14.0"
|
adventure = "4.14.0"
|
||||||
adventure-bukkit = "4.3.1"
|
adventure-bukkit = "4.3.1"
|
||||||
checkerqual = "3.39.0"
|
checkerqual = "3.40.0"
|
||||||
truezip = "6.8.4"
|
truezip = "6.8.4"
|
||||||
auto-value = "1.10.4"
|
auto-value = "1.10.4"
|
||||||
findbugs = "3.0.2"
|
findbugs = "3.0.2"
|
||||||
@ -35,7 +35,7 @@ jlibnoise = "1.0.0"
|
|||||||
jchronic = "0.2.4a"
|
jchronic = "0.2.4a"
|
||||||
lz4-java = "1.8.0"
|
lz4-java = "1.8.0"
|
||||||
lz4-stream = "1.0.0"
|
lz4-stream = "1.0.0"
|
||||||
commons-cli = "1.5.0"
|
commons-cli = "1.6.0"
|
||||||
paperlib = "1.0.8"
|
paperlib = "1.0.8"
|
||||||
paster = "1.1.5"
|
paster = "1.1.5"
|
||||||
vault = "1.7.1"
|
vault = "1.7.1"
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
|
||||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||||
@ -10,17 +9,14 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
|||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.PaperweightAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.PaperweightAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
|
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
|
||||||
@ -40,7 +36,6 @@ import com.sk89q.worldedit.registry.state.Property;
|
|||||||
import com.sk89q.worldedit.util.Direction;
|
import com.sk89q.worldedit.util.Direction;
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
import com.sk89q.worldedit.util.SideEffect;
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
import com.sk89q.worldedit.util.SideEffectSet;
|
||||||
import com.sk89q.worldedit.util.TreeGenerator;
|
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||||
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||||
@ -92,15 +87,12 @@ import net.minecraft.world.level.levelgen.structure.StructureStart;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.TreeType;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
|
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
|
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.block.CraftBlockState;
|
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity;
|
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity;
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
|
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
|
||||||
@ -125,8 +117,7 @@ import java.util.function.Supplier;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
|
||||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
@ -250,11 +241,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public BlockState getBlock(Location location) {
|
public BlockState getBlock(Location location) {
|
||||||
Preconditions.checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
int y = location.getBlockY();
|
int y = location.getBlockY();
|
||||||
int z = location.getBlockZ();
|
int z = location.getBlockZ();
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||||
@ -270,12 +260,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public BaseBlock getFullBlock(final Location location) {
|
public BaseBlock getFullBlock(final Location location) {
|
||||||
Preconditions.checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
int y = location.getBlockY();
|
int y = location.getBlockY();
|
||||||
int z = location.getBlockZ();
|
int z = location.getBlockZ();
|
||||||
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||||
@ -359,10 +348,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||||
return new PaperweightFaweWorldNativeAccess(
|
return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
|
||||||
this,
|
|
||||||
new WeakReference<>(((CraftWorld) world).getHandle())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -507,7 +493,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
ServerLevel nmsWorld = getServerLevel(world);
|
||||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||||
if (map != null && map.wasAccessibleSinceLastSave()) {
|
if (map != null && map.wasAccessibleSinceLastSave()) {
|
||||||
boolean flag = false;
|
boolean flag = false;
|
||||||
@ -545,7 +531,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
||||||
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
||||||
return blockState1.hasPostProcess(
|
return blockState1.hasPostProcess(
|
||||||
((CraftWorld) world).getHandle(),
|
getServerLevel(world),
|
||||||
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -561,47 +547,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean generateTree(
|
protected void preCaptureStates(final ServerLevel serverLevel) {
|
||||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
|
||||||
org.bukkit.World bukkitWorld
|
|
||||||
) {
|
|
||||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
|
||||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
|
||||||
blockVector3 = blockVector3.add(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
|
||||||
}
|
|
||||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
|
||||||
final BlockVector3 finalBlockVector = blockVector3;
|
|
||||||
// Sync to main thread to ensure no clashes occur
|
|
||||||
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
|
|
||||||
serverLevel.captureTreeGeneration = true;
|
serverLevel.captureTreeGeneration = true;
|
||||||
serverLevel.captureBlockStates = true;
|
serverLevel.captureBlockStates = true;
|
||||||
try {
|
|
||||||
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
|
|
||||||
} finally {
|
@Override
|
||||||
|
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
|
||||||
|
return new ArrayList<>(serverLevel.capturedBlockStates.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
|
||||||
serverLevel.captureBlockStates = false;
|
serverLevel.captureBlockStates = false;
|
||||||
serverLevel.captureTreeGeneration = false;
|
serverLevel.captureTreeGeneration = false;
|
||||||
serverLevel.capturedBlockStates.clear();
|
serverLevel.capturedBlockStates.clear();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
if (placed == null || placed.isEmpty()) {
|
@Override
|
||||||
return false;
|
protected ServerLevel getServerLevel(final World world) {
|
||||||
}
|
return ((CraftWorld) world).getHandle();
|
||||||
for (CraftBlockState craftBlockState : placed.values()) {
|
|
||||||
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
|
|
||||||
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -766,7 +731,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
|
public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
|
||||||
// Quickly add each entity to a list copy.
|
// Quickly add each entity to a list copy.
|
||||||
List<Entity> mcEntities = new ArrayList<>();
|
List<Entity> mcEntities = new ArrayList<>();
|
||||||
((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
|
getServerLevel(world).entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
|
||||||
|
|
||||||
List<org.bukkit.entity.Entity> list = new ArrayList<>();
|
List<org.bukkit.entity.Entity> list = new ArrayList<>();
|
||||||
mcEntities.forEach((mcEnt) -> {
|
mcEntities.forEach((mcEnt) -> {
|
||||||
|
@ -1,17 +1,8 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
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 net.minecraft.server.MCUtil;
|
import net.minecraft.server.MCUtil;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||||
@ -19,27 +10,18 @@ import net.minecraft.server.level.TicketType;
|
|||||||
import net.minecraft.util.Unit;
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
|
|
||||||
public class PaperweightStarlightRelighter implements Relighter {
|
public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
|
||||||
|
|
||||||
public static final MethodHandle RELIGHT;
|
|
||||||
private 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 static final MethodHandle RELIGHT;
|
||||||
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||||
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
|
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
|
||||||
|
|
||||||
@ -58,22 +40,36 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
IntConsumer.class
|
IntConsumer.class
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
tmp = MethodHandles.dropReturn(tmp);
|
||||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||||
LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e);
|
LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e);
|
||||||
}
|
}
|
||||||
RELIGHT = tmp;
|
RELIGHT = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ServerLevel serverLevel;
|
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
super(serverLevel, queue);
|
||||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
}
|
||||||
private final ReentrantLock areaLock = new ReentrantLock();
|
|
||||||
private final NMSRelighter delegate;
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@Override
|
||||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
protected ChunkPos createChunkPos(final long chunkKey) {
|
||||||
this.serverLevel = serverLevel;
|
return new ChunkPos(chunkKey);
|
||||||
this.delegate = new NMSRelighter(queue);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long asLong(final int chunkX, final int chunkZ) {
|
||||||
|
return ChunkPos.asLong(chunkX, chunkZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
|
||||||
|
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
|
||||||
|
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||||
|
FAWE_TICKET,
|
||||||
|
chunkPos,
|
||||||
|
LIGHT_LEVEL,
|
||||||
|
Unit.INSTANCE
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isUsable() {
|
public static boolean isUsable() {
|
||||||
@ -81,95 +77,13 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
protected void invokeRelight(
|
||||||
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(ChunkPos.asLong(cx, cz));
|
|
||||||
} finally {
|
|
||||||
areaLock.unlock();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addLightUpdate(int x, int y, int z) {
|
|
||||||
delegate.addLightUpdate(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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).
|
|
||||||
*/
|
|
||||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
|
||||||
// convert from long keys to ChunkPos
|
|
||||||
Set<ChunkPos> coords = new HashSet<>();
|
|
||||||
LongIterator iterator = chunks.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
coords.add(new ChunkPos(iterator.nextLong()));
|
|
||||||
}
|
|
||||||
TaskManager.taskManager().task(() -> {
|
|
||||||
// trigger chunk load and apply ticket on main thread
|
|
||||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
|
||||||
for (ChunkPos pos : coords) {
|
|
||||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
|
||||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
|
||||||
FAWE_TICKET,
|
|
||||||
pos,
|
|
||||||
LIGHT_LEVEL,
|
|
||||||
Unit.INSTANCE
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// 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
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void invokeRelight(
|
|
||||||
Set<ChunkPos> coords,
|
Set<ChunkPos> coords,
|
||||||
Consumer<ChunkPos> chunkCallback,
|
Consumer<ChunkPos> chunkCallback,
|
||||||
IntConsumer processCallback
|
IntConsumer processCallback
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
int unused = (int) RELIGHT.invokeExact(
|
RELIGHT.invokeExact(
|
||||||
serverLevel.getChunkSource().getLightEngine(),
|
serverLevel.getChunkSource().getLightEngine(),
|
||||||
coords,
|
coords,
|
||||||
chunkCallback, // callback per chunk
|
chunkCallback, // callback per chunk
|
||||||
@ -184,7 +98,7 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
* Allow the server to unload the chunks again.
|
* Allow the server to unload the chunks again.
|
||||||
* Also, if chunk packets are sent delayed, we need to do that here
|
* Also, if chunk packets are sent delayed, we need to do that here
|
||||||
*/
|
*/
|
||||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
protected void postProcessChunks(Set<ChunkPos> coords) {
|
||||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||||
for (ChunkPos pos : coords) {
|
for (ChunkPos pos : coords) {
|
||||||
int x = pos.x;
|
int x = pos.x;
|
||||||
@ -196,44 +110,4 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
|||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@ -15,9 +14,7 @@ import javax.annotation.Nonnull;
|
|||||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nonnull
|
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
|
||||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||||
if (w == null) {
|
if (w == null) {
|
||||||
return NullRelighter.INSTANCE;
|
return NullRelighter.INSTANCE;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
|
||||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||||
@ -10,17 +9,14 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
|||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R2.PaperweightAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R2.PaperweightAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag;
|
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag;
|
||||||
@ -40,7 +36,6 @@ import com.sk89q.worldedit.registry.state.Property;
|
|||||||
import com.sk89q.worldedit.util.Direction;
|
import com.sk89q.worldedit.util.Direction;
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
import com.sk89q.worldedit.util.SideEffect;
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
import com.sk89q.worldedit.util.SideEffectSet;
|
||||||
import com.sk89q.worldedit.util.TreeGenerator;
|
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||||
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||||
@ -92,15 +87,12 @@ import net.minecraft.world.level.levelgen.structure.StructureStart;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.TreeType;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
|
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
|
||||||
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
|
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
|
||||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_18_R2.block.CraftBlockState;
|
|
||||||
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
|
||||||
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity;
|
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity;
|
||||||
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
|
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
|
||||||
@ -124,8 +116,7 @@ import java.util.function.Supplier;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
|
||||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
@ -248,11 +239,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public BlockState getBlock(Location location) {
|
public BlockState getBlock(Location location) {
|
||||||
Preconditions.checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
int y = location.getBlockY();
|
int y = location.getBlockY();
|
||||||
int z = location.getBlockZ();
|
int z = location.getBlockZ();
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||||
@ -268,12 +258,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public BaseBlock getFullBlock(final Location location) {
|
public BaseBlock getFullBlock(final Location location) {
|
||||||
Preconditions.checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
int y = location.getBlockY();
|
int y = location.getBlockY();
|
||||||
int z = location.getBlockZ();
|
int z = location.getBlockZ();
|
||||||
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||||
@ -350,10 +339,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||||
return new PaperweightFaweWorldNativeAccess(
|
return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
|
||||||
this,
|
|
||||||
new WeakReference<>(((CraftWorld) world).getHandle())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -498,7 +484,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
ServerLevel nmsWorld = getServerLevel(world);
|
||||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||||
if (map != null && map.wasAccessibleSinceLastSave()) {
|
if (map != null && map.wasAccessibleSinceLastSave()) {
|
||||||
// PlayerChunk.d players = map.players;
|
// PlayerChunk.d players = map.players;
|
||||||
@ -535,7 +521,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
||||||
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
||||||
return blockState1.hasPostProcess(
|
return blockState1.hasPostProcess(
|
||||||
((CraftWorld) world).getHandle(),
|
getServerLevel(world),
|
||||||
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -551,47 +537,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean generateTree(
|
protected void preCaptureStates(final ServerLevel serverLevel) {
|
||||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
|
||||||
org.bukkit.World bukkitWorld
|
|
||||||
) {
|
|
||||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
|
||||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
|
||||||
blockVector3 = blockVector3.add(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
|
||||||
}
|
|
||||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
|
||||||
final BlockVector3 finalBlockVector = blockVector3;
|
|
||||||
// Sync to main thread to ensure no clashes occur
|
|
||||||
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
|
|
||||||
serverLevel.captureTreeGeneration = true;
|
serverLevel.captureTreeGeneration = true;
|
||||||
serverLevel.captureBlockStates = true;
|
serverLevel.captureBlockStates = true;
|
||||||
try {
|
|
||||||
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
|
|
||||||
} finally {
|
@Override
|
||||||
|
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
|
||||||
|
return new ArrayList<>(serverLevel.capturedBlockStates.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
|
||||||
serverLevel.captureBlockStates = false;
|
serverLevel.captureBlockStates = false;
|
||||||
serverLevel.captureTreeGeneration = false;
|
serverLevel.captureTreeGeneration = false;
|
||||||
serverLevel.capturedBlockStates.clear();
|
serverLevel.capturedBlockStates.clear();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
if (placed == null || placed.isEmpty()) {
|
@Override
|
||||||
return false;
|
protected ServerLevel getServerLevel(final World world) {
|
||||||
}
|
return ((CraftWorld) world).getHandle();
|
||||||
for (CraftBlockState craftBlockState : placed.values()) {
|
|
||||||
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
|
|
||||||
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -755,7 +720,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
|
public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
|
||||||
// Quickly add each entity to a list copy.
|
// Quickly add each entity to a list copy.
|
||||||
List<Entity> mcEntities = new ArrayList<>();
|
List<Entity> mcEntities = new ArrayList<>();
|
||||||
((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
|
getServerLevel(world).entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
|
||||||
|
|
||||||
List<org.bukkit.entity.Entity> list = new ArrayList<>();
|
List<org.bukkit.entity.Entity> list = new ArrayList<>();
|
||||||
mcEntities.forEach((mcEnt) -> {
|
mcEntities.forEach((mcEnt) -> {
|
||||||
|
@ -1,140 +1,51 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
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 net.minecraft.server.MCUtil;
|
import net.minecraft.server.MCUtil;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.TicketType;
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.Unit;
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
|
|
||||||
public class PaperweightStarlightRelighter implements Relighter {
|
public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
|
||||||
|
|
||||||
private 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 static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||||
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
|
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
|
||||||
|
|
||||||
|
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
|
||||||
private final ServerLevel serverLevel;
|
super(serverLevel, queue);
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
|
||||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
|
||||||
private final ReentrantLock areaLock = new ReentrantLock();
|
|
||||||
private final NMSRelighter delegate;
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
|
||||||
this.serverLevel = serverLevel;
|
|
||||||
this.delegate = new NMSRelighter(queue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
protected ChunkPos createChunkPos(final long chunkKey) {
|
||||||
areaLock.lock();
|
return new ChunkPos(chunkKey);
|
||||||
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(ChunkPos.asLong(cx, cz));
|
|
||||||
} finally {
|
|
||||||
areaLock.unlock();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addLightUpdate(int x, int y, int z) {
|
protected long asLong(final int chunkX, final int chunkZ) {
|
||||||
delegate.addLightUpdate(x, y, z);
|
return ChunkPos.asLong(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
@Override
|
||||||
public void fixLightingSafe(boolean sky) {
|
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
|
||||||
this.areaLock.lock();
|
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
|
||||||
try {
|
|
||||||
if (regions.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LongSet first = regions.removeFirst();
|
|
||||||
fixLighting(first, () -> fixLightingSafe(true));
|
|
||||||
} finally {
|
|
||||||
this.areaLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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).
|
|
||||||
*/
|
|
||||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
|
||||||
// convert from long keys to ChunkPos
|
|
||||||
Set<ChunkPos> coords = new HashSet<>();
|
|
||||||
LongIterator iterator = chunks.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
coords.add(new ChunkPos(iterator.nextLong()));
|
|
||||||
}
|
|
||||||
TaskManager.taskManager().task(() -> {
|
|
||||||
// trigger chunk load and apply ticket on main thread
|
|
||||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
|
||||||
for (ChunkPos pos : coords) {
|
|
||||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
|
||||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||||
FAWE_TICKET,
|
FAWE_TICKET,
|
||||||
pos,
|
chunkPos,
|
||||||
LIGHT_LEVEL,
|
LIGHT_LEVEL,
|
||||||
Unit.INSTANCE
|
Unit.INSTANCE
|
||||||
))
|
));
|
||||||
);
|
|
||||||
}
|
|
||||||
// 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
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invokeRelight(
|
protected void invokeRelight(
|
||||||
Set<ChunkPos> coords,
|
Set<ChunkPos> coords,
|
||||||
Consumer<ChunkPos> chunkCallback,
|
Consumer<ChunkPos> chunkCallback,
|
||||||
IntConsumer processCallback
|
IntConsumer processCallback
|
||||||
@ -150,7 +61,7 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
* Allow the server to unload the chunks again.
|
* Allow the server to unload the chunks again.
|
||||||
* Also, if chunk packets are sent delayed, we need to do that here
|
* Also, if chunk packets are sent delayed, we need to do that here
|
||||||
*/
|
*/
|
||||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
protected void postProcessChunks(Set<ChunkPos> coords) {
|
||||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||||
for (ChunkPos pos : coords) {
|
for (ChunkPos pos : coords) {
|
||||||
int x = pos.x;
|
int x = pos.x;
|
||||||
@ -162,44 +73,4 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
|||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@ -15,9 +14,7 @@ import javax.annotation.Nonnull;
|
|||||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nonnull
|
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
|
||||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||||
if (w == null) {
|
if (w == null) {
|
||||||
return NullRelighter.INSTANCE;
|
return NullRelighter.INSTANCE;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
|
||||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||||
@ -10,16 +9,13 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
|||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R3.PaperweightAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R3.PaperweightAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt.PaperweightLazyCompoundTag;
|
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt.PaperweightLazyCompoundTag;
|
||||||
@ -39,7 +35,6 @@ import com.sk89q.worldedit.registry.state.Property;
|
|||||||
import com.sk89q.worldedit.util.Direction;
|
import com.sk89q.worldedit.util.Direction;
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
import com.sk89q.worldedit.util.SideEffect;
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
import com.sk89q.worldedit.util.SideEffectSet;
|
||||||
import com.sk89q.worldedit.util.TreeGenerator;
|
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||||
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||||
@ -88,14 +83,11 @@ import net.minecraft.world.level.levelgen.structure.StructureStart;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.TreeType;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockState;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity;
|
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity;
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
|
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
|
||||||
@ -123,8 +115,7 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import static net.minecraft.core.registries.Registries.BIOME;
|
import static net.minecraft.core.registries.Registries.BIOME;
|
||||||
|
|
||||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
|
||||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
||||||
@ -256,11 +247,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public BlockState getBlock(Location location) {
|
public BlockState getBlock(Location location) {
|
||||||
Preconditions.checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
int y = location.getBlockY();
|
int y = location.getBlockY();
|
||||||
int z = location.getBlockZ();
|
int z = location.getBlockZ();
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||||
@ -276,12 +266,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public BaseBlock getFullBlock(final Location location) {
|
public BaseBlock getFullBlock(final Location location) {
|
||||||
Preconditions.checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
int y = location.getBlockY();
|
int y = location.getBlockY();
|
||||||
int z = location.getBlockZ();
|
int z = location.getBlockZ();
|
||||||
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||||
@ -310,10 +299,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||||
return new PaperweightFaweWorldNativeAccess(
|
return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
|
||||||
this,
|
|
||||||
new WeakReference<>(((CraftWorld) world).getHandle())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -458,7 +444,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
ServerLevel nmsWorld = getServerLevel(world);
|
||||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||||
if (map != null && wasAccessibleSinceLastSave(map)) {
|
if (map != null && wasAccessibleSinceLastSave(map)) {
|
||||||
boolean flag = false;
|
boolean flag = false;
|
||||||
@ -496,7 +482,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
||||||
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
||||||
return blockState1.hasPostProcess(
|
return blockState1.hasPostProcess(
|
||||||
((CraftWorld) world).getHandle(),
|
getServerLevel(world),
|
||||||
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -513,47 +499,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean generateTree(
|
protected void preCaptureStates(final ServerLevel serverLevel) {
|
||||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
|
||||||
org.bukkit.World bukkitWorld
|
|
||||||
) {
|
|
||||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
|
||||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
|
||||||
blockVector3 = blockVector3.add(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
|
||||||
}
|
|
||||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
|
||||||
final BlockVector3 finalBlockVector = blockVector3;
|
|
||||||
// Sync to main thread to ensure no clashes occur
|
|
||||||
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
|
|
||||||
serverLevel.captureTreeGeneration = true;
|
serverLevel.captureTreeGeneration = true;
|
||||||
serverLevel.captureBlockStates = true;
|
serverLevel.captureBlockStates = true;
|
||||||
try {
|
|
||||||
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
|
|
||||||
} finally {
|
@Override
|
||||||
|
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
|
||||||
|
return new ArrayList<>(serverLevel.capturedBlockStates.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
|
||||||
serverLevel.captureBlockStates = false;
|
serverLevel.captureBlockStates = false;
|
||||||
serverLevel.captureTreeGeneration = false;
|
serverLevel.captureTreeGeneration = false;
|
||||||
serverLevel.capturedBlockStates.clear();
|
serverLevel.capturedBlockStates.clear();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
if (placed == null || placed.isEmpty()) {
|
@Override
|
||||||
return false;
|
protected ServerLevel getServerLevel(final World world) {
|
||||||
}
|
return ((CraftWorld) world).getHandle();
|
||||||
for (CraftBlockState craftBlockState : placed.values()) {
|
|
||||||
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
|
|
||||||
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,140 +1,51 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
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 net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.TicketType;
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.Unit;
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
|
|
||||||
public class PaperweightStarlightRelighter implements Relighter {
|
public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
|
||||||
|
|
||||||
private 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 static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||||
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
||||||
|
|
||||||
|
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
|
||||||
private final ServerLevel serverLevel;
|
super(serverLevel, queue);
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
|
||||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
|
||||||
private final ReentrantLock areaLock = new ReentrantLock();
|
|
||||||
private final NMSRelighter delegate;
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
|
||||||
this.serverLevel = serverLevel;
|
|
||||||
this.delegate = new NMSRelighter(queue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
protected ChunkPos createChunkPos(final long chunkKey) {
|
||||||
areaLock.lock();
|
return new ChunkPos(chunkKey);
|
||||||
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(ChunkPos.asLong(cx, cz));
|
|
||||||
} finally {
|
|
||||||
areaLock.unlock();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addLightUpdate(int x, int y, int z) {
|
protected long asLong(final int chunkX, final int chunkZ) {
|
||||||
delegate.addLightUpdate(x, y, z);
|
return ChunkPos.asLong(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
@Override
|
||||||
public void fixLightingSafe(boolean sky) {
|
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
|
||||||
this.areaLock.lock();
|
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
|
||||||
try {
|
|
||||||
if (regions.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LongSet first = regions.removeFirst();
|
|
||||||
fixLighting(first, () -> fixLightingSafe(true));
|
|
||||||
} finally {
|
|
||||||
this.areaLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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).
|
|
||||||
*/
|
|
||||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
|
||||||
// convert from long keys to ChunkPos
|
|
||||||
Set<ChunkPos> coords = new HashSet<>();
|
|
||||||
LongIterator iterator = chunks.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
coords.add(new ChunkPos(iterator.nextLong()));
|
|
||||||
}
|
|
||||||
TaskManager.taskManager().task(() -> {
|
|
||||||
// trigger chunk load and apply ticket on main thread
|
|
||||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
|
||||||
for (ChunkPos pos : coords) {
|
|
||||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
|
||||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||||
FAWE_TICKET,
|
FAWE_TICKET,
|
||||||
pos,
|
chunkPos,
|
||||||
LIGHT_LEVEL,
|
LIGHT_LEVEL,
|
||||||
Unit.INSTANCE
|
Unit.INSTANCE
|
||||||
))
|
));
|
||||||
);
|
|
||||||
}
|
|
||||||
// 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
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invokeRelight(
|
protected void invokeRelight(
|
||||||
Set<ChunkPos> coords,
|
Set<ChunkPos> coords,
|
||||||
Consumer<ChunkPos> chunkCallback,
|
Consumer<ChunkPos> chunkCallback,
|
||||||
IntConsumer processCallback
|
IntConsumer processCallback
|
||||||
@ -150,7 +61,7 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
* Allow the server to unload the chunks again.
|
* Allow the server to unload the chunks again.
|
||||||
* Also, if chunk packets are sent delayed, we need to do that here
|
* Also, if chunk packets are sent delayed, we need to do that here
|
||||||
*/
|
*/
|
||||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
protected void postProcessChunks(Set<ChunkPos> coords) {
|
||||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||||
for (ChunkPos pos : coords) {
|
for (ChunkPos pos : coords) {
|
||||||
int x = pos.x;
|
int x = pos.x;
|
||||||
@ -162,44 +73,4 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
|||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@ -15,9 +14,7 @@ import javax.annotation.Nonnull;
|
|||||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nonnull
|
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
|
||||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||||
if (w == null) {
|
if (w == null) {
|
||||||
return NullRelighter.INSTANCE;
|
return NullRelighter.INSTANCE;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
|
||||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||||
@ -10,16 +9,13 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
|||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1.PaperweightAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1.PaperweightAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt.PaperweightLazyCompoundTag;
|
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt.PaperweightLazyCompoundTag;
|
||||||
@ -39,7 +35,6 @@ import com.sk89q.worldedit.registry.state.Property;
|
|||||||
import com.sk89q.worldedit.util.Direction;
|
import com.sk89q.worldedit.util.Direction;
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
import com.sk89q.worldedit.util.SideEffect;
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
import com.sk89q.worldedit.util.SideEffectSet;
|
||||||
import com.sk89q.worldedit.util.TreeGenerator;
|
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||||
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||||
@ -88,14 +83,11 @@ import net.minecraft.world.level.levelgen.structure.StructureStart;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.TreeType;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
|
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockState;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
|
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
|
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
|
||||||
@ -123,8 +115,7 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import static net.minecraft.core.registries.Registries.BIOME;
|
import static net.minecraft.core.registries.Registries.BIOME;
|
||||||
|
|
||||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
|
||||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
||||||
@ -256,11 +247,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public BlockState getBlock(Location location) {
|
public BlockState getBlock(Location location) {
|
||||||
Preconditions.checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
int y = location.getBlockY();
|
int y = location.getBlockY();
|
||||||
int z = location.getBlockZ();
|
int z = location.getBlockZ();
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||||
@ -276,12 +266,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public BaseBlock getFullBlock(final Location location) {
|
public BaseBlock getFullBlock(final Location location) {
|
||||||
Preconditions.checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
int y = location.getBlockY();
|
int y = location.getBlockY();
|
||||||
int z = location.getBlockZ();
|
int z = location.getBlockZ();
|
||||||
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||||
@ -310,10 +299,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||||
return new PaperweightFaweWorldNativeAccess(
|
return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
|
||||||
this,
|
|
||||||
new WeakReference<>(((CraftWorld) world).getHandle())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -458,7 +444,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
ServerLevel nmsWorld = getServerLevel(world);
|
||||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||||
if (map != null && wasAccessibleSinceLastSave(map)) {
|
if (map != null && wasAccessibleSinceLastSave(map)) {
|
||||||
boolean flag = false;
|
boolean flag = false;
|
||||||
@ -496,7 +482,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
||||||
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
||||||
return blockState1.hasPostProcess(
|
return blockState1.hasPostProcess(
|
||||||
((CraftWorld) world).getHandle(),
|
getServerLevel(world),
|
||||||
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -513,47 +499,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean generateTree(
|
protected void preCaptureStates(final ServerLevel serverLevel) {
|
||||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
|
||||||
org.bukkit.World bukkitWorld
|
|
||||||
) {
|
|
||||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
|
||||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
|
||||||
blockVector3 = blockVector3.add(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
|
||||||
}
|
|
||||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
|
||||||
final BlockVector3 finalBlockVector = blockVector3;
|
|
||||||
// Sync to main thread to ensure no clashes occur
|
|
||||||
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
|
|
||||||
serverLevel.captureTreeGeneration = true;
|
serverLevel.captureTreeGeneration = true;
|
||||||
serverLevel.captureBlockStates = true;
|
serverLevel.captureBlockStates = true;
|
||||||
try {
|
|
||||||
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
|
|
||||||
} finally {
|
@Override
|
||||||
|
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
|
||||||
|
return new ArrayList<>(serverLevel.capturedBlockStates.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
|
||||||
serverLevel.captureBlockStates = false;
|
serverLevel.captureBlockStates = false;
|
||||||
serverLevel.captureTreeGeneration = false;
|
serverLevel.captureTreeGeneration = false;
|
||||||
serverLevel.capturedBlockStates.clear();
|
serverLevel.capturedBlockStates.clear();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
if (placed == null || placed.isEmpty()) {
|
@Override
|
||||||
return false;
|
protected ServerLevel getServerLevel(final World world) {
|
||||||
}
|
return ((CraftWorld) world).getHandle();
|
||||||
for (CraftBlockState craftBlockState : placed.values()) {
|
|
||||||
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
|
|
||||||
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,140 +1,51 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
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 net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.TicketType;
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.Unit;
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
|
|
||||||
public class PaperweightStarlightRelighter implements Relighter {
|
public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
|
||||||
|
|
||||||
private 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 static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||||
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
||||||
|
|
||||||
|
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
|
||||||
private final ServerLevel serverLevel;
|
super(serverLevel, queue);
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
|
||||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
|
||||||
private final ReentrantLock areaLock = new ReentrantLock();
|
|
||||||
private final NMSRelighter delegate;
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
|
||||||
this.serverLevel = serverLevel;
|
|
||||||
this.delegate = new NMSRelighter(queue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
protected ChunkPos createChunkPos(final long chunkKey) {
|
||||||
areaLock.lock();
|
return new ChunkPos(chunkKey);
|
||||||
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(ChunkPos.asLong(cx, cz));
|
|
||||||
} finally {
|
|
||||||
areaLock.unlock();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addLightUpdate(int x, int y, int z) {
|
protected long asLong(final int chunkX, final int chunkZ) {
|
||||||
delegate.addLightUpdate(x, y, z);
|
return ChunkPos.asLong(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
@Override
|
||||||
public void fixLightingSafe(boolean sky) {
|
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
|
||||||
this.areaLock.lock();
|
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
|
||||||
try {
|
|
||||||
if (regions.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LongSet first = regions.removeFirst();
|
|
||||||
fixLighting(first, () -> fixLightingSafe(true));
|
|
||||||
} finally {
|
|
||||||
this.areaLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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).
|
|
||||||
*/
|
|
||||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
|
||||||
// convert from long keys to ChunkPos
|
|
||||||
Set<ChunkPos> coords = new HashSet<>();
|
|
||||||
LongIterator iterator = chunks.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
coords.add(new ChunkPos(iterator.nextLong()));
|
|
||||||
}
|
|
||||||
TaskManager.taskManager().task(() -> {
|
|
||||||
// trigger chunk load and apply ticket on main thread
|
|
||||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
|
||||||
for (ChunkPos pos : coords) {
|
|
||||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
|
||||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||||
FAWE_TICKET,
|
FAWE_TICKET,
|
||||||
pos,
|
chunkPos,
|
||||||
LIGHT_LEVEL,
|
LIGHT_LEVEL,
|
||||||
Unit.INSTANCE
|
Unit.INSTANCE
|
||||||
))
|
));
|
||||||
);
|
|
||||||
}
|
|
||||||
// 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
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invokeRelight(
|
protected void invokeRelight(
|
||||||
Set<ChunkPos> coords,
|
Set<ChunkPos> coords,
|
||||||
Consumer<ChunkPos> chunkCallback,
|
Consumer<ChunkPos> chunkCallback,
|
||||||
IntConsumer processCallback
|
IntConsumer processCallback
|
||||||
@ -150,7 +61,7 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
* Allow the server to unload the chunks again.
|
* Allow the server to unload the chunks again.
|
||||||
* Also, if chunk packets are sent delayed, we need to do that here
|
* Also, if chunk packets are sent delayed, we need to do that here
|
||||||
*/
|
*/
|
||||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
protected void postProcessChunks(Set<ChunkPos> coords) {
|
||||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||||
for (ChunkPos pos : coords) {
|
for (ChunkPos pos : coords) {
|
||||||
int x = pos.x;
|
int x = pos.x;
|
||||||
@ -162,44 +73,4 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
|||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@ -15,9 +14,7 @@ import javax.annotation.Nonnull;
|
|||||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nonnull
|
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
|
||||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||||
if (w == null) {
|
if (w == null) {
|
||||||
return NullRelighter.INSTANCE;
|
return NullRelighter.INSTANCE;
|
||||||
|
@ -12,6 +12,6 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
// https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
||||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.2-R0.1-20231008.101509-26")
|
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.2-R0.1-20231029.153906-63")
|
||||||
compileOnly(libs.paperlib)
|
compileOnly(libs.paperlib)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||||
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
import com.fastasyncworldedit.bukkit.adapter.FaweAdapter;
|
||||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
|
||||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||||
@ -10,16 +9,13 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
|||||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.EditSession;
|
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag;
|
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen;
|
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen;
|
||||||
@ -38,7 +34,6 @@ import com.sk89q.worldedit.registry.state.Property;
|
|||||||
import com.sk89q.worldedit.util.Direction;
|
import com.sk89q.worldedit.util.Direction;
|
||||||
import com.sk89q.worldedit.util.SideEffect;
|
import com.sk89q.worldedit.util.SideEffect;
|
||||||
import com.sk89q.worldedit.util.SideEffectSet;
|
import com.sk89q.worldedit.util.SideEffectSet;
|
||||||
import com.sk89q.worldedit.util.TreeGenerator;
|
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||||
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
||||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||||
@ -87,14 +82,11 @@ import net.minecraft.world.level.levelgen.structure.StructureStart;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.TreeType;
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
|
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockState;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
|
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftEntity;
|
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftEntity;
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
|
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
|
||||||
@ -122,8 +114,7 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
import static net.minecraft.core.registries.Registries.BIOME;
|
import static net.minecraft.core.registries.Registries.BIOME;
|
||||||
|
|
||||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
public final class PaperweightFaweAdapter extends FaweAdapter<net.minecraft.nbt.Tag, ServerLevel> {
|
||||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
||||||
@ -259,11 +250,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public BlockState getBlock(Location location) {
|
public BlockState getBlock(Location location) {
|
||||||
Preconditions.checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
int y = location.getBlockY();
|
int y = location.getBlockY();
|
||||||
int z = location.getBlockZ();
|
int z = location.getBlockZ();
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||||
@ -279,12 +269,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
public BaseBlock getFullBlock(final Location location) {
|
public BaseBlock getFullBlock(final Location location) {
|
||||||
Preconditions.checkNotNull(location);
|
Preconditions.checkNotNull(location);
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
||||||
int x = location.getBlockX();
|
int x = location.getBlockX();
|
||||||
int y = location.getBlockY();
|
int y = location.getBlockY();
|
||||||
int z = location.getBlockZ();
|
int z = location.getBlockZ();
|
||||||
|
|
||||||
final ServerLevel handle = craftWorld.getHandle();
|
final ServerLevel handle = getServerLevel(location.getWorld());
|
||||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||||
@ -313,10 +302,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||||
return new PaperweightFaweWorldNativeAccess(
|
return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world)));
|
||||||
this,
|
|
||||||
new WeakReference<>(((CraftWorld) world).getHandle())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -461,7 +447,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
ServerLevel nmsWorld = getServerLevel(world);
|
||||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||||
if (map != null && wasAccessibleSinceLastSave(map)) {
|
if (map != null && wasAccessibleSinceLastSave(map)) {
|
||||||
boolean flag = false;
|
boolean flag = false;
|
||||||
@ -499,7 +485,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
||||||
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
||||||
return blockState1.hasPostProcess(
|
return blockState1.hasPostProcess(
|
||||||
((CraftWorld) world).getHandle(),
|
getServerLevel(world),
|
||||||
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -516,47 +502,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean generateTree(
|
protected void preCaptureStates(final ServerLevel serverLevel) {
|
||||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
|
||||||
org.bukkit.World bukkitWorld
|
|
||||||
) {
|
|
||||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
|
||||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
|
||||||
blockVector3 = blockVector3.add(
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0
|
|
||||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
|
||||||
}
|
|
||||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
|
||||||
final BlockVector3 finalBlockVector = blockVector3;
|
|
||||||
// Sync to main thread to ensure no clashes occur
|
|
||||||
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
|
|
||||||
serverLevel.captureTreeGeneration = true;
|
serverLevel.captureTreeGeneration = true;
|
||||||
serverLevel.captureBlockStates = true;
|
serverLevel.captureBlockStates = true;
|
||||||
try {
|
|
||||||
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
|
|
||||||
} finally {
|
@Override
|
||||||
|
protected List<org.bukkit.block.BlockState> getCapturedBlockStatesCopy(final ServerLevel serverLevel) {
|
||||||
|
return new ArrayList<>(serverLevel.capturedBlockStates.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postCaptureBlockStates(final ServerLevel serverLevel) {
|
||||||
serverLevel.captureBlockStates = false;
|
serverLevel.captureBlockStates = false;
|
||||||
serverLevel.captureTreeGeneration = false;
|
serverLevel.captureTreeGeneration = false;
|
||||||
serverLevel.capturedBlockStates.clear();
|
serverLevel.capturedBlockStates.clear();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
if (placed == null || placed.isEmpty()) {
|
@Override
|
||||||
return false;
|
protected ServerLevel getServerLevel(final World world) {
|
||||||
}
|
return ((CraftWorld) world).getHandle();
|
||||||
for (CraftBlockState craftBlockState : placed.values()) {
|
|
||||||
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
|
|
||||||
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,140 +1,51 @@
|
|||||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
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 net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.TicketType;
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.Unit;
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
|
|
||||||
public class PaperweightStarlightRelighter implements Relighter {
|
public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
|
||||||
|
|
||||||
private 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 static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||||
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
||||||
|
|
||||||
|
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
|
||||||
private final ServerLevel serverLevel;
|
super(serverLevel, queue);
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
|
||||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
|
||||||
private final ReentrantLock areaLock = new ReentrantLock();
|
|
||||||
private final NMSRelighter delegate;
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
|
||||||
this.serverLevel = serverLevel;
|
|
||||||
this.delegate = new NMSRelighter(queue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
protected ChunkPos createChunkPos(final long chunkKey) {
|
||||||
areaLock.lock();
|
return new ChunkPos(chunkKey);
|
||||||
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(ChunkPos.asLong(cx, cz));
|
|
||||||
} finally {
|
|
||||||
areaLock.unlock();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addLightUpdate(int x, int y, int z) {
|
protected long asLong(final int chunkX, final int chunkZ) {
|
||||||
delegate.addLightUpdate(x, y, z);
|
return ChunkPos.asLong(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
@Override
|
||||||
public void fixLightingSafe(boolean sky) {
|
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
|
||||||
this.areaLock.lock();
|
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
|
||||||
try {
|
|
||||||
if (regions.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LongSet first = regions.removeFirst();
|
|
||||||
fixLighting(first, () -> fixLightingSafe(true));
|
|
||||||
} finally {
|
|
||||||
this.areaLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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).
|
|
||||||
*/
|
|
||||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
|
||||||
// convert from long keys to ChunkPos
|
|
||||||
Set<ChunkPos> coords = new HashSet<>();
|
|
||||||
LongIterator iterator = chunks.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
coords.add(new ChunkPos(iterator.nextLong()));
|
|
||||||
}
|
|
||||||
TaskManager.taskManager().task(() -> {
|
|
||||||
// trigger chunk load and apply ticket on main thread
|
|
||||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
|
||||||
for (ChunkPos pos : coords) {
|
|
||||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
|
||||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||||
FAWE_TICKET,
|
FAWE_TICKET,
|
||||||
pos,
|
chunkPos,
|
||||||
LIGHT_LEVEL,
|
LIGHT_LEVEL,
|
||||||
Unit.INSTANCE
|
Unit.INSTANCE
|
||||||
))
|
));
|
||||||
);
|
|
||||||
}
|
|
||||||
// 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
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invokeRelight(
|
protected void invokeRelight(
|
||||||
Set<ChunkPos> coords,
|
Set<ChunkPos> coords,
|
||||||
Consumer<ChunkPos> chunkCallback,
|
Consumer<ChunkPos> chunkCallback,
|
||||||
IntConsumer processCallback
|
IntConsumer processCallback
|
||||||
@ -150,7 +61,7 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
* Allow the server to unload the chunks again.
|
* Allow the server to unload the chunks again.
|
||||||
* Also, if chunk packets are sent delayed, we need to do that here
|
* Also, if chunk packets are sent delayed, we need to do that here
|
||||||
*/
|
*/
|
||||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
protected void postProcessChunks(Set<ChunkPos> coords) {
|
||||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||||
for (ChunkPos pos : coords) {
|
for (ChunkPos pos : coords) {
|
||||||
int x = pos.x;
|
int x = pos.x;
|
||||||
@ -162,44 +73,4 @@ public class PaperweightStarlightRelighter implements Relighter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
|||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@ -15,9 +14,7 @@ import javax.annotation.Nonnull;
|
|||||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nonnull
|
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
|
||||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||||
if (w == null) {
|
if (w == null) {
|
||||||
return NullRelighter.INSTANCE;
|
return NullRelighter.INSTANCE;
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
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.math.BlockVector3;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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> {
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
}
|
@ -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.RelightMode;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
|
|
||||||
@ -14,8 +13,7 @@ import javax.annotation.Nonnull;
|
|||||||
public class NMSRelighterFactory implements RelighterFactory {
|
public class NMSRelighterFactory implements RelighterFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nonnull
|
public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
|
||||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
|
||||||
return new NMSRelighter(
|
return new NMSRelighter(
|
||||||
queue,
|
queue,
|
||||||
relightMode != null ? relightMode : RelightMode.valueOf(Settings.settings().LIGHTING.MODE)
|
relightMode != null ? relightMode : RelightMode.valueOf(Settings.settings().LIGHTING.MODE)
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -60,16 +60,17 @@ import java.util.function.Supplier;
|
|||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
public enum FaweCache implements Trimable {
|
public enum FaweCache implements Trimable {
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #INSTANCE} to get an instance.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true, since = "2.0.0")
|
|
||||||
IMP,
|
|
||||||
/**
|
/**
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
INSTANCE;
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #INSTANCE} to get an instance.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.0.0")
|
||||||
|
public static final FaweCache IMP = INSTANCE;
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
public final int BLOCKS_PER_LAYER = 4096;
|
public final int BLOCKS_PER_LAYER = 4096;
|
||||||
|
@ -19,14 +19,14 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
public class Settings extends Config {
|
public class Settings extends Config {
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
static Settings INSTANCE = new Settings();
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #settings()} instead to get an instance.
|
* @deprecated Use {@link #settings()} instead to get an instance.
|
||||||
*/
|
*/
|
||||||
@Ignore
|
@Ignore
|
||||||
@Deprecated(forRemoval = true, since = "2.0.0")
|
@Deprecated(forRemoval = true, since = "2.0.0")
|
||||||
public static final Settings IMP = new Settings();
|
public static final Settings IMP = INSTANCE;
|
||||||
@Ignore
|
|
||||||
static Settings INSTANCE = new Settings();
|
|
||||||
@Ignore
|
@Ignore
|
||||||
public boolean PROTOCOL_SUPPORT_FIX = false;
|
public boolean PROTOCOL_SUPPORT_FIX = false;
|
||||||
@Comment("These first 6 aren't configurable") // This is a comment
|
@Comment("These first 6 aren't configurable") // This is a comment
|
||||||
|
@ -13,7 +13,7 @@ public class DBHandler {
|
|||||||
* @deprecated Use {@link #dbHandler()} instead.
|
* @deprecated Use {@link #dbHandler()} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated(forRemoval = true, since = "2.0.0")
|
@Deprecated(forRemoval = true, since = "2.0.0")
|
||||||
public static final DBHandler IMP = new DBHandler();
|
public static final DBHandler IMP = dbHandler();
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
private static DBHandler INSTANCE;
|
private static DBHandler INSTANCE;
|
||||||
private final Map<World, RollbackDatabase> databases = new ConcurrentHashMap<>(8, 0.9f, 1);
|
private final Map<World, RollbackDatabase> databases = new ConcurrentHashMap<>(8, 0.9f, 1);
|
||||||
|
@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.Fawe;
|
|||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.math.BlockVectorSet;
|
import com.fastasyncworldedit.core.math.BlockVectorSet;
|
||||||
import com.fastasyncworldedit.core.math.MutableBlockVector3;
|
import com.fastasyncworldedit.core.math.MutableBlockVector3;
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||||
import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkHolder;
|
import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkHolder;
|
||||||
import com.fastasyncworldedit.core.util.MathMan;
|
import com.fastasyncworldedit.core.util.MathMan;
|
||||||
@ -34,7 +33,6 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public class NMSRelighter implements Relighter {
|
public class NMSRelighter implements Relighter {
|
||||||
|
|
||||||
private static final int DISPATCH_SIZE = 64;
|
private static final int DISPATCH_SIZE = 64;
|
||||||
@ -51,7 +49,7 @@ public class NMSRelighter implements Relighter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final MutableBlockVector3 mutableBlockPos = new MutableBlockVector3(0, 0, 0);
|
public final MutableBlockVector3 mutableBlockPos = new MutableBlockVector3(0, 0, 0);
|
||||||
private final IQueueExtent<IQueueChunk> queue;
|
private final IQueueExtent<?> queue;
|
||||||
private final Map<Long, RelightSkyEntry> skyToRelight;
|
private final Map<Long, RelightSkyEntry> skyToRelight;
|
||||||
private final Object present = new Object();
|
private final Object present = new Object();
|
||||||
private final Map<Long, Integer> chunksToSend;
|
private final Map<Long, Integer> chunksToSend;
|
||||||
@ -66,11 +64,11 @@ public class NMSRelighter implements Relighter {
|
|||||||
private final AtomicBoolean finished = new AtomicBoolean(false);
|
private final AtomicBoolean finished = new AtomicBoolean(false);
|
||||||
private boolean removeFirst;
|
private boolean removeFirst;
|
||||||
|
|
||||||
public NMSRelighter(IQueueExtent<IQueueChunk> queue) {
|
public NMSRelighter(IQueueExtent<?> queue) {
|
||||||
this(queue, null);
|
this(queue, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NMSRelighter(IQueueExtent<IQueueChunk> queue, RelightMode relightMode) {
|
public NMSRelighter(IQueueExtent<?> queue, RelightMode relightMode) {
|
||||||
this.queue = queue;
|
this.queue = queue;
|
||||||
this.skyToRelight = new Long2ObjectOpenHashMap<>(12);
|
this.skyToRelight = new Long2ObjectOpenHashMap<>(12);
|
||||||
this.lightQueue = new Long2ObjectOpenHashMap<>(12);
|
this.lightQueue = new Long2ObjectOpenHashMap<>(12);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.fastasyncworldedit.core.extent.processor.lighting;
|
package com.fastasyncworldedit.core.extent.processor.lighting;
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
|
||||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
|
|
||||||
@ -25,6 +24,6 @@ public interface RelighterFactory {
|
|||||||
* @return a new Relighter instance with the specified settings.
|
* @return a new Relighter instance with the specified settings.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue);
|
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -64,14 +64,14 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput {
|
|||||||
* Lock the {@link IChunkGet#call(IChunkSet, Runnable)} method to the current thread using a reentrant lock. Also locks
|
* Lock the {@link IChunkGet#call(IChunkSet, Runnable)} method to the current thread using a reentrant lock. Also locks
|
||||||
* related methods e.g. {@link IChunkGet#setCreateCopy(boolean)}
|
* related methods e.g. {@link IChunkGet#setCreateCopy(boolean)}
|
||||||
*
|
*
|
||||||
* @since TODO
|
* @since 2.8.2
|
||||||
*/
|
*/
|
||||||
default void lockCall() {}
|
default void lockCall() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unlock {@link IChunkGet#call(IChunkSet, Runnable)} (and other related methods) to executions from other threads
|
* Unlock {@link IChunkGet#call(IChunkSet, Runnable)} (and other related methods) to executions from other threads
|
||||||
*
|
*
|
||||||
* @since TODO
|
* @since 2.8.2
|
||||||
*/
|
*/
|
||||||
default void unlockCall() {}
|
default void unlockCall() {}
|
||||||
|
|
||||||
|
@ -135,9 +135,6 @@ public interface IQueueExtent<T extends IChunk> extends Flushable, Trimable, ICh
|
|||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
T chunk = this.getOrCreateChunk(chunkX, chunkZ);
|
T chunk = this.getOrCreateChunk(chunkX, chunkZ);
|
||||||
// Initialize
|
|
||||||
chunk.init(this, chunkX, chunkZ);
|
|
||||||
chunk.setFastMode(isFastMode());
|
|
||||||
|
|
||||||
T newChunk = filter.applyChunk(chunk, region);
|
T newChunk = filter.applyChunk(chunk, region);
|
||||||
if (newChunk != null) {
|
if (newChunk != null) {
|
||||||
|
@ -32,7 +32,7 @@ public class WEManager {
|
|||||||
* @deprecated Use {@link #weManager()} instead.
|
* @deprecated Use {@link #weManager()} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated(forRemoval = true, since = "2.0.0")
|
@Deprecated(forRemoval = true, since = "2.0.0")
|
||||||
public static WEManager IMP = new WEManager();
|
public static WEManager IMP = weManager();
|
||||||
private final ArrayDeque<FaweMaskManager> managers = new ArrayDeque<>();
|
private final ArrayDeque<FaweMaskManager> managers = new ArrayDeque<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,7 +100,6 @@ import java.util.TimeZone;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -406,7 +405,8 @@ public class LocalSession implements TextureHolder {
|
|||||||
*/
|
*/
|
||||||
public void clearHistory() {
|
public void clearHistory() {
|
||||||
//FAWE start
|
//FAWE start
|
||||||
if (Fawe.isMainThread() && !historyWriteLock.tryLock()) {
|
boolean mainThread = Fawe.isMainThread();
|
||||||
|
if (mainThread && !historyWriteLock.tryLock()) {
|
||||||
// Do not make main thread wait if we cannot immediately clear history (on player logout usually)
|
// Do not make main thread wait if we cannot immediately clear history (on player logout usually)
|
||||||
TaskManager.taskManager().async(this::clearHistoryTask);
|
TaskManager.taskManager().async(this::clearHistoryTask);
|
||||||
return;
|
return;
|
||||||
@ -414,9 +414,12 @@ public class LocalSession implements TextureHolder {
|
|||||||
try {
|
try {
|
||||||
clearHistoryTask();
|
clearHistoryTask();
|
||||||
} finally {
|
} finally {
|
||||||
|
// only if we are on the main thread, we ever called tryLock -> need to unlock again
|
||||||
|
if (mainThread) {
|
||||||
historyWriteLock.unlock();
|
historyWriteLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void clearHistoryTask() {
|
private void clearHistoryTask() {
|
||||||
historyWriteLock.lock();
|
historyWriteLock.lock();
|
||||||
|
@ -29,7 +29,6 @@ import com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard;
|
|||||||
import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
|
import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
|
||||||
import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard;
|
import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard;
|
||||||
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
|
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
|
||||||
import com.fastasyncworldedit.core.internal.exception.FaweException;
|
|
||||||
import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream;
|
import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream;
|
||||||
import com.fastasyncworldedit.core.limit.FaweLimit;
|
import com.fastasyncworldedit.core.limit.FaweLimit;
|
||||||
import com.fastasyncworldedit.core.util.ImgurUtility;
|
import com.fastasyncworldedit.core.util.ImgurUtility;
|
||||||
@ -449,6 +448,8 @@ public class ClipboardCommands {
|
|||||||
boolean atOrigin,
|
boolean atOrigin,
|
||||||
@Switch(name = 's', desc = "Select the region after pasting")
|
@Switch(name = 's', desc = "Select the region after pasting")
|
||||||
boolean selectPasted,
|
boolean selectPasted,
|
||||||
|
@Switch(name = 'n', desc = "No paste, select only. (Implies -s)")
|
||||||
|
boolean onlySelect,
|
||||||
@Switch(name = 'e', desc = "Paste entities if available")
|
@Switch(name = 'e', desc = "Paste entities if available")
|
||||||
boolean pasteEntities,
|
boolean pasteEntities,
|
||||||
@Switch(name = 'b', desc = "Paste biomes if available")
|
@Switch(name = 'b', desc = "Paste biomes if available")
|
||||||
@ -460,10 +461,12 @@ public class ClipboardCommands {
|
|||||||
final BlockVector3 to = atOrigin ? origin : session.getPlacementPosition(actor);
|
final BlockVector3 to = atOrigin ? origin : session.getPlacementPosition(actor);
|
||||||
checkPaste(actor, editSession, to, holder, clipboard);
|
checkPaste(actor, editSession, to, holder, clipboard);
|
||||||
|
|
||||||
|
if (!onlySelect) {
|
||||||
clipboard.paste(editSession, to, !ignoreAirBlocks, pasteEntities, pasteBiomes);
|
clipboard.paste(editSession, to, !ignoreAirBlocks, pasteEntities, pasteBiomes);
|
||||||
|
}
|
||||||
|
|
||||||
Region region = clipboard.getRegion().clone();
|
Region region = clipboard.getRegion().clone();
|
||||||
if (selectPasted) {
|
if (selectPasted || onlySelect) {
|
||||||
BlockVector3 clipboardOffset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
|
BlockVector3 clipboardOffset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
|
||||||
BlockVector3 realTo = to.add(holder.getTransform().apply(clipboardOffset.toVector3()).toBlockPoint());
|
BlockVector3 realTo = to.add(holder.getTransform().apply(clipboardOffset.toVector3()).toBlockPoint());
|
||||||
BlockVector3 max = realTo.add(holder
|
BlockVector3 max = realTo.add(holder
|
||||||
@ -475,7 +478,11 @@ public class ClipboardCommands {
|
|||||||
selector.learnChanges();
|
selector.learnChanges();
|
||||||
selector.explainRegionAdjust(actor, session);
|
selector.explainRegionAdjust(actor, session);
|
||||||
}
|
}
|
||||||
|
if (onlySelect) {
|
||||||
|
actor.print(Caption.of("worldedit.paste.selected"));
|
||||||
|
} else {
|
||||||
actor.print(Caption.of("fawe.worldedit.paste.command.paste", to));
|
actor.print(Caption.of("fawe.worldedit.paste.command.paste", to));
|
||||||
|
}
|
||||||
|
|
||||||
if (!actor.hasPermission("fawe.tips")) {
|
if (!actor.hasPermission("fawe.tips")) {
|
||||||
actor.print(Caption.of("fawe.tips.tip.copypaste"));
|
actor.print(Caption.of("fawe.tips.tip.copypaste"));
|
||||||
@ -512,7 +519,7 @@ public class ClipboardCommands {
|
|||||||
ClipboardHolder holder = session.getClipboard();
|
ClipboardHolder holder = session.getClipboard();
|
||||||
//FAWE start - use place
|
//FAWE start - use place
|
||||||
if (holder.getTransform().isIdentity() && sourceMask == null) {
|
if (holder.getTransform().isIdentity() && sourceMask == null) {
|
||||||
place(actor, world, session, editSession, ignoreAirBlocks, atOrigin, selectPasted,
|
place(actor, world, session, editSession, ignoreAirBlocks, atOrigin, selectPasted, onlySelect,
|
||||||
pasteEntities, pasteBiomes
|
pasteEntities, pasteBiomes
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
@ -201,7 +201,7 @@ public class HistorySubCommands {
|
|||||||
.at(summary.maxX, world.getMaxY(), summary.maxZ)
|
.at(summary.maxX, world.getMaxY(), summary.maxZ)
|
||||||
);
|
);
|
||||||
rollback.setTime(historyFile.lastModified());
|
rollback.setTime(historyFile.lastModified());
|
||||||
RollbackDatabase db = DBHandler.IMP
|
RollbackDatabase db = DBHandler.dbHandler()
|
||||||
.getDatabase(world);
|
.getDatabase(world);
|
||||||
db.logEdit(rollback);
|
db.logEdit(rollback);
|
||||||
actor.print(TextComponent.of("Logging: " + historyFile));
|
actor.print(TextComponent.of("Logging: " + historyFile));
|
||||||
|
@ -313,9 +313,9 @@ public class SchematicCommands {
|
|||||||
Actor actor, LocalSession session,
|
Actor actor, LocalSession session,
|
||||||
@Arg(desc = "File name.")
|
@Arg(desc = "File name.")
|
||||||
String filename,
|
String filename,
|
||||||
@Arg(desc = "Format name.", def = "fast")
|
|
||||||
String formatName,
|
|
||||||
//FAWE start - random rotation
|
//FAWE start - random rotation
|
||||||
|
@Arg(desc = "Format name.", def = "")
|
||||||
|
String formatName,
|
||||||
@Switch(name = 'r', desc = "Apply random rotation to the clipboard")
|
@Switch(name = 'r', desc = "Apply random rotation to the clipboard")
|
||||||
boolean randomRotate
|
boolean randomRotate
|
||||||
//FAWE end
|
//FAWE end
|
||||||
@ -325,6 +325,11 @@ public class SchematicCommands {
|
|||||||
//FAWE start
|
//FAWE start
|
||||||
ClipboardFormat format;
|
ClipboardFormat format;
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
|
// if format is set explicitly, do not look up by extension!
|
||||||
|
boolean noExplicitFormat = formatName == null;
|
||||||
|
if (noExplicitFormat) {
|
||||||
|
formatName = "fast";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
URI uri;
|
URI uri;
|
||||||
if (formatName.startsWith("url:")) {
|
if (formatName.startsWith("url:")) {
|
||||||
@ -369,7 +374,7 @@ public class SchematicCommands {
|
|||||||
actor.print(Caption.of("fawe.error.no-perm", "worldedit.schematic.load.other"));
|
actor.print(Caption.of("fawe.error.no-perm", "worldedit.schematic.load.other"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (filename.matches(".*\\.[\\w].*")) {
|
if (noExplicitFormat && filename.matches(".*\\.[\\w].*")) {
|
||||||
format = ClipboardFormats
|
format = ClipboardFormats
|
||||||
.findByExtension(filename.substring(filename.lastIndexOf('.') + 1));
|
.findByExtension(filename.substring(filename.lastIndexOf('.') + 1));
|
||||||
} else {
|
} else {
|
||||||
|
@ -26,7 +26,6 @@ import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
|
|||||||
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
|
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
|
||||||
import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream;
|
import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream;
|
||||||
import com.fastasyncworldedit.core.util.MainUtil;
|
import com.fastasyncworldedit.core.util.MainUtil;
|
||||||
import com.google.common.collect.HashMultimap;
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.Multimaps;
|
import com.google.common.collect.Multimaps;
|
||||||
import com.google.common.io.ByteSource;
|
import com.google.common.io.ByteSource;
|
||||||
@ -51,6 +50,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -64,7 +64,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||||||
public class ClipboardFormats {
|
public class ClipboardFormats {
|
||||||
|
|
||||||
private static final Map<String, ClipboardFormat> aliasMap = new HashMap<>();
|
private static final Map<String, ClipboardFormat> aliasMap = new HashMap<>();
|
||||||
private static final Multimap<String, ClipboardFormat> fileExtensionMap = HashMultimap.create();
|
// FAWE start - keep order of ClipboardFormat entries -> prefer FAST over SPONGE_SCHEMATIC
|
||||||
|
private static final Multimap<String, ClipboardFormat> fileExtensionMap = Multimaps.newMultimap(new HashMap<>(), LinkedHashSet::new);
|
||||||
|
// FAWE end
|
||||||
private static final List<ClipboardFormat> registeredFormats = new ArrayList<>();
|
private static final List<ClipboardFormat> registeredFormats = new ArrayList<>();
|
||||||
|
|
||||||
public static void registerClipboardFormat(ClipboardFormat format) {
|
public static void registerClipboardFormat(ClipboardFormat format) {
|
||||||
|
@ -41,7 +41,7 @@ public class BoundedHeightMask extends AbstractMask {
|
|||||||
* @param maxY the maximum Y (must be equal to or greater than minY)
|
* @param maxY the maximum Y (must be equal to or greater than minY)
|
||||||
*/
|
*/
|
||||||
public BoundedHeightMask(int minY, int maxY) {
|
public BoundedHeightMask(int minY, int maxY) {
|
||||||
checkArgument(minY <= maxY, "minY <= maxY required");
|
checkArgument(minY <= maxY, "minY <= maxY required. minY:" + minY + " and maxY:" + maxY + " were given.");
|
||||||
this.minY = minY;
|
this.minY = minY;
|
||||||
this.maxY = maxY;
|
this.maxY = maxY;
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren