3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-12-24 18:10:08 +01:00
Dieser Commit ist enthalten in:
dordsor21 2024-11-16 12:11:56 +00:00
Ursprung bb5c68e102
Commit 051f5c3a8a
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 1E53E88969FFCF0B
3 geänderte Dateien mit 459 neuen und 457 gelöschten Zeilen

Datei anzeigen

@ -6,14 +6,18 @@ import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.FaweCache;
import com.fastasyncworldedit.core.configuration.Settings;
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.IntPair;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.MemUtil;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.fastasyncworldedit.core.util.collection.AdaptedMap;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
@ -21,6 +25,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
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.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
@ -83,7 +88,9 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReadWriteLock;
@ -192,7 +199,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
try {
fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition);
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Error setting lighting to get", e);
}
}
}
@ -204,7 +211,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
try {
fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition);
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Error setting sky lighting to get", e);
}
}
}
@ -420,18 +427,75 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
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);
}
@Override
@SuppressWarnings("rawtypes")
public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finalizer) {
@SuppressWarnings({"rawtypes", "unchecked"})
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
if (!callLock.isHeldByCurrentThread()) {
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
}
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()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) {
try {
// "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of
// memory usage
chunk = nmsChunkFuture.get();
} 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;
if (createCopy) {
if (copies.containsKey(copyKey)) {
@ -439,457 +503,371 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}
copies.put(copyKey, copy);
}
try {
// Remove existing tiles. Create a copy so that we can remove blocks
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
List<BlockEntity> beacons = null;
if (!chunkTiles.isEmpty()) {
for (Map.Entry<BlockPos, BlockEntity> entry : chunkTiles.entrySet()) {
final BlockPos pos = entry.getKey();
final int lx = pos.getX() & 15;
final int ly = pos.getY();
final int lz = pos.getZ() & 15;
final int layer = ly >> 4;
if (!set.hasSection(layer)) {
// Remove existing tiles. Create a copy so that we can remove blocks
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
List<BlockEntity> beacons = null;
if (!chunkTiles.isEmpty()) {
for (Map.Entry<BlockPos, BlockEntity> entry : chunkTiles.entrySet()) {
final BlockPos pos = entry.getKey();
final int lx = pos.getX() & 15;
final int ly = pos.getY();
final int lz = pos.getZ() & 15;
final int layer = ly >> 4;
if (!set.hasSection(layer)) {
continue;
}
int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
if (ordinal != BlockTypesCache.ReservedIDs.__RESERVED__) {
BlockEntity tile = entry.getValue();
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
if (beacons == null) {
beacons = new ArrayList<>();
}
beacons.add(tile);
PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk);
continue;
}
int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
if (ordinal != BlockTypesCache.ReservedIDs.__RESERVED__) {
BlockEntity tile = entry.getValue();
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
if (beacons == null) {
beacons = new ArrayList<>();
}
beacons.add(tile);
PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk);
continue;
}
nmsChunk.removeBlockEntity(tile.getBlockPos());
if (createCopy) {
copy.storeTile(tile);
}
nmsChunk.removeBlockEntity(tile.getBlockPos());
if (createCopy) {
copy.storeTile(tile);
}
}
}
final BiomeType[][] biomes = set.getBiomes();
}
final BiomeType[][] biomes = set.getBiomes();
int bitMask = 0;
synchronized (nmsChunk) {
LevelChunkSection[] levelChunkSections = nmsChunk.getSections();
int bitMask = 0;
synchronized (nmsChunk) {
LevelChunkSection[] levelChunkSections = nmsChunk.getSections();
for (int layerNo = getMinSectionPosition(); layerNo <= getMaxSectionPosition(); layerNo++) {
for (int layerNo = getMinSectionPosition(); layerNo <= getMaxSectionPosition(); layerNo++) {
int getSectionIndex = layerNo - getMinSectionPosition();
int setSectionIndex = layerNo - set.getMinSectionPosition();
int getSectionIndex = layerNo - getMinSectionPosition();
int setSectionIndex = layerNo - set.getMinSectionPosition();
if (!set.hasSection(layerNo)) {
// No blocks, but might be biomes present. Handle this lazily.
if (biomes == null) {
continue;
}
if (layerNo < set.getMinSectionPosition() || layerNo > set.getMaxSectionPosition()) {
continue;
}
if (biomes[setSectionIndex] != null) {
synchronized (super.sectionLocks[getSectionIndex]) {
LevelChunkSection existingSection = levelChunkSections[getSectionIndex];
if (createCopy && existingSection != null) {
copy.storeBiomes(getSectionIndex, existingSection.getBiomes());
}
if (existingSection == null) {
PalettedContainer<Holder<Biome>> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer(
biomes[setSectionIndex],
biomeHolderIdMap
);
LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection(
layerNo,
new char[4096],
adapter,
biomeRegistry,
biomeData
);
if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections,
null,
newSection,
getSectionIndex
)) {
updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex);
continue;
} else {
existingSection = levelChunkSections[getSectionIndex];
if (existingSection == null) {
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
getSectionIndex
);
continue;
}
}
} else {
PalettedContainer<Holder<Biome>> paletteBiomes = setBiomesToPalettedContainer(
biomes,
setSectionIndex,
existingSection.getBiomes()
);
if (paletteBiomes != null) {
PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes);
}
}
}
}
if (!set.hasSection(layerNo)) {
// No blocks, but might be biomes present. Handle this lazily.
if (biomes == null) {
continue;
}
bitMask |= 1 << getSectionIndex;
// setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to
// this chunk GET when #updateGet is called. Future dords, please listen this time.
char[] tmp = set.load(layerNo);
char[] setArr = new char[tmp.length];
System.arraycopy(tmp, 0, setArr, 0, tmp.length);
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
// submitted to keep loaded internal chunks to queue target size.
synchronized (super.sectionLocks[getSectionIndex]) {
LevelChunkSection newSection;
LevelChunkSection existingSection = levelChunkSections[getSectionIndex];
// Don't attempt to tick section whilst we're editing
if (existingSection != null) {
PaperweightPlatformAdapter.clearCounts(existingSection);
}
if (createCopy) {
char[] tmpLoad = loadPrivately(layerNo);
char[] copyArr = new char[4096];
System.arraycopy(tmpLoad, 0, copyArr, 0, 4096);
copy.storeSection(getSectionIndex, copyArr);
if (biomes != null && existingSection != null) {
if (layerNo < set.getMinSectionPosition() || layerNo > set.getMaxSectionPosition()) {
continue;
}
if (biomes[setSectionIndex] != null) {
synchronized (super.sectionLocks[getSectionIndex]) {
LevelChunkSection existingSection = levelChunkSections[getSectionIndex];
if (createCopy && existingSection != null) {
copy.storeBiomes(getSectionIndex, existingSection.getBiomes());
}
}
if (existingSection == null) {
PalettedContainer<Holder<Biome>> biomeData = biomes == null ? new PalettedContainer<>(
biomeHolderIdMap,
biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)),
PalettedContainer.Strategy.SECTION_BIOMES
) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap);
newSection = PaperweightPlatformAdapter.newChunkSection(
layerNo,
setArr,
adapter,
biomeRegistry,
biomeData
);
if (PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections,
null,
newSection,
getSectionIndex
)) {
updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex);
continue;
} else {
existingSection = levelChunkSections[getSectionIndex];
if (existingSection == null) {
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
getSectionIndex
);
if (existingSection == null) {
PalettedContainer<Holder<Biome>> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap);
LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection(layerNo, new char[4096], adapter, biomeRegistry, biomeData);
if (PaperweightPlatformAdapter.setSectionAtomic(serverLevel.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, getSectionIndex)) {
updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex);
continue;
} else {
existingSection = levelChunkSections[getSectionIndex];
if (existingSection == null) {
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, getSectionIndex);
continue;
}
}
} else {
PalettedContainer<Holder<Biome>> paletteBiomes = setBiomesToPalettedContainer(biomes, setSectionIndex, existingSection.getBiomes());
if (paletteBiomes != null) {
PaperweightPlatformAdapter.setBiomesToChunkSection(existingSection, paletteBiomes);
}
}
}
}
continue;
}
//ensure that the server doesn't try to tick the chunksection while we're editing it. (Again)
bitMask |= 1 << getSectionIndex;
// setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to
// this chunk GET when #updateGet is called. Future dords, please listen this time.
char[] tmp = set.load(layerNo);
char[] setArr = new char[tmp.length];
System.arraycopy(tmp, 0, setArr, 0, tmp.length);
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
// submitted to keep loaded internal chunks to queue target size.
synchronized (super.sectionLocks[getSectionIndex]) {
LevelChunkSection newSection;
LevelChunkSection existingSection = levelChunkSections[getSectionIndex];
// Don't attempt to tick section whilst we're editing
if (existingSection != null) {
PaperweightPlatformAdapter.clearCounts(existingSection);
DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection);
}
// Synchronize to prevent further acquisitions
synchronized (lock) {
lock.acquire(); // Wait until we have the lock
lock.release();
try {
sectionLock.writeLock().lock();
if (this.getChunk() != nmsChunk) {
this.levelChunk = nmsChunk;
this.sections = null;
this.reset();
} else if (existingSection != getSections(false)[getSectionIndex]) {
this.sections[getSectionIndex] = existingSection;
this.reset();
} else if (!Arrays.equals(
update(getSectionIndex, new char[4096], true),
loadPrivately(layerNo)
)) {
this.reset(layerNo);
if (createCopy) {
char[] tmpLoad = loadPrivately(layerNo);
char[] copyArr = new char[4096];
System.arraycopy(tmpLoad, 0, copyArr, 0, 4096);
copy.storeSection(getSectionIndex, copyArr);
if (biomes != null && existingSection != null) {
copy.storeBiomes(getSectionIndex, existingSection.getBiomes());
}
}
if (existingSection == null) {
PalettedContainer<Holder<Biome>> biomeData = biomes == null ? new PalettedContainer<>(biomeHolderIdMap, biomeHolderIdMap.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), PalettedContainer.Strategy.SECTION_BIOMES) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap);
newSection = PaperweightPlatformAdapter.newChunkSection(layerNo, setArr, adapter, biomeRegistry, biomeData);
if (PaperweightPlatformAdapter.setSectionAtomic(serverLevel.getWorld().getName(), chunkPos, levelChunkSections, null, newSection, getSectionIndex)) {
updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex);
continue;
} else {
existingSection = levelChunkSections[getSectionIndex];
if (existingSection == null) {
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, getSectionIndex);
continue;
}
}
}
//ensure that the server doesn't try to tick the chunksection while we're editing it. (Again)
PaperweightPlatformAdapter.clearCounts(existingSection);
DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection);
// Synchronize to prevent further acquisitions
synchronized (lock) {
lock.acquire(); // Wait until we have the lock
lock.release();
try {
sectionLock.writeLock().lock();
if (this.getChunk() != nmsChunk) {
this.levelChunk = nmsChunk;
this.sections = null;
this.reset();
} else if (existingSection != getSections(false)[getSectionIndex]) {
this.sections[getSectionIndex] = existingSection;
this.reset();
} else if (!Arrays.equals(update(getSectionIndex, new char[4096], true), loadPrivately(layerNo))) {
this.reset(layerNo);
/*} else if (lock.isModified()) {
this.reset(layerNo);*/
}
} finally {
sectionLock.writeLock().unlock();
}
PalettedContainer<Holder<Biome>> biomeData = setBiomesToPalettedContainer(
biomes,
setSectionIndex,
existingSection.getBiomes()
);
newSection = PaperweightPlatformAdapter.newChunkSection(
layerNo,
this::loadPrivately,
setArr,
adapter,
biomeRegistry,
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
);
if (!PaperweightPlatformAdapter.setSectionAtomic(
serverLevel.getWorld().getName(),
chunkPos,
levelChunkSections,
existingSection,
newSection,
getSectionIndex
)) {
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ,
getSectionIndex
);
} else {
updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex);
}
} finally {
sectionLock.writeLock().unlock();
}
}
}
Map<HeightMapType, int[]> heightMaps = set.getHeightMaps();
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue());
}
PaperweightGetBlocks.this.setLightingToGet(
set.getLight(),
set.getMinSectionPosition(),
set.getMaxSectionPosition()
);
PaperweightGetBlocks.this.setSkyLightingToGet(
set.getSkyLight(),
set.getMinSectionPosition(),
set.getMaxSectionPosition()
);
PalettedContainer<Holder<Biome>> biomeData = setBiomesToPalettedContainer(biomes, setSectionIndex, existingSection.getBiomes());
Runnable[] syncTasks = null;
int bx = chunkX << 4;
int bz = chunkZ << 4;
// Call beacon deactivate events here synchronously
// list will be null on spigot, so this is an implicit isPaper check
if (beacons != null && !beacons.isEmpty()) {
final List<BlockEntity> finalBeacons = beacons;
syncTasks = new Runnable[4];
syncTasks[3] = () -> {
for (BlockEntity beacon : finalBeacons) {
BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE);
new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent();
newSection = PaperweightPlatformAdapter.newChunkSection(layerNo, this::loadPrivately, setArr, adapter, biomeRegistry, biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes());
if (!PaperweightPlatformAdapter.setSectionAtomic(serverLevel.getWorld().getName(), chunkPos, levelChunkSections, existingSection, newSection, getSectionIndex)) {
LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, getSectionIndex);
} else {
updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex);
}
};
}
Set<UUID> entityRemoves = set.getEntityRemoves();
if (entityRemoves != null && !entityRemoves.isEmpty()) {
if (syncTasks == null) {
syncTasks = new Runnable[3];
}
syncTasks[2] = () -> {
Set<UUID> entitiesRemoved = new HashSet<>();
final List<Entity> entities = PaperweightPlatformAdapter.getEntities(nmsChunk);
for (Entity entity : entities) {
UUID uuid = entity.getUUID();
if (entityRemoves.contains(uuid)) {
if (createCopy) {
copy.storeEntity(entity);
}
removeEntity(entity);
entitiesRemoved.add(uuid);
entityRemoves.remove(uuid);
}
}
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
for (UUID uuid : entityRemoves) {
Entity entity = serverLevel.getEntities().get(uuid);
if (entity != null) {
removeEntity(entity);
}
}
}
// Only save entities that were actually removed to history
set.getEntityRemoves().clear();
set.getEntityRemoves().addAll(entitiesRemoved);
};
}
Collection<FaweCompoundTag> entities = set.entities();
if (entities != null && !entities.isEmpty()) {
if (syncTasks == null) {
syncTasks = new Runnable[2];
}
syncTasks[1] = () -> {
Iterator<FaweCompoundTag> iterator = entities.iterator();
while (iterator.hasNext()) {
final FaweCompoundTag nativeTag = iterator.next();
final LinCompoundTag linTag = nativeTag.linTag();
final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
final LinListTag<LinFloatTag> rotTag = linTag.findListTag("Rotation", LinTagType.floatTag());
if (idTag == null || posTag == null || rotTag == null) {
LOGGER.error("Unknown entity tag: {}", nativeTag);
continue;
}
final double x = posTag.get(0).valueAsDouble();
final double y = posTag.get(1).valueAsDouble();
final double z = posTag.get(2).valueAsDouble();
final float yaw = rotTag.get(0).valueAsFloat();
final float pitch = rotTag.get(1).valueAsFloat();
final String id = idTag.value();
EntityType<?> type = EntityType.byString(id).orElse(null);
if (type != null) {
Entity entity = type.create(serverLevel, EntitySpawnReason.COMMAND);
if (entity != null) {
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag);
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name);
}
entity.load(tag);
entity.absMoveTo(x, y, z, yaw, pitch);
entity.setUUID(NbtUtils.uuid(nativeTag));
if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
LOGGER.warn(
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
id,
serverLevel.getWorld().getName(),
x,
y,
z
);
// Unsuccessful create should not be saved to history
iterator.remove();
}
}
}
}
};
}
// set tiles
Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
if (tiles != null && !tiles.isEmpty()) {
if (syncTasks == null) {
syncTasks = new Runnable[1];
}
syncTasks[0] = () -> {
for (final Map.Entry<BlockVector3, FaweCompoundTag> entry : tiles.entrySet()) {
final FaweCompoundTag nativeTag = entry.getValue();
final BlockVector3 blockHash = entry.getKey();
final int x = blockHash.x() + bx;
final int y = blockHash.y();
final int z = blockHash.z() + bz;
final BlockPos pos = new BlockPos(x, y, z);
synchronized (serverLevel) {
BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
if (tileEntity == null || tileEntity.isRemoved()) {
serverLevel.removeBlockEntity(pos);
tileEntity = serverLevel.getBlockEntity(pos);
}
if (tileEntity != null) {
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
tag.put("x", IntTag.valueOf(x));
tag.put("y", IntTag.valueOf(y));
tag.put("z", IntTag.valueOf(z));
tileEntity.loadWithComponents(tag, DedicatedServer.getServer().registryAccess());
}
}
}
};
}
Runnable callback;
if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
callback = () -> {
// Set Modified
nmsChunk.setLightCorrect(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markUnsaved();
// send to player
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
this.send();
}
if (finalizer != null) {
finalizer.run();
}
};
}
if (syncTasks != null) {
QueueHandler queueHandler = Fawe.instance().getQueueHandler();
Runnable[] finalSyncTasks = syncTasks;
// Chain the sync tasks and the callback
Callable<Future> chain = () -> {
try {
// Run the sync tasks
for (Runnable task : finalSyncTasks) {
if (task != null) {
task.run();
}
}
if (callback == null) {
if (finalizer != null) {
queueHandler.async(finalizer, null);
}
return null;
} else {
return queueHandler.async(callback, null);
}
} catch (Throwable e) {
e.printStackTrace();
throw e;
}
};
//noinspection unchecked - required at compile time
return (T) (Future) queueHandler.sync(chain);
} else {
if (callback == null) {
if (finalizer != null) {
finalizer.run();
}
} else {
callback.run();
}
}
}
return null;
} catch (Throwable e) {
e.printStackTrace();
return null;
} finally {
forceLoadSections = true;
Map<HeightMapType, int[]> heightMaps = set.getHeightMaps();
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue());
}
PaperweightGetBlocks.this.setLightingToGet(set.getLight(), set.getMinSectionPosition(), set.getMaxSectionPosition());
PaperweightGetBlocks.this.setSkyLightingToGet(set.getSkyLight(), set.getMinSectionPosition(), set.getMaxSectionPosition());
Runnable[] syncTasks = null;
int bx = chunkX << 4;
int bz = chunkZ << 4;
// Call beacon deactivate events here synchronously
// list will be null on spigot, so this is an implicit isPaper check
if (beacons != null && !beacons.isEmpty()) {
final List<BlockEntity> finalBeacons = beacons;
syncTasks = new Runnable[4];
syncTasks[3] = () -> {
for (BlockEntity beacon : finalBeacons) {
BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE);
new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent();
}
};
}
Set<UUID> entityRemoves = set.getEntityRemoves();
if (entityRemoves != null && !entityRemoves.isEmpty()) {
if (syncTasks == null) {
syncTasks = new Runnable[3];
}
syncTasks[2] = () -> {
Set<UUID> entitiesRemoved = new HashSet<>();
final List<Entity> entities = PaperweightPlatformAdapter.getEntities(nmsChunk);
for (Entity entity : entities) {
UUID uuid = entity.getUUID();
if (entityRemoves.contains(uuid)) {
if (createCopy) {
copy.storeEntity(entity);
}
removeEntity(entity);
entitiesRemoved.add(uuid);
entityRemoves.remove(uuid);
}
}
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
for (UUID uuid : entityRemoves) {
Entity entity = serverLevel.getEntities().get(uuid);
if (entity != null) {
removeEntity(entity);
}
}
}
// Only save entities that were actually removed to history
set.getEntityRemoves().clear();
set.getEntityRemoves().addAll(entitiesRemoved);
};
}
Collection<FaweCompoundTag> entities = set.entities();
if (entities != null && !entities.isEmpty()) {
if (syncTasks == null) {
syncTasks = new Runnable[2];
}
syncTasks[1] = () -> {
Iterator<FaweCompoundTag> iterator = entities.iterator();
while (iterator.hasNext()) {
final FaweCompoundTag nativeTag = iterator.next();
final LinCompoundTag linTag = nativeTag.linTag();
final LinStringTag idTag = linTag.findTag("Id", LinTagType.stringTag());
final LinListTag<LinDoubleTag> posTag = linTag.findListTag("Pos", LinTagType.doubleTag());
final LinListTag<LinFloatTag> rotTag = linTag.findListTag("Rotation", LinTagType.floatTag());
if (idTag == null || posTag == null || rotTag == null) {
LOGGER.error("Unknown entity tag: {}", nativeTag);
continue;
}
final double x = posTag.get(0).valueAsDouble();
final double y = posTag.get(1).valueAsDouble();
final double z = posTag.get(2).valueAsDouble();
final float yaw = rotTag.get(0).valueAsFloat();
final float pitch = rotTag.get(1).valueAsFloat();
final String id = idTag.value();
EntityType<?> type = EntityType.byString(id).orElse(null);
if (type != null) {
Entity entity = type.create(serverLevel, EntitySpawnReason.COMMAND);
if (entity != null) {
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag);
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
tag.remove(name);
}
entity.load(tag);
entity.absMoveTo(x, y, z, yaw, pitch);
entity.setUUID(NbtUtils.uuid(nativeTag));
if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
LOGGER.warn("Error creating entity of type `{}` in world `{}` at location `{},{},{}`", id, serverLevel.getWorld().getName(), x, y, z);
// Unsuccessful create should not be saved to history
iterator.remove();
}
}
}
}
};
}
// set tiles
Map<BlockVector3, FaweCompoundTag> tiles = set.tiles();
if (tiles != null && !tiles.isEmpty()) {
if (syncTasks == null) {
syncTasks = new Runnable[1];
}
syncTasks[0] = () -> {
for (final Map.Entry<BlockVector3, FaweCompoundTag> entry : tiles.entrySet()) {
final FaweCompoundTag nativeTag = entry.getValue();
final BlockVector3 blockHash = entry.getKey();
final int x = blockHash.x() + bx;
final int y = blockHash.y();
final int z = blockHash.z() + bz;
final BlockPos pos = new BlockPos(x, y, z);
synchronized (serverLevel) {
BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
if (tileEntity == null || tileEntity.isRemoved()) {
serverLevel.removeBlockEntity(pos);
tileEntity = serverLevel.getBlockEntity(pos);
}
if (tileEntity != null) {
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
tag.put("x", IntTag.valueOf(x));
tag.put("y", IntTag.valueOf(y));
tag.put("z", IntTag.valueOf(z));
tileEntity.loadWithComponents(tag, DedicatedServer.getServer().registryAccess());
}
}
}
};
}
Runnable callback;
if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
callback = () -> {
// Set Modified
nmsChunk.setLightCorrect(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markUnsaved();
// send to player
if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING || finalMask == 0 && biomes != null) {
this.send();
}
if (finalizer != null) {
finalizer.run();
}
};
}
if (syncTasks != null) {
QueueHandler queueHandler = Fawe.instance().getQueueHandler();
Runnable[] finalSyncTasks = syncTasks;
// Chain the sync tasks and the callback
Callable<Future> chain = () -> {
try {
// Run the sync tasks
for (Runnable task : finalSyncTasks) {
if (task != null) {
task.run();
}
}
if (callback == null) {
if (finalizer != null) {
queueHandler.async(finalizer, null);
}
return null;
} else {
return queueHandler.async(callback, null);
}
} catch (Throwable e) {
e.printStackTrace();
throw e;
}
};
//noinspection unchecked - required at compile time
return (T) (Future) queueHandler.sync(chain);
} else {
if (callback == null) {
if (finalizer != null) {
finalizer.run();
}
} else {
callback.run();
}
}
}
return null;
}
private void updateGet(
@ -1030,7 +1008,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
}
return data;
} catch (IllegalAccessException | InterruptedException e) {
e.printStackTrace();
LOGGER.error("Could not read block data from palette", e);
throw new RuntimeException(e);
} finally {
lock.release();
@ -1072,7 +1050,16 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
synchronized (this) {
levelChunk = this.levelChunk;
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
);
}
}
}
}

Datei anzeigen

@ -3,8 +3,10 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_21_3;
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
import com.fastasyncworldedit.core.queue.IBlocks;
import com.fastasyncworldedit.core.queue.IChunk;
import com.fastasyncworldedit.core.queue.IChunkGet;
import com.fastasyncworldedit.core.queue.IChunkSet;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.NbtUtils;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
@ -257,7 +259,7 @@ public class PaperweightGetBlocks_Copy implements IChunkGet {
}
@Override
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk> owner, IChunkSet set, Runnable finalizer) {
return null;
}

Datei anzeigen

@ -55,7 +55,6 @@ import net.minecraft.world.level.chunk.SingleValuePalette;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftChunk;
import javax.annotation.Nonnull;
@ -76,9 +75,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import static java.lang.invoke.MethodType.methodType;
@ -258,12 +256,48 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
}
}
} catch (Throwable e) {
e.printStackTrace();
LOGGER.error("Error apply DelegateSemaphore", 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()) {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
if (nmsChunk != null) {
@ -272,6 +306,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
if (Fawe.isMainThread()) {
return serverLevel.getChunk(chunkX, chunkZ);
}
return null;
} else {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
if (nmsChunk != null) {
@ -287,30 +322,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
if (Fawe.isMainThread()) {
return serverLevel.getChunk(chunkX, chunkZ);
}
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
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 null;
}
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
}
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
@ -649,7 +662,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
}
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
} catch (Throwable throwable) {
throwable.printStackTrace();
LOGGER.error("Error removing beacon", throwable);
}
}