Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-11-17 00:20:09 +01:00
Apply to all versions
Dieser Commit ist enthalten in:
Ursprung
c0a9bc6d97
Commit
9ef74ef7f4
@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.Fawe;
|
|||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||||
|
import com.fastasyncworldedit.core.internal.exception.FaweException;
|
||||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||||
import com.fastasyncworldedit.core.math.IntPair;
|
import com.fastasyncworldedit.core.math.IntPair;
|
||||||
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
||||||
@ -16,6 +17,7 @@ import com.fastasyncworldedit.core.queue.IQueueExtent;
|
|||||||
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
|
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
|
||||||
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
|
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
|
||||||
import com.fastasyncworldedit.core.util.MathMan;
|
import com.fastasyncworldedit.core.util.MathMan;
|
||||||
|
import com.fastasyncworldedit.core.util.MemUtil;
|
||||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||||
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
|
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
@ -23,6 +25,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
|||||||
import com.sk89q.worldedit.internal.Constants;
|
import com.sk89q.worldedit.internal.Constants;
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
@ -83,7 +86,9 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
@ -192,7 +197,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
try {
|
try {
|
||||||
fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition);
|
fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error setting lighting to get", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,7 +209,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
try {
|
try {
|
||||||
fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition);
|
fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error setting sky lighting to get", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -419,18 +424,73 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
entity.discard();
|
entity.discard();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) {
|
public CompletableFuture<LevelChunk> ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) {
|
||||||
return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ);
|
return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
|
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
|
||||||
if (!callLock.isHeldByCurrentThread()) {
|
if (!callLock.isHeldByCurrentThread()) {
|
||||||
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
|
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
|
||||||
}
|
}
|
||||||
forceLoadSections = false;
|
forceLoadSections = false;
|
||||||
LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
|
final ServerLevel nmsWorld = serverLevel;
|
||||||
|
CompletableFuture<LevelChunk> nmsChunkFuture = ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||||
|
LevelChunk chunk = nmsChunkFuture.getNow(null);
|
||||||
|
if (chunk == null && MemUtil.shouldBeginSlow()) {
|
||||||
|
try {
|
||||||
|
chunk = nmsChunkFuture.get(); // "Artificially" slow FAWE down if memory low as performing the
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e);
|
||||||
|
throw new FaweException(
|
||||||
|
TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + " whilst low memory: " + e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final int finalCopyKey = copyKey;
|
||||||
|
// Run immediately if possible
|
||||||
|
if (chunk != null) {
|
||||||
|
return tryWrappedInternalCall(set, finalizer, finalCopyKey, chunk, nmsWorld);
|
||||||
|
}
|
||||||
|
// Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the
|
||||||
|
// target size
|
||||||
|
nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) tryWrappedInternalCall(
|
||||||
|
set,
|
||||||
|
finalizer,
|
||||||
|
finalCopyKey,
|
||||||
|
nmsChunk,
|
||||||
|
nmsWorld
|
||||||
|
)));
|
||||||
|
// If we have re-submitted, return a completed future to prevent potential deadlocks where a future reliant on the
|
||||||
|
// above submission is halting the BlockingExecutor, and preventing the above task from actually running. The futures
|
||||||
|
// submitted above will still be added to the STQE submissions.
|
||||||
|
return (T) (Future) CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Future<T>> T tryWrappedInternalCall(
|
||||||
|
IChunkSet set,
|
||||||
|
Runnable finalizer,
|
||||||
|
int copyKey,
|
||||||
|
LevelChunk nmsChunk,
|
||||||
|
ServerLevel nmsWorld
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return internalCall(set, finalizer, copyKey, nmsChunk, nmsWorld);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOGGER.error("Error performing chunk call at chunk {},{}", chunkX, chunkZ, e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
forceLoadSections = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Future<T>> T internalCall(
|
||||||
|
IChunkSet set,
|
||||||
|
Runnable finalizer,
|
||||||
|
int copyKey,
|
||||||
|
LevelChunk nmsChunk,
|
||||||
|
ServerLevel nmsWorld
|
||||||
|
) throws Exception {
|
||||||
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
|
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
|
||||||
if (createCopy) {
|
if (createCopy) {
|
||||||
if (copies.containsKey(copyKey)) {
|
if (copies.containsKey(copyKey)) {
|
||||||
@ -438,7 +498,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
copies.put(copyKey, copy);
|
copies.put(copyKey, copy);
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
// Remove existing tiles. Create a copy so that we can remove blocks
|
// Remove existing tiles. Create a copy so that we can remove blocks
|
||||||
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||||
List<BlockEntity> beacons = null;
|
List<BlockEntity> beacons = null;
|
||||||
@ -510,7 +569,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
biomeData
|
biomeData
|
||||||
);
|
);
|
||||||
if (PaperweightPlatformAdapter.setSectionAtomic(
|
if (PaperweightPlatformAdapter.setSectionAtomic(
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
chunkPos,
|
chunkPos,
|
||||||
levelChunkSections,
|
levelChunkSections,
|
||||||
null,
|
null,
|
||||||
@ -522,7 +581,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else {
|
} else {
|
||||||
existingSection = levelChunkSections[getSectionIndex];
|
existingSection = levelChunkSections[getSectionIndex];
|
||||||
if (existingSection == null) {
|
if (existingSection == null) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -589,7 +651,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
biomeData
|
biomeData
|
||||||
);
|
);
|
||||||
if (PaperweightPlatformAdapter.setSectionAtomic(
|
if (PaperweightPlatformAdapter.setSectionAtomic(
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
chunkPos,
|
chunkPos,
|
||||||
levelChunkSections,
|
levelChunkSections,
|
||||||
null,
|
null,
|
||||||
@ -601,7 +663,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else {
|
} else {
|
||||||
existingSection = levelChunkSections[getSectionIndex];
|
existingSection = levelChunkSections[getSectionIndex];
|
||||||
if (existingSection == null) {
|
if (existingSection == null) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -629,10 +694,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else if (existingSection != getSections(false)[getSectionIndex]) {
|
} else if (existingSection != getSections(false)[getSectionIndex]) {
|
||||||
this.sections[getSectionIndex] = existingSection;
|
this.sections[getSectionIndex] = existingSection;
|
||||||
this.reset();
|
this.reset();
|
||||||
} else if (!Arrays.equals(
|
} else if (!Arrays.equals(update(getSectionIndex, new char[4096], true), loadPrivately(layerNo))) {
|
||||||
update(getSectionIndex, new char[4096], true),
|
|
||||||
loadPrivately(layerNo)
|
|
||||||
)) {
|
|
||||||
this.reset(layerNo);
|
this.reset(layerNo);
|
||||||
/*} else if (lock.isModified()) {
|
/*} else if (lock.isModified()) {
|
||||||
this.reset(layerNo);*/
|
this.reset(layerNo);*/
|
||||||
@ -656,14 +718,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
|
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
|
||||||
);
|
);
|
||||||
if (!PaperweightPlatformAdapter.setSectionAtomic(
|
if (!PaperweightPlatformAdapter.setSectionAtomic(
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
chunkPos,
|
chunkPos,
|
||||||
levelChunkSections,
|
levelChunkSections,
|
||||||
existingSection,
|
existingSection,
|
||||||
newSection,
|
newSection,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
)) {
|
)) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -677,11 +742,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
|
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
|
||||||
PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue());
|
PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
PaperweightGetBlocks.this.setLightingToGet(
|
PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), set.getMaxSectionPosition());
|
||||||
set.getLight(),
|
|
||||||
set.getMinSectionPosition(),
|
|
||||||
set.getMaxSectionPosition()
|
|
||||||
);
|
|
||||||
PaperweightGetBlocks.this.setSkyLightingToGet(
|
PaperweightGetBlocks.this.setSkyLightingToGet(
|
||||||
set.getSkyLight(),
|
set.getSkyLight(),
|
||||||
set.getMinSectionPosition(),
|
set.getMinSectionPosition(),
|
||||||
@ -731,7 +792,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
||||||
for (UUID uuid : entityRemoves) {
|
for (UUID uuid : entityRemoves) {
|
||||||
Entity entity = serverLevel.getEntities().get(uuid);
|
Entity entity = nmsWorld.getEntities().get(uuid);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
removeEntity(entity);
|
removeEntity(entity);
|
||||||
}
|
}
|
||||||
@ -770,7 +831,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
EntityType<?> type = EntityType.byString(id).orElse(null);
|
EntityType<?> type = EntityType.byString(id).orElse(null);
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
Entity entity = type.create(serverLevel);
|
Entity entity = type.create(nmsWorld);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag);
|
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag);
|
||||||
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||||
@ -779,11 +840,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
entity.load(tag);
|
entity.load(tag);
|
||||||
entity.absMoveTo(x, y, z, yaw, pitch);
|
entity.absMoveTo(x, y, z, yaw, pitch);
|
||||||
entity.setUUID(NbtUtils.uuid(nativeTag));
|
entity.setUUID(NbtUtils.uuid(nativeTag));
|
||||||
if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
|
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
|
||||||
LOGGER.warn(
|
LOGGER.warn(
|
||||||
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
|
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
|
||||||
id,
|
id,
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
z
|
z
|
||||||
@ -813,11 +874,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
final int z = blockHash.z() + bz;
|
final int z = blockHash.z() + bz;
|
||||||
final BlockPos pos = new BlockPos(x, y, z);
|
final BlockPos pos = new BlockPos(x, y, z);
|
||||||
|
|
||||||
synchronized (serverLevel) {
|
synchronized (nmsWorld) {
|
||||||
BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
|
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos);
|
||||||
if (tileEntity == null || tileEntity.isRemoved()) {
|
if (tileEntity == null || tileEntity.isRemoved()) {
|
||||||
serverLevel.removeBlockEntity(pos);
|
nmsWorld.removeBlockEntity(pos);
|
||||||
tileEntity = serverLevel.getBlockEntity(pos);
|
tileEntity = nmsWorld.getBlockEntity(pos);
|
||||||
}
|
}
|
||||||
if (tileEntity != null) {
|
if (tileEntity != null) {
|
||||||
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
|
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
|
||||||
@ -855,7 +916,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
Runnable[] finalSyncTasks = syncTasks;
|
Runnable[] finalSyncTasks = syncTasks;
|
||||||
|
|
||||||
// Chain the sync tasks and the callback
|
// Chain the sync tasks and the callback
|
||||||
Callable<Future> chain = () -> {
|
Callable<Future<?>> chain = () -> {
|
||||||
try {
|
try {
|
||||||
// Run the sync tasks
|
// Run the sync tasks
|
||||||
for (Runnable task : finalSyncTasks) {
|
for (Runnable task : finalSyncTasks) {
|
||||||
@ -872,7 +933,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
return queueHandler.async(callback, null);
|
return queueHandler.async(callback, null);
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error performing final chunk calling at {},{}", chunkX, chunkZ, e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -889,12 +950,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
forceLoadSections = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGet(
|
private void updateGet(
|
||||||
@ -1035,7 +1090,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
} catch (IllegalAccessException | InterruptedException e) {
|
} catch (IllegalAccessException | InterruptedException e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Could not read block data from palette", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
@ -1077,7 +1132,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
levelChunk = this.levelChunk;
|
levelChunk = this.levelChunk;
|
||||||
if (levelChunk == null) {
|
if (levelChunk == null) {
|
||||||
this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ);
|
try {
|
||||||
|
this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ).get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e);
|
||||||
|
throw new FaweException(
|
||||||
|
TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()),
|
||||||
|
FaweException.Type.OTHER,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,6 @@ import net.minecraft.world.level.chunk.PalettedContainer;
|
|||||||
import net.minecraft.world.level.chunk.SingleValuePalette;
|
import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||||
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R2.CraftChunk;
|
import org.bukkit.craftbukkit.v1_20_R2.CraftChunk;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -79,9 +78,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
import static java.lang.invoke.MethodType.methodType;
|
||||||
@ -276,12 +274,49 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error apply DelegateSemaphore", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
public static CompletableFuture<LevelChunk> ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
|
LevelChunk levelChunk = getChunkImmediatelyAsync(serverLevel, chunkX, chunkZ);
|
||||||
|
if (levelChunk != null) {
|
||||||
|
return CompletableFuture.completedFuture(levelChunk);
|
||||||
|
}
|
||||||
|
if (PaperLib.isPaper()) {
|
||||||
|
CompletableFuture<LevelChunk> future = serverLevel
|
||||||
|
.getWorld()
|
||||||
|
.getChunkAtAsync(chunkX, chunkZ, true, true)
|
||||||
|
.thenApply(chunk -> {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
|
try {
|
||||||
|
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOGGER.error("Could not asynchronously load chunk at {},{}", chunkX, chunkZ, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
if (!future.isCompletedExceptionally() || (future.isDone() && future.get() != null)) {
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
Throwable t = future.exceptionNow();
|
||||||
|
LOGGER.error("Asynchronous chunk load at {},{} exceptionally completed immediately", chunkX, chunkZ, t);
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
LOGGER.error(
|
||||||
|
"Unexpected error when getting completed future at chunk {},{}. Returning to default.",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static @Nullable LevelChunk getChunkImmediatelyAsync(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
if (!PaperLib.isPaper()) {
|
if (!PaperLib.isPaper()) {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
@ -290,6 +325,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
if (Fawe.isMainThread()) {
|
if (Fawe.isMainThread()) {
|
||||||
return serverLevel.getChunk(chunkX, chunkZ);
|
return serverLevel.getChunk(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
@ -305,31 +341,9 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
if (Fawe.isMainThread()) {
|
if (Fawe.isMainThread()) {
|
||||||
return serverLevel.getChunk(chunkX, chunkZ);
|
return serverLevel.getChunk(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
return null;
|
||||||
try {
|
|
||||||
CraftChunk chunk;
|
|
||||||
try {
|
|
||||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
|
||||||
} catch (TimeoutException e) {
|
|
||||||
String world = serverLevel.getWorld().getName();
|
|
||||||
// We've already taken 10 seconds we can afford to wait a little here.
|
|
||||||
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
|
||||||
if (loaded) {
|
|
||||||
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
|
||||||
// Retry chunk load
|
|
||||||
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
|
||||||
} else {
|
|
||||||
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
// Ensure chunk is definitely loaded before applying a ticket
|
// Ensure chunk is definitely loaded before applying a ticket
|
||||||
@ -672,7 +686,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
throwable.printStackTrace();
|
LOGGER.error("Error removing beacon", throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.Fawe;
|
|||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||||
|
import com.fastasyncworldedit.core.internal.exception.FaweException;
|
||||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||||
import com.fastasyncworldedit.core.math.IntPair;
|
import com.fastasyncworldedit.core.math.IntPair;
|
||||||
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
||||||
@ -16,6 +17,7 @@ import com.fastasyncworldedit.core.queue.IQueueExtent;
|
|||||||
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
|
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
|
||||||
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
|
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
|
||||||
import com.fastasyncworldedit.core.util.MathMan;
|
import com.fastasyncworldedit.core.util.MathMan;
|
||||||
|
import com.fastasyncworldedit.core.util.MemUtil;
|
||||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||||
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
|
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
@ -23,6 +25,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
|||||||
import com.sk89q.worldedit.internal.Constants;
|
import com.sk89q.worldedit.internal.Constants;
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
@ -83,7 +86,9 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
@ -192,7 +197,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
try {
|
try {
|
||||||
fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition);
|
fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error setting lighting to get", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,7 +209,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
try {
|
try {
|
||||||
fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition);
|
fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error setting sky lighting to get", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -419,18 +424,73 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
entity.discard();
|
entity.discard();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) {
|
public CompletableFuture<LevelChunk> ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) {
|
||||||
return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ);
|
return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
|
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
|
||||||
if (!callLock.isHeldByCurrentThread()) {
|
if (!callLock.isHeldByCurrentThread()) {
|
||||||
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
|
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
|
||||||
}
|
}
|
||||||
forceLoadSections = false;
|
forceLoadSections = false;
|
||||||
LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
|
final ServerLevel nmsWorld = serverLevel;
|
||||||
|
CompletableFuture<LevelChunk> nmsChunkFuture = ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||||
|
LevelChunk chunk = nmsChunkFuture.getNow(null);
|
||||||
|
if (chunk == null && MemUtil.shouldBeginSlow()) {
|
||||||
|
try {
|
||||||
|
chunk = nmsChunkFuture.get(); // "Artificially" slow FAWE down if memory low as performing the
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e);
|
||||||
|
throw new FaweException(
|
||||||
|
TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + " whilst low memory: " + e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final int finalCopyKey = copyKey;
|
||||||
|
// Run immediately if possible
|
||||||
|
if (chunk != null) {
|
||||||
|
return tryWrappedInternalCall(set, finalizer, finalCopyKey, chunk, nmsWorld);
|
||||||
|
}
|
||||||
|
// Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the
|
||||||
|
// target size
|
||||||
|
nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) tryWrappedInternalCall(
|
||||||
|
set,
|
||||||
|
finalizer,
|
||||||
|
finalCopyKey,
|
||||||
|
nmsChunk,
|
||||||
|
nmsWorld
|
||||||
|
)));
|
||||||
|
// If we have re-submitted, return a completed future to prevent potential deadlocks where a future reliant on the
|
||||||
|
// above submission is halting the BlockingExecutor, and preventing the above task from actually running. The futures
|
||||||
|
// submitted above will still be added to the STQE submissions.
|
||||||
|
return (T) (Future) CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Future<T>> T tryWrappedInternalCall(
|
||||||
|
IChunkSet set,
|
||||||
|
Runnable finalizer,
|
||||||
|
int copyKey,
|
||||||
|
LevelChunk nmsChunk,
|
||||||
|
ServerLevel nmsWorld
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return internalCall(set, finalizer, copyKey, nmsChunk, nmsWorld);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOGGER.error("Error performing chunk call at chunk {},{}", chunkX, chunkZ, e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
forceLoadSections = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Future<T>> T internalCall(
|
||||||
|
IChunkSet set,
|
||||||
|
Runnable finalizer,
|
||||||
|
int copyKey,
|
||||||
|
LevelChunk nmsChunk,
|
||||||
|
ServerLevel nmsWorld
|
||||||
|
) throws Exception {
|
||||||
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
|
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
|
||||||
if (createCopy) {
|
if (createCopy) {
|
||||||
if (copies.containsKey(copyKey)) {
|
if (copies.containsKey(copyKey)) {
|
||||||
@ -438,7 +498,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
copies.put(copyKey, copy);
|
copies.put(copyKey, copy);
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
// Remove existing tiles. Create a copy so that we can remove blocks
|
// Remove existing tiles. Create a copy so that we can remove blocks
|
||||||
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||||
List<BlockEntity> beacons = null;
|
List<BlockEntity> beacons = null;
|
||||||
@ -510,7 +569,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
biomeData
|
biomeData
|
||||||
);
|
);
|
||||||
if (PaperweightPlatformAdapter.setSectionAtomic(
|
if (PaperweightPlatformAdapter.setSectionAtomic(
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
chunkPos,
|
chunkPos,
|
||||||
levelChunkSections,
|
levelChunkSections,
|
||||||
null,
|
null,
|
||||||
@ -522,7 +581,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else {
|
} else {
|
||||||
existingSection = levelChunkSections[getSectionIndex];
|
existingSection = levelChunkSections[getSectionIndex];
|
||||||
if (existingSection == null) {
|
if (existingSection == null) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -589,7 +651,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
biomeData
|
biomeData
|
||||||
);
|
);
|
||||||
if (PaperweightPlatformAdapter.setSectionAtomic(
|
if (PaperweightPlatformAdapter.setSectionAtomic(
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
chunkPos,
|
chunkPos,
|
||||||
levelChunkSections,
|
levelChunkSections,
|
||||||
null,
|
null,
|
||||||
@ -601,7 +663,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else {
|
} else {
|
||||||
existingSection = levelChunkSections[getSectionIndex];
|
existingSection = levelChunkSections[getSectionIndex];
|
||||||
if (existingSection == null) {
|
if (existingSection == null) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -629,10 +694,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else if (existingSection != getSections(false)[getSectionIndex]) {
|
} else if (existingSection != getSections(false)[getSectionIndex]) {
|
||||||
this.sections[getSectionIndex] = existingSection;
|
this.sections[getSectionIndex] = existingSection;
|
||||||
this.reset();
|
this.reset();
|
||||||
} else if (!Arrays.equals(
|
} else if (!Arrays.equals(update(getSectionIndex, new char[4096], true), loadPrivately(layerNo))) {
|
||||||
update(getSectionIndex, new char[4096], true),
|
|
||||||
loadPrivately(layerNo)
|
|
||||||
)) {
|
|
||||||
this.reset(layerNo);
|
this.reset(layerNo);
|
||||||
/*} else if (lock.isModified()) {
|
/*} else if (lock.isModified()) {
|
||||||
this.reset(layerNo);*/
|
this.reset(layerNo);*/
|
||||||
@ -656,14 +718,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
|
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
|
||||||
);
|
);
|
||||||
if (!PaperweightPlatformAdapter.setSectionAtomic(
|
if (!PaperweightPlatformAdapter.setSectionAtomic(
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
chunkPos,
|
chunkPos,
|
||||||
levelChunkSections,
|
levelChunkSections,
|
||||||
existingSection,
|
existingSection,
|
||||||
newSection,
|
newSection,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
)) {
|
)) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -677,11 +742,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
|
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
|
||||||
PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue());
|
PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
PaperweightGetBlocks.this.setLightingToGet(
|
PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), set.getMaxSectionPosition());
|
||||||
set.getLight(),
|
|
||||||
set.getMinSectionPosition(),
|
|
||||||
set.getMaxSectionPosition()
|
|
||||||
);
|
|
||||||
PaperweightGetBlocks.this.setSkyLightingToGet(
|
PaperweightGetBlocks.this.setSkyLightingToGet(
|
||||||
set.getSkyLight(),
|
set.getSkyLight(),
|
||||||
set.getMinSectionPosition(),
|
set.getMinSectionPosition(),
|
||||||
@ -731,7 +792,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
||||||
for (UUID uuid : entityRemoves) {
|
for (UUID uuid : entityRemoves) {
|
||||||
Entity entity = serverLevel.getEntities().get(uuid);
|
Entity entity = nmsWorld.getEntities().get(uuid);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
removeEntity(entity);
|
removeEntity(entity);
|
||||||
}
|
}
|
||||||
@ -770,20 +831,21 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
EntityType<?> type = EntityType.byString(id).orElse(null);
|
EntityType<?> type = EntityType.byString(id).orElse(null);
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
Entity entity = type.create(serverLevel);
|
Entity entity = type.create(nmsWorld);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(linTag);
|
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(
|
||||||
|
linTag);
|
||||||
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||||
tag.remove(name);
|
tag.remove(name);
|
||||||
}
|
}
|
||||||
entity.load(tag);
|
entity.load(tag);
|
||||||
entity.absMoveTo(x, y, z, yaw, pitch);
|
entity.absMoveTo(x, y, z, yaw, pitch);
|
||||||
entity.setUUID(NbtUtils.uuid(nativeTag));
|
entity.setUUID(NbtUtils.uuid(nativeTag));
|
||||||
if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
|
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
|
||||||
LOGGER.warn(
|
LOGGER.warn(
|
||||||
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
|
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
|
||||||
id,
|
id,
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
z
|
z
|
||||||
@ -813,11 +875,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
final int z = blockHash.z() + bz;
|
final int z = blockHash.z() + bz;
|
||||||
final BlockPos pos = new BlockPos(x, y, z);
|
final BlockPos pos = new BlockPos(x, y, z);
|
||||||
|
|
||||||
synchronized (serverLevel) {
|
synchronized (nmsWorld) {
|
||||||
BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
|
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos);
|
||||||
if (tileEntity == null || tileEntity.isRemoved()) {
|
if (tileEntity == null || tileEntity.isRemoved()) {
|
||||||
serverLevel.removeBlockEntity(pos);
|
nmsWorld.removeBlockEntity(pos);
|
||||||
tileEntity = serverLevel.getBlockEntity(pos);
|
tileEntity = nmsWorld.getBlockEntity(pos);
|
||||||
}
|
}
|
||||||
if (tileEntity != null) {
|
if (tileEntity != null) {
|
||||||
final net.minecraft.nbt.CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
|
final net.minecraft.nbt.CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
|
||||||
@ -872,7 +934,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
return queueHandler.async(callback, null);
|
return queueHandler.async(callback, null);
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error performing final chunk calling at {},{}", chunkX, chunkZ, e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -889,12 +951,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
forceLoadSections = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGet(
|
private void updateGet(
|
||||||
@ -940,8 +996,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send() {
|
public void send() {
|
||||||
|
synchronized (sectionLock) {
|
||||||
PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ);
|
PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a given (nullable) data array to the current data stored in the server's chunk, associated with this
|
* Update a given (nullable) data array to the current data stored in the server's chunk, associated with this
|
||||||
@ -1033,7 +1091,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
} catch (IllegalAccessException | InterruptedException e) {
|
} catch (IllegalAccessException | InterruptedException e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Could not read block data from palette", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
lock.release();
|
lock.release();
|
||||||
@ -1075,7 +1133,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
levelChunk = this.levelChunk;
|
levelChunk = this.levelChunk;
|
||||||
if (levelChunk == null) {
|
if (levelChunk == null) {
|
||||||
this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ);
|
try {
|
||||||
|
this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ).get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e);
|
||||||
|
throw new FaweException(
|
||||||
|
TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()),
|
||||||
|
FaweException.Type.OTHER,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,6 @@ import net.minecraft.world.level.chunk.PalettedContainer;
|
|||||||
import net.minecraft.world.level.chunk.SingleValuePalette;
|
import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||||
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.craftbukkit.v1_20_R3.CraftChunk;
|
import org.bukkit.craftbukkit.v1_20_R3.CraftChunk;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -79,9 +78,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
import static java.lang.invoke.MethodType.methodType;
|
||||||
@ -276,12 +274,48 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error apply DelegateSemaphore", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
public static CompletableFuture<LevelChunk> ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
|
LevelChunk levelChunk = getChunkImmediatelyAsync(serverLevel, chunkX, chunkZ);
|
||||||
|
if (levelChunk != null) {
|
||||||
|
return CompletableFuture.completedFuture(levelChunk);
|
||||||
|
}
|
||||||
|
if (PaperLib.isPaper()) {
|
||||||
|
CompletableFuture<LevelChunk> future = serverLevel
|
||||||
|
.getWorld()
|
||||||
|
.getChunkAtAsync(chunkX, chunkZ, true, true)
|
||||||
|
.thenApply(chunk -> {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
|
try {
|
||||||
|
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOGGER.error("Could not asynchronously load chunk at {},{}", chunkX, chunkZ, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
if (!future.isCompletedExceptionally() || (future.isDone() && future.get() != null)) {
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
Throwable t = future.exceptionNow();
|
||||||
|
LOGGER.error("Asynchronous chunk load at {},{} exceptionally completed immediately", chunkX, chunkZ, t);
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
LOGGER.error(
|
||||||
|
"Unexpected error when getting completed future at chunk {},{}. Returning to default.",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable LevelChunk getChunkImmediatelyAsync(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
if (!PaperLib.isPaper()) {
|
if (!PaperLib.isPaper()) {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
@ -290,6 +324,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
if (Fawe.isMainThread()) {
|
if (Fawe.isMainThread()) {
|
||||||
return serverLevel.getChunk(chunkX, chunkZ);
|
return serverLevel.getChunk(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
@ -305,31 +340,9 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
if (Fawe.isMainThread()) {
|
if (Fawe.isMainThread()) {
|
||||||
return serverLevel.getChunk(chunkX, chunkZ);
|
return serverLevel.getChunk(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
return null;
|
||||||
try {
|
|
||||||
CraftChunk chunk;
|
|
||||||
try {
|
|
||||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
|
||||||
} catch (TimeoutException e) {
|
|
||||||
String world = serverLevel.getWorld().getName();
|
|
||||||
// We've already taken 10 seconds we can afford to wait a little here.
|
|
||||||
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
|
||||||
if (loaded) {
|
|
||||||
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
|
||||||
// Retry chunk load
|
|
||||||
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
|
||||||
} else {
|
|
||||||
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
// Ensure chunk is definitely loaded before applying a ticket
|
// Ensure chunk is definitely loaded before applying a ticket
|
||||||
@ -672,7 +685,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
throwable.printStackTrace();
|
LOGGER.error("Error removing beacon", throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.Fawe;
|
|||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||||
|
import com.fastasyncworldedit.core.internal.exception.FaweException;
|
||||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||||
import com.fastasyncworldedit.core.math.IntPair;
|
import com.fastasyncworldedit.core.math.IntPair;
|
||||||
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
|
||||||
@ -16,6 +17,7 @@ import com.fastasyncworldedit.core.queue.IQueueExtent;
|
|||||||
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
|
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
|
||||||
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
|
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
|
||||||
import com.fastasyncworldedit.core.util.MathMan;
|
import com.fastasyncworldedit.core.util.MathMan;
|
||||||
|
import com.fastasyncworldedit.core.util.MemUtil;
|
||||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||||
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
|
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
@ -23,6 +25,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
|||||||
import com.sk89q.worldedit.internal.Constants;
|
import com.sk89q.worldedit.internal.Constants;
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
@ -84,7 +87,9 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
@ -193,7 +198,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
try {
|
try {
|
||||||
fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition);
|
fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error setting lighting to get", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,7 +210,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
try {
|
try {
|
||||||
fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition);
|
fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error setting sky lighting to get", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -420,18 +425,73 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
entity.discard();
|
entity.discard();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) {
|
public CompletableFuture<LevelChunk> ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) {
|
||||||
return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ);
|
return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
|
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
|
||||||
if (!callLock.isHeldByCurrentThread()) {
|
if (!callLock.isHeldByCurrentThread()) {
|
||||||
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
|
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
|
||||||
}
|
}
|
||||||
forceLoadSections = false;
|
forceLoadSections = false;
|
||||||
LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
|
final ServerLevel nmsWorld = serverLevel;
|
||||||
|
CompletableFuture<LevelChunk> nmsChunkFuture = ensureLoaded(nmsWorld, chunkX, chunkZ);
|
||||||
|
LevelChunk chunk = nmsChunkFuture.getNow(null);
|
||||||
|
if (chunk == null && MemUtil.shouldBeginSlow()) {
|
||||||
|
try {
|
||||||
|
chunk = nmsChunkFuture.get(); // "Artificially" slow FAWE down if memory low as performing the
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e);
|
||||||
|
throw new FaweException(
|
||||||
|
TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + " whilst low memory: " + e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final int finalCopyKey = copyKey;
|
||||||
|
// Run immediately if possible
|
||||||
|
if (chunk != null) {
|
||||||
|
return tryWrappedInternalCall(set, finalizer, finalCopyKey, chunk, nmsWorld);
|
||||||
|
}
|
||||||
|
// Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the
|
||||||
|
// target size
|
||||||
|
nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) tryWrappedInternalCall(
|
||||||
|
set,
|
||||||
|
finalizer,
|
||||||
|
finalCopyKey,
|
||||||
|
nmsChunk,
|
||||||
|
nmsWorld
|
||||||
|
)));
|
||||||
|
// If we have re-submitted, return a completed future to prevent potential deadlocks where a future reliant on the
|
||||||
|
// above submission is halting the BlockingExecutor, and preventing the above task from actually running. The futures
|
||||||
|
// submitted above will still be added to the STQE submissions.
|
||||||
|
return (T) (Future) CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Future<T>> T tryWrappedInternalCall(
|
||||||
|
IChunkSet set,
|
||||||
|
Runnable finalizer,
|
||||||
|
int copyKey,
|
||||||
|
LevelChunk nmsChunk,
|
||||||
|
ServerLevel nmsWorld
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return internalCall(set, finalizer, copyKey, nmsChunk, nmsWorld);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOGGER.error("Error performing chunk call at chunk {},{}", chunkX, chunkZ, e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
forceLoadSections = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Future<T>> T internalCall(
|
||||||
|
IChunkSet set,
|
||||||
|
Runnable finalizer,
|
||||||
|
int copyKey,
|
||||||
|
LevelChunk nmsChunk,
|
||||||
|
ServerLevel nmsWorld
|
||||||
|
) throws Exception {
|
||||||
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
|
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
|
||||||
if (createCopy) {
|
if (createCopy) {
|
||||||
if (copies.containsKey(copyKey)) {
|
if (copies.containsKey(copyKey)) {
|
||||||
@ -439,7 +499,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
copies.put(copyKey, copy);
|
copies.put(copyKey, copy);
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
// Remove existing tiles. Create a copy so that we can remove blocks
|
// Remove existing tiles. Create a copy so that we can remove blocks
|
||||||
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
|
||||||
List<BlockEntity> beacons = null;
|
List<BlockEntity> beacons = null;
|
||||||
@ -511,7 +570,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
biomeData
|
biomeData
|
||||||
);
|
);
|
||||||
if (PaperweightPlatformAdapter.setSectionAtomic(
|
if (PaperweightPlatformAdapter.setSectionAtomic(
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
chunkPos,
|
chunkPos,
|
||||||
levelChunkSections,
|
levelChunkSections,
|
||||||
null,
|
null,
|
||||||
@ -523,7 +582,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else {
|
} else {
|
||||||
existingSection = levelChunkSections[getSectionIndex];
|
existingSection = levelChunkSections[getSectionIndex];
|
||||||
if (existingSection == null) {
|
if (existingSection == null) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -590,7 +652,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
biomeData
|
biomeData
|
||||||
);
|
);
|
||||||
if (PaperweightPlatformAdapter.setSectionAtomic(
|
if (PaperweightPlatformAdapter.setSectionAtomic(
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
chunkPos,
|
chunkPos,
|
||||||
levelChunkSections,
|
levelChunkSections,
|
||||||
null,
|
null,
|
||||||
@ -602,7 +664,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else {
|
} else {
|
||||||
existingSection = levelChunkSections[getSectionIndex];
|
existingSection = levelChunkSections[getSectionIndex];
|
||||||
if (existingSection == null) {
|
if (existingSection == null) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -630,10 +695,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else if (existingSection != getSections(false)[getSectionIndex]) {
|
} else if (existingSection != getSections(false)[getSectionIndex]) {
|
||||||
this.sections[getSectionIndex] = existingSection;
|
this.sections[getSectionIndex] = existingSection;
|
||||||
this.reset();
|
this.reset();
|
||||||
} else if (!Arrays.equals(
|
} else if (!Arrays.equals(update(getSectionIndex, new char[4096], true), loadPrivately(layerNo))) {
|
||||||
update(getSectionIndex, new char[4096], true),
|
|
||||||
loadPrivately(layerNo)
|
|
||||||
)) {
|
|
||||||
this.reset(layerNo);
|
this.reset(layerNo);
|
||||||
/*} else if (lock.isModified()) {
|
/*} else if (lock.isModified()) {
|
||||||
this.reset(layerNo);*/
|
this.reset(layerNo);*/
|
||||||
@ -657,14 +719,17 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
|
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
|
||||||
);
|
);
|
||||||
if (!PaperweightPlatformAdapter.setSectionAtomic(
|
if (!PaperweightPlatformAdapter.setSectionAtomic(
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
chunkPos,
|
chunkPos,
|
||||||
levelChunkSections,
|
levelChunkSections,
|
||||||
existingSection,
|
existingSection,
|
||||||
newSection,
|
newSection,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
)) {
|
)) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -678,11 +743,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
|
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
|
||||||
PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue());
|
PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
PaperweightGetBlocks.this.setLightingToGet(
|
PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), set.getMaxSectionPosition());
|
||||||
set.getLight(),
|
|
||||||
set.getMinSectionPosition(),
|
|
||||||
set.getMaxSectionPosition()
|
|
||||||
);
|
|
||||||
PaperweightGetBlocks.this.setSkyLightingToGet(
|
PaperweightGetBlocks.this.setSkyLightingToGet(
|
||||||
set.getSkyLight(),
|
set.getSkyLight(),
|
||||||
set.getMinSectionPosition(),
|
set.getMinSectionPosition(),
|
||||||
@ -732,7 +793,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
|
||||||
for (UUID uuid : entityRemoves) {
|
for (UUID uuid : entityRemoves) {
|
||||||
Entity entity = serverLevel.getEntities().get(uuid);
|
Entity entity = nmsWorld.getEntities().get(uuid);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
removeEntity(entity);
|
removeEntity(entity);
|
||||||
}
|
}
|
||||||
@ -771,20 +832,21 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
EntityType<?> type = EntityType.byString(id).orElse(null);
|
EntityType<?> type = EntityType.byString(id).orElse(null);
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
Entity entity = type.create(serverLevel);
|
Entity entity = type.create(nmsWorld);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(linTag);
|
final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeLin(
|
||||||
|
linTag);
|
||||||
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
||||||
tag.remove(name);
|
tag.remove(name);
|
||||||
}
|
}
|
||||||
entity.load(tag);
|
entity.load(tag);
|
||||||
entity.absMoveTo(x, y, z, yaw, pitch);
|
entity.absMoveTo(x, y, z, yaw, pitch);
|
||||||
entity.setUUID(NbtUtils.uuid(nativeTag));
|
entity.setUUID(NbtUtils.uuid(nativeTag));
|
||||||
if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
|
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
|
||||||
LOGGER.warn(
|
LOGGER.warn(
|
||||||
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
|
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
|
||||||
id,
|
id,
|
||||||
serverLevel.getWorld().getName(),
|
nmsWorld.getWorld().getName(),
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
z
|
z
|
||||||
@ -814,11 +876,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
final int z = blockHash.z() + bz;
|
final int z = blockHash.z() + bz;
|
||||||
final BlockPos pos = new BlockPos(x, y, z);
|
final BlockPos pos = new BlockPos(x, y, z);
|
||||||
|
|
||||||
synchronized (serverLevel) {
|
synchronized (nmsWorld) {
|
||||||
BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
|
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos);
|
||||||
if (tileEntity == null || tileEntity.isRemoved()) {
|
if (tileEntity == null || tileEntity.isRemoved()) {
|
||||||
serverLevel.removeBlockEntity(pos);
|
nmsWorld.removeBlockEntity(pos);
|
||||||
tileEntity = serverLevel.getBlockEntity(pos);
|
tileEntity = nmsWorld.getBlockEntity(pos);
|
||||||
}
|
}
|
||||||
if (tileEntity != null) {
|
if (tileEntity != null) {
|
||||||
final net.minecraft.nbt.CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
|
final net.minecraft.nbt.CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
|
||||||
@ -873,7 +935,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
return queueHandler.async(callback, null);
|
return queueHandler.async(callback, null);
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error performing final chunk calling at {},{}", chunkX, chunkZ, e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -890,12 +952,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
forceLoadSections = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGet(
|
private void updateGet(
|
||||||
@ -1078,7 +1134,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
levelChunk = this.levelChunk;
|
levelChunk = this.levelChunk;
|
||||||
if (levelChunk == null) {
|
if (levelChunk == null) {
|
||||||
this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ);
|
try {
|
||||||
|
this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ).get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
LOGGER.error("Could not get chunk at {},{}", chunkX, chunkZ, e);
|
||||||
|
throw new FaweException(
|
||||||
|
TextComponent.of("Could not get chunk at " + chunkX + "," + chunkZ + ": " + e.getMessage()),
|
||||||
|
FaweException.Type.OTHER,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,6 @@ import net.minecraft.world.level.chunk.SingleValuePalette;
|
|||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.craftbukkit.CraftChunk;
|
import org.bukkit.craftbukkit.CraftChunk;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -79,9 +78,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
import static java.lang.invoke.MethodType.methodType;
|
||||||
@ -276,12 +274,48 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error apply DelegateSemaphore", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
public static CompletableFuture<LevelChunk> ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
|
LevelChunk levelChunk = getChunkImmediatelyAsync(serverLevel, chunkX, chunkZ);
|
||||||
|
if (levelChunk != null) {
|
||||||
|
return CompletableFuture.completedFuture(levelChunk);
|
||||||
|
}
|
||||||
|
if (PaperLib.isPaper()) {
|
||||||
|
CompletableFuture<LevelChunk> future = serverLevel
|
||||||
|
.getWorld()
|
||||||
|
.getChunkAtAsync(chunkX, chunkZ, true, true)
|
||||||
|
.thenApply(chunk -> {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
|
try {
|
||||||
|
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOGGER.error("Could not asynchronously load chunk at {},{}", chunkX, chunkZ, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
if (!future.isCompletedExceptionally() || (future.isDone() && future.get() != null)) {
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
Throwable t = future.exceptionNow();
|
||||||
|
LOGGER.error("Asynchronous chunk load at {},{} exceptionally completed immediately", chunkX, chunkZ, t);
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
LOGGER.error(
|
||||||
|
"Unexpected error when getting completed future at chunk {},{}. Returning to default.",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CompletableFuture.supplyAsync(() -> TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable LevelChunk getChunkImmediatelyAsync(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
if (!PaperLib.isPaper()) {
|
if (!PaperLib.isPaper()) {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
@ -290,6 +324,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
if (Fawe.isMainThread()) {
|
if (Fawe.isMainThread()) {
|
||||||
return serverLevel.getChunk(chunkX, chunkZ);
|
return serverLevel.getChunk(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
@ -305,31 +340,9 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
if (Fawe.isMainThread()) {
|
if (Fawe.isMainThread()) {
|
||||||
return serverLevel.getChunk(chunkX, chunkZ);
|
return serverLevel.getChunk(chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
return null;
|
||||||
try {
|
|
||||||
CraftChunk chunk;
|
|
||||||
try {
|
|
||||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
|
||||||
} catch (TimeoutException e) {
|
|
||||||
String world = serverLevel.getWorld().getName();
|
|
||||||
// We've already taken 10 seconds we can afford to wait a little here.
|
|
||||||
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
|
||||||
if (loaded) {
|
|
||||||
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
|
||||||
// Retry chunk load
|
|
||||||
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
|
||||||
} else {
|
|
||||||
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
|
||||||
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
// Ensure chunk is definitely loaded before applying a ticket
|
// Ensure chunk is definitely loaded before applying a ticket
|
||||||
@ -672,7 +685,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
throwable.printStackTrace();
|
LOGGER.error("Error removing beacon", throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,11 +452,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
final int finalCopyKey = copyKey;
|
final int finalCopyKey = copyKey;
|
||||||
// Run immediately if possible
|
// Run immediately if possible
|
||||||
if (chunk != null) {
|
if (chunk != null) {
|
||||||
return internalCall(set, finalizer, finalCopyKey, chunk, nmsWorld);
|
return tryWrappedInternalCall(set, finalizer, finalCopyKey, chunk, nmsWorld);
|
||||||
}
|
}
|
||||||
// Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the
|
// Submit via the STQE as that will help handle excessive queuing by waiting for the submission count to fall below the
|
||||||
// target size
|
// target size
|
||||||
nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) internalCall(
|
nmsChunkFuture.thenApply(nmsChunk -> owner.submitTaskUnchecked(() -> (T) tryWrappedInternalCall(
|
||||||
set,
|
set,
|
||||||
finalizer,
|
finalizer,
|
||||||
finalCopyKey,
|
finalCopyKey,
|
||||||
@ -469,7 +469,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
return (T) (Future) CompletableFuture.completedFuture(null);
|
return (T) (Future) CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends Future<T>> T internalCall(
|
private <T extends Future<T>> T tryWrappedInternalCall(
|
||||||
IChunkSet set,
|
IChunkSet set,
|
||||||
Runnable finalizer,
|
Runnable finalizer,
|
||||||
int copyKey,
|
int copyKey,
|
||||||
@ -477,6 +477,22 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
ServerLevel nmsWorld
|
ServerLevel nmsWorld
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
return internalCall(set, finalizer, copyKey, nmsChunk, nmsWorld);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOGGER.error("Error performing chunk call at chunk {},{}", chunkX, chunkZ, e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
forceLoadSections = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Future<T>> T internalCall(
|
||||||
|
IChunkSet set,
|
||||||
|
Runnable finalizer,
|
||||||
|
int copyKey,
|
||||||
|
LevelChunk nmsChunk,
|
||||||
|
ServerLevel nmsWorld
|
||||||
|
) throws Exception {
|
||||||
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
|
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
|
||||||
if (createCopy) {
|
if (createCopy) {
|
||||||
if (copies.containsKey(copyKey)) {
|
if (copies.containsKey(copyKey)) {
|
||||||
@ -567,7 +583,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else {
|
} else {
|
||||||
existingSection = levelChunkSections[getSectionIndex];
|
existingSection = levelChunkSections[getSectionIndex];
|
||||||
if (existingSection == null) {
|
if (existingSection == null) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -643,7 +662,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else {
|
} else {
|
||||||
existingSection = levelChunkSections[getSectionIndex];
|
existingSection = levelChunkSections[getSectionIndex];
|
||||||
if (existingSection == null) {
|
if (existingSection == null) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
@ -668,10 +690,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
} else if (existingSection != getSections(false)[getSectionIndex]) {
|
} else if (existingSection != getSections(false)[getSectionIndex]) {
|
||||||
this.sections[getSectionIndex] = existingSection;
|
this.sections[getSectionIndex] = existingSection;
|
||||||
this.reset();
|
this.reset();
|
||||||
} else if (!Arrays.equals(
|
} else if (!Arrays.equals(update(getSectionIndex, new char[4096], true), loadPrivately(layerNo))) {
|
||||||
update(getSectionIndex, new char[4096], true),
|
|
||||||
loadPrivately(layerNo)
|
|
||||||
)) {
|
|
||||||
this.reset(layerNo);
|
this.reset(layerNo);
|
||||||
/*} else if (lock.isModified()) {
|
/*} else if (lock.isModified()) {
|
||||||
this.reset(layerNo);*/
|
this.reset(layerNo);*/
|
||||||
@ -702,7 +721,10 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
newSection,
|
newSection,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
)) {
|
)) {
|
||||||
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
|
LOGGER.error(
|
||||||
|
"Skipping invalid null section. chunk: {}, {} layer: {}",
|
||||||
|
chunkX,
|
||||||
|
chunkZ,
|
||||||
getSectionIndex
|
getSectionIndex
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -716,11 +738,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
|
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
|
||||||
PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue());
|
PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
PaperweightGetBlocks.this.setLightingToGet(
|
PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), set.getMaxSectionPosition());
|
||||||
set.getLight(),
|
|
||||||
set.getMinSectionPosition(),
|
|
||||||
set.getMaxSectionPosition()
|
|
||||||
);
|
|
||||||
PaperweightGetBlocks.this.setSkyLightingToGet(
|
PaperweightGetBlocks.this.setSkyLightingToGet(
|
||||||
set.getSkyLight(),
|
set.getSkyLight(),
|
||||||
set.getMinSectionPosition(),
|
set.getMinSectionPosition(),
|
||||||
@ -928,12 +946,6 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (Throwable e) {
|
|
||||||
LOGGER.error("Error calling chunk at {},{}", chunkX, chunkZ, e);
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
forceLoadSections = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGet(
|
private void updateGet(
|
||||||
|
@ -56,7 +56,6 @@ import net.minecraft.world.level.chunk.SingleValuePalette;
|
|||||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.craftbukkit.CraftChunk;
|
import org.bukkit.craftbukkit.CraftChunk;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -79,10 +78,7 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
import static java.lang.invoke.MethodType.methodType;
|
||||||
@ -262,7 +258,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
LOGGER.error("Error apply DelegateSemaphore", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -673,7 +669,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
throwable.printStackTrace();
|
LOGGER.error("Error removing beacon", throwable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren