Skip to content

Commit b4635e8

Browse files
authored
fix: some improvements to GET chunk writing (#2853)
* fix: some improvements to GET chunk writing - ensure levelChunk is loaded before giving to copy GET - this is not necessarily guaranteed to be nonnull if two edits overlap. Whilst not advised, such an easy failure should not occur when two edits collide * Prevent writing chunk sections when FAWE is also sending packets for a chunk and vice versa - alter IntPair hashcode to be more often unique - Utilise ConcurrentHashMap for free synchronisation * Minor comment changes * Use one-per-world-instance FaweBukkitWorld to store world chunk map
1 parent 5ac60f0 commit b4635e8

File tree

16 files changed

+425
-166
lines changed

16 files changed

+425
-166
lines changed

worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.fastasyncworldedit.core.configuration.Settings;
88
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
99
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
10+
import com.fastasyncworldedit.core.math.IntPair;
1011
import com.fastasyncworldedit.core.nbt.FaweCompoundTag;
1112
import com.fastasyncworldedit.core.queue.IChunkGet;
1213
import com.fastasyncworldedit.core.queue.IChunkSet;
@@ -106,6 +107,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
106107
private final ServerLevel serverLevel;
107108
private final int chunkX;
108109
private final int chunkZ;
110+
private final IntPair chunkPos;
109111
private final int minHeight;
110112
private final int maxHeight;
111113
private final int minSectionPosition;
@@ -140,6 +142,7 @@ public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) {
140142
this.blockLight = new DataLayer[getSectionCount()];
141143
this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME);
142144
this.biomeHolderIdMap = biomeRegistry.asHolderIdMap();
145+
this.chunkPos = new IntPair(chunkX, chunkZ);
143146
}
144147

145148
public int getChunkX() {
@@ -425,17 +428,15 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
425428
throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked.");
426429
}
427430
forceLoadSections = false;
428-
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null;
431+
LevelChunk nmsChunk = ensureLoaded(serverLevel, chunkX, chunkZ);
432+
PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(nmsChunk) : null;
429433
if (createCopy) {
430434
if (copies.containsKey(copyKey)) {
431435
throw new IllegalStateException("Copy key already used.");
432436
}
433437
copies.put(copyKey, copy);
434438
}
435439
try {
436-
ServerLevel nmsWorld = serverLevel;
437-
LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ);
438-
439440
// Remove existing tiles. Create a copy so that we can remove blocks
440441
Map<BlockPos, BlockEntity> chunkTiles = new HashMap<>(nmsChunk.getBlockEntities());
441442
List<BlockEntity> beacons = null;
@@ -507,6 +508,8 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
507508
biomeData
508509
);
509510
if (PaperweightPlatformAdapter.setSectionAtomic(
511+
serverLevel.getWorld().getName(),
512+
chunkPos,
510513
levelChunkSections,
511514
null,
512515
newSection,
@@ -584,6 +587,8 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
584587
biomeData
585588
);
586589
if (PaperweightPlatformAdapter.setSectionAtomic(
590+
serverLevel.getWorld().getName(),
591+
chunkPos,
587592
levelChunkSections,
588593
null,
589594
newSection,
@@ -649,6 +654,8 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
649654
biomeData != null ? biomeData : (PalettedContainer<Holder<Biome>>) existingSection.getBiomes()
650655
);
651656
if (!PaperweightPlatformAdapter.setSectionAtomic(
657+
serverLevel.getWorld().getName(),
658+
chunkPos,
652659
levelChunkSections,
653660
existingSection,
654661
newSection,
@@ -722,7 +729,7 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
722729
}
723730
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
724731
for (UUID uuid : entityRemoves) {
725-
Entity entity = nmsWorld.getEntities().get(uuid);
732+
Entity entity = serverLevel.getEntities().get(uuid);
726733
if (entity != null) {
727734
removeEntity(entity);
728735
}
@@ -761,7 +768,7 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
761768

762769
EntityType<?> type = EntityType.byString(id).orElse(null);
763770
if (type != null) {
764-
Entity entity = type.create(nmsWorld);
771+
Entity entity = type.create(serverLevel);
765772
if (entity != null) {
766773
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(linTag);
767774
for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
@@ -770,11 +777,11 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
770777
entity.load(tag);
771778
entity.absMoveTo(x, y, z, yaw, pitch);
772779
entity.setUUID(NbtUtils.uuid(nativeTag));
773-
if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
780+
if (!serverLevel.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) {
774781
LOGGER.warn(
775782
"Error creating entity of type `{}` in world `{}` at location `{},{},{}`",
776783
id,
777-
nmsWorld.getWorld().getName(),
784+
serverLevel.getWorld().getName(),
778785
x,
779786
y,
780787
z
@@ -804,11 +811,11 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
804811
final int z = blockHash.z() + bz;
805812
final BlockPos pos = new BlockPos(x, y, z);
806813

807-
synchronized (nmsWorld) {
808-
BlockEntity tileEntity = nmsWorld.getBlockEntity(pos);
814+
synchronized (serverLevel) {
815+
BlockEntity tileEntity = serverLevel.getBlockEntity(pos);
809816
if (tileEntity == null || tileEntity.isRemoved()) {
810-
nmsWorld.removeBlockEntity(pos);
811-
tileEntity = nmsWorld.getBlockEntity(pos);
817+
serverLevel.removeBlockEntity(pos);
818+
tileEntity = serverLevel.getBlockEntity(pos);
812819
}
813820
if (tileEntity != null) {
814821
final CompoundTag tag = (CompoundTag) adapter.fromNativeLin(nativeTag.linTag());
@@ -827,7 +834,6 @@ public synchronized <T extends Future<T>> T call(IChunkSet set, Runnable finaliz
827834
callback = null;
828835
} else {
829836
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
830-
boolean finalLightUpdate = lightUpdate;
831837
callback = () -> {
832838
// Set Modified
833839
nmsChunk.setLightCorrect(true); // Set Modified
@@ -933,7 +939,7 @@ private char[] loadPrivately(int layer) {
933939
@Override
934940
public void send() {
935941
synchronized (sendLock) {
936-
PaperweightPlatformAdapter.sendChunk(this, serverLevel, chunkX, chunkZ);
942+
PaperweightPlatformAdapter.sendChunk(new IntPair(chunkX, chunkZ), serverLevel, chunkX, chunkZ);
937943
}
938944
}
939945

worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import com.fastasyncworldedit.core.Fawe;
88
import com.fastasyncworldedit.core.FaweCache;
99
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
10+
import com.fastasyncworldedit.core.math.IntPair;
1011
import com.fastasyncworldedit.core.util.MathMan;
11-
import com.fastasyncworldedit.core.util.ReflectionUtils;
1212
import com.fastasyncworldedit.core.util.TaskManager;
1313
import com.mojang.datafixers.util.Either;
1414
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
@@ -243,15 +243,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
243243
}
244244

245245
static boolean setSectionAtomic(
246+
String worldName,
247+
IntPair pair,
246248
LevelChunkSection[] sections,
247249
LevelChunkSection expected,
248250
LevelChunkSection value,
249251
int layer
250252
) {
251-
if (layer >= 0 && layer < sections.length) {
252-
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
253-
}
254-
return false;
253+
return NMSAdapter.setSectionAtomic(worldName, pair, sections, expected, value, layer);
255254
}
256255

257256
// There is no point in having a functional semaphore for paper servers.
@@ -349,7 +348,7 @@ public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX,
349348
}
350349

351350
@SuppressWarnings("deprecation")
352-
public static void sendChunk(Object chunk, ServerLevel nmsWorld, int chunkX, int chunkZ) {
351+
public static void sendChunk(IntPair pair, ServerLevel nmsWorld, int chunkX, int chunkZ) {
353352
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
354353
if (chunkHolder == null) {
355354
return;
@@ -370,26 +369,35 @@ public static void sendChunk(Object chunk, ServerLevel nmsWorld, int chunkX, int
370369
if (levelChunk == null) {
371370
return;
372371
}
372+
StampLockHolder lockHolder = new StampLockHolder();
373+
NMSAdapter.beginChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
374+
if (lockHolder.chunkLock == null) {
375+
return;
376+
}
373377
MinecraftServer.getServer().execute(() -> {
374-
ClientboundLevelChunkWithLightPacket packet;
375-
if (PaperLib.isPaper()) {
376-
packet = new ClientboundLevelChunkWithLightPacket(
377-
levelChunk,
378-
nmsWorld.getChunkSource().getLightEngine(),
379-
null,
380-
null,
381-
false // last false is to not bother with x-ray
382-
);
383-
} else {
384-
// deprecated on paper - deprecation suppressed
385-
packet = new ClientboundLevelChunkWithLightPacket(
386-
levelChunk,
387-
nmsWorld.getChunkSource().getLightEngine(),
388-
null,
389-
null
390-
);
378+
try {
379+
ClientboundLevelChunkWithLightPacket packet;
380+
if (PaperLib.isPaper()) {
381+
packet = new ClientboundLevelChunkWithLightPacket(
382+
levelChunk,
383+
nmsWorld.getChunkSource().getLightEngine(),
384+
null,
385+
null,
386+
false // last false is to not bother with x-ray
387+
);
388+
} else {
389+
// deprecated on paper - deprecation suppressed
390+
packet = new ClientboundLevelChunkWithLightPacket(
391+
levelChunk,
392+
nmsWorld.getChunkSource().getLightEngine(),
393+
null,
394+
null
395+
);
396+
}
397+
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
398+
} finally {
399+
NMSAdapter.endChunkPacketSend(nmsWorld.getWorld().getName(), pair, lockHolder);
391400
}
392-
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
393401
});
394402
}
395403

worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightStarlightRelighter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
44
import com.fastasyncworldedit.core.configuration.Settings;
5+
import com.fastasyncworldedit.core.math.IntPair;
56
import com.fastasyncworldedit.core.queue.IQueueExtent;
67
import net.minecraft.server.level.ChunkMap;
78
import net.minecraft.server.level.ServerLevel;
@@ -67,7 +68,7 @@ protected void postProcessChunks(Set<ChunkPos> coords) {
6768
int x = pos.x;
6869
int z = pos.z;
6970
if (delay) { // we still need to send the block changes of that chunk
70-
PaperweightPlatformAdapter.sendChunk(pos, serverLevel, x, z);
71+
PaperweightPlatformAdapter.sendChunk(new IntPair(x, z), serverLevel, x, z);
7172
}
7273
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
7374
}

0 commit comments

Comments
 (0)