Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-11-16 16:10:07 +01:00
fix some bugs/todos
Dieser Commit ist enthalten in:
Ursprung
c0903e71f7
Commit
0bee4a6bf2
@ -95,7 +95,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
if (Fawe.isMainThread()) {
|
||||
if (Fawe.isTickThread()) {
|
||||
return levelChunk.setBlockState(blockPos, blockState,
|
||||
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
);
|
||||
@ -250,7 +250,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
}
|
||||
}
|
||||
};
|
||||
// TODO global sync is not correct on folia
|
||||
// we don't support Folia on that version, we can run this globally
|
||||
TaskManager.taskManager().async(() -> TaskManager.taskManager().syncGlobal(runnableVal));
|
||||
}
|
||||
|
||||
@ -267,10 +267,10 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Fawe.isMainThread()) {
|
||||
if (Fawe.isTickThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
// TODO global sync is not correct on folia
|
||||
// we don't support Folia on that version, we can run this globally
|
||||
TaskManager.taskManager().syncGlobal(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
|
@ -95,7 +95,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
if (Fawe.isMainThread()) {
|
||||
if (Fawe.isTickThread()) {
|
||||
return levelChunk.setBlockState(blockPos, blockState,
|
||||
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
);
|
||||
@ -250,8 +250,8 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
}
|
||||
}
|
||||
};
|
||||
// TODO ???
|
||||
// TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
||||
// we don't support Folia on that version, we can run this globally
|
||||
TaskManager.taskManager().async(() -> TaskManager.taskManager().syncGlobal(runnableVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -267,11 +267,11 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Fawe.isMainThread()) {
|
||||
if (Fawe.isTickThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
// TODO
|
||||
// TaskManager.taskManager().sync(runnableVal);
|
||||
// we don't support Folia on that version, we can run this globally
|
||||
TaskManager.taskManager().syncGlobal(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
cachedChunksToSend.clear();
|
||||
|
@ -1,8 +1,9 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.util.collection.FlushingPartitionedCache;
|
||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
@ -14,21 +15,19 @@ import com.sk89q.worldedit.world.block.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@ -50,8 +49,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
private final PaperweightFaweAdapter paperweightFaweAdapter;
|
||||
private final WeakReference<Level> level;
|
||||
private final AtomicInteger lastTick;
|
||||
private final Set<CachedChange> cachedChanges = new HashSet<>();
|
||||
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
|
||||
private final FlushingPartitionedCache<IntPair, CachedChange, Set<CachedChange>> cache;
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> level) {
|
||||
@ -60,7 +58,29 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
// Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging.
|
||||
// - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway.
|
||||
// TODO
|
||||
this.lastTick = new AtomicInteger(0);
|
||||
this.lastTick = new AtomicInteger();
|
||||
if (!FoliaSupport.isFolia()) {
|
||||
this.lastTick.set(Bukkit.getCurrentTick());
|
||||
}
|
||||
this.cache = new FlushingPartitionedCache<>(
|
||||
cachedChange -> new IntPair(cachedChange.blockPos.getX() >> 4, cachedChange.blockPos.getZ() >> 4),
|
||||
HashSet::new,
|
||||
(chunk, changes) -> {
|
||||
// TODO only send chunks based on ticks? Need to make sure everything actually flushed in the end
|
||||
/*boolean nextTick = true;
|
||||
if (!FoliaSupport.isFolia()) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
nextTick = lastTick.get() > currentTick;
|
||||
if (nextTick) {
|
||||
lastTick.set(currentTick);
|
||||
}
|
||||
}*/
|
||||
flushAsync(chunk, changes, true);
|
||||
},
|
||||
2048,
|
||||
16
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private Level getLevel() {
|
||||
@ -96,22 +116,13 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
LevelChunk levelChunk, BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
if (Fawe.isMainThread()) {
|
||||
if (PaperweightPlatformAdapter.isTickThreadFor(levelChunk)) {
|
||||
return levelChunk.setBlockState(blockPos, blockState,
|
||||
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
);
|
||||
}
|
||||
// Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( )
|
||||
cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState));
|
||||
cachedChunksToSend.add(new IntPair(levelChunk.locX, levelChunk.locZ));
|
||||
boolean nextTick = lastTick.get() > currentTick;
|
||||
if (nextTick || cachedChanges.size() >= 1024) {
|
||||
if (nextTick) {
|
||||
lastTick.set(currentTick);
|
||||
}
|
||||
flushAsync(nextTick);
|
||||
}
|
||||
cache.insert(new CachedChange(levelChunk, blockPos, blockState));
|
||||
return blockState;
|
||||
}
|
||||
|
||||
@ -228,16 +239,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
getLevel().onBlockStateChange(blockPos, oldState, newState);
|
||||
}
|
||||
|
||||
private synchronized void flushAsync(final boolean sendChunks) {
|
||||
final Set<CachedChange> changes = Set.copyOf(cachedChanges);
|
||||
cachedChanges.clear();
|
||||
final Set<IntPair> toSend;
|
||||
if (sendChunks) {
|
||||
toSend = Set.copyOf(cachedChunksToSend);
|
||||
cachedChunksToSend.clear();
|
||||
} else {
|
||||
toSend = Collections.emptySet();
|
||||
}
|
||||
private synchronized void flushAsync(IntPair chunk, Set<CachedChange> changes, final boolean sendChunks) {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
@ -247,36 +249,18 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
if (!sendChunks) {
|
||||
return;
|
||||
}
|
||||
for (IntPair chunk : toSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
};
|
||||
// TODO ???
|
||||
// TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
||||
TaskManager.taskManager().async(
|
||||
() -> PaperweightPlatformAdapter.task(runnableVal, getLevel().getWorld().getHandle(), chunk.x(), chunk.z())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
for (IntPair chunk : cachedChunksToSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Fawe.isMainThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
// TODO
|
||||
// TaskManager.taskManager().sync(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
cachedChunksToSend.clear();
|
||||
this.cache.flush();
|
||||
}
|
||||
|
||||
private record CachedChange(
|
||||
|
@ -7,6 +7,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
@ -24,6 +25,7 @@ import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import io.papermc.paper.util.TickThread;
|
||||
import io.papermc.paper.world.ChunkEntitySlices;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
@ -304,9 +306,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||
try {
|
||||
CraftChunk chunk;
|
||||
try {
|
||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||
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.
|
||||
@ -314,13 +315,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
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();
|
||||
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);
|
||||
// chunk is loaded now, can access it directly
|
||||
return serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -721,6 +723,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
TaskManager.taskManager().task(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ);
|
||||
}
|
||||
|
||||
public static boolean isTickThreadFor(LevelChunk levelChunk) {
|
||||
if (FoliaSupport.isFolia()) {
|
||||
return TickThread.isTickThreadFor(levelChunk.level, levelChunk.locX, levelChunk.locZ);
|
||||
}
|
||||
return Fawe.isTickThread();
|
||||
}
|
||||
|
||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||
|
||||
@Override
|
||||
|
@ -1,9 +1,9 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.util.collection.FlushingPartitionedCache;
|
||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
@ -15,7 +15,6 @@ import com.sk89q.worldedit.world.block.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.level.Level;
|
||||
@ -29,7 +28,6 @@ import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@ -51,8 +49,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
private final PaperweightFaweAdapter paperweightFaweAdapter;
|
||||
private final WeakReference<Level> level;
|
||||
private final AtomicInteger lastTick;
|
||||
private final Set<CachedChange> cachedChanges = new HashSet<>();
|
||||
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
|
||||
private final FlushingPartitionedCache<IntPair, CachedChange, Set<CachedChange>> cache;
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> level) {
|
||||
@ -64,6 +61,24 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
if (!FoliaSupport.isFolia()) {
|
||||
this.lastTick.set(Bukkit.getCurrentTick());
|
||||
}
|
||||
this.cache = new FlushingPartitionedCache<>(
|
||||
cachedChange -> new IntPair(cachedChange.blockPos.getX() >> 4, cachedChange.blockPos.getZ() >> 4),
|
||||
HashSet::new,
|
||||
(chunk, changes) -> {
|
||||
// TODO only send chunks based on ticks? Need to make sure everything actually flushed in the end
|
||||
/*boolean nextTick = true;
|
||||
if (!FoliaSupport.isFolia()) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
nextTick = lastTick.get() > currentTick;
|
||||
if (nextTick) {
|
||||
lastTick.set(currentTick);
|
||||
}
|
||||
}*/
|
||||
flushAsync(chunk, changes, true);
|
||||
},
|
||||
2048,
|
||||
16
|
||||
);
|
||||
}
|
||||
|
||||
private Level getLevel() {
|
||||
@ -99,22 +114,13 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
LevelChunk levelChunk, BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
if (Fawe.isMainThread()) {
|
||||
if (PaperweightPlatformAdapter.isTickThreadFor(levelChunk)) {
|
||||
return levelChunk.setBlockState(blockPos, blockState,
|
||||
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
);
|
||||
}
|
||||
// Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( )
|
||||
cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState));
|
||||
cachedChunksToSend.add(new IntPair(levelChunk.locX, levelChunk.locZ));
|
||||
boolean nextTick = lastTick.get() > currentTick;
|
||||
if (nextTick || cachedChanges.size() >= 1024) {
|
||||
if (nextTick) {
|
||||
lastTick.set(currentTick);
|
||||
}
|
||||
flushAsync(nextTick);
|
||||
}
|
||||
cache.insert(new CachedChange(levelChunk, blockPos, blockState));
|
||||
return blockState;
|
||||
}
|
||||
|
||||
@ -231,16 +237,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
getLevel().onBlockStateChange(blockPos, oldState, newState);
|
||||
}
|
||||
|
||||
private synchronized void flushAsync(final boolean sendChunks) {
|
||||
final Set<CachedChange> changes = Set.copyOf(cachedChanges);
|
||||
cachedChanges.clear();
|
||||
final Set<IntPair> toSend;
|
||||
if (sendChunks) {
|
||||
toSend = Set.copyOf(cachedChunksToSend);
|
||||
cachedChunksToSend.clear();
|
||||
} else {
|
||||
toSend = Collections.emptySet();
|
||||
}
|
||||
private synchronized void flushAsync(IntPair chunk, Set<CachedChange> changes, final boolean sendChunks) {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
@ -250,36 +247,17 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
if (!sendChunks) {
|
||||
return;
|
||||
}
|
||||
for (IntPair chunk : toSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
};
|
||||
// TODO global sync is not correct on folia
|
||||
TaskManager.taskManager().async(() -> TaskManager.taskManager().syncGlobal(runnableVal));
|
||||
TaskManager.taskManager().async(
|
||||
() -> PaperweightPlatformAdapter.task(runnableVal, getLevel().getWorld().getHandle(), chunk.x(), chunk.z())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
for (IntPair chunk : cachedChunksToSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Fawe.isMainThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
// TODO global sync is not correct on folia
|
||||
TaskManager.taskManager().syncGlobal(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
cachedChunksToSend.clear();
|
||||
this.cache.flush();
|
||||
}
|
||||
|
||||
private record CachedChange(
|
||||
|
@ -7,6 +7,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
@ -21,6 +22,7 @@ import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import io.papermc.paper.util.TickThread;
|
||||
import io.papermc.paper.world.ChunkEntitySlices;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
@ -292,9 +294,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||
try {
|
||||
CraftChunk chunk;
|
||||
try {
|
||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||
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.
|
||||
@ -302,26 +303,28 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
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();
|
||||
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);
|
||||
// chunk is loaded now, can access it directly
|
||||
return serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return TaskManager.taskManager().syncAt(() -> serverLevel.getChunk(chunkX, chunkZ),
|
||||
BukkitAdapter.adapt(serverLevel.getWorld()), chunkX, chunkZ);
|
||||
BukkitAdapter.adapt(serverLevel.getWorld()), chunkX, chunkZ
|
||||
);
|
||||
}
|
||||
|
||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
// Ensure chunk is definitely loaded before applying a ticket
|
||||
PaperweightPlatformAdapter.task(() -> serverLevel
|
||||
.getChunkSource()
|
||||
.addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE),
|
||||
.getChunkSource()
|
||||
.addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE),
|
||||
serverLevel, chunkX, chunkZ
|
||||
);
|
||||
}
|
||||
@ -681,6 +684,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
TaskManager.taskManager().task(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ);
|
||||
}
|
||||
|
||||
public static boolean isTickThreadFor(LevelChunk levelChunk) {
|
||||
if (FoliaSupport.isFolia()) {
|
||||
return TickThread.isTickThreadFor(levelChunk.level, levelChunk.locX, levelChunk.locZ);
|
||||
}
|
||||
return Fawe.isTickThread();
|
||||
}
|
||||
|
||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||
|
||||
@Override
|
||||
|
@ -1,8 +1,9 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.util.collection.FlushingPartitionedCache;
|
||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
@ -14,7 +15,6 @@ import com.sk89q.worldedit.world.block.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.level.Level;
|
||||
@ -28,7 +28,6 @@ import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@ -50,8 +49,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
private final PaperweightFaweAdapter paperweightFaweAdapter;
|
||||
private final WeakReference<Level> level;
|
||||
private final AtomicInteger lastTick;
|
||||
private final Set<CachedChange> cachedChanges = new HashSet<>();
|
||||
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
|
||||
private final FlushingPartitionedCache<IntPair, CachedChange, Set<CachedChange>> cache;
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> level) {
|
||||
@ -63,6 +61,24 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
if (!FoliaSupport.isFolia()) {
|
||||
this.lastTick.set(Bukkit.getCurrentTick());
|
||||
}
|
||||
this.cache = new FlushingPartitionedCache<>(
|
||||
cachedChange -> new IntPair(cachedChange.blockPos.getX() >> 4, cachedChange.blockPos.getZ() >> 4),
|
||||
HashSet::new,
|
||||
(chunk, changes) -> {
|
||||
// TODO only send chunks based on ticks? Need to make sure everything actually flushed in the end
|
||||
/*boolean nextTick = true;
|
||||
if (!FoliaSupport.isFolia()) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
nextTick = lastTick.get() > currentTick;
|
||||
if (nextTick) {
|
||||
lastTick.set(currentTick);
|
||||
}
|
||||
}*/
|
||||
flushAsync(chunk, changes, true);
|
||||
},
|
||||
2048,
|
||||
16
|
||||
);
|
||||
}
|
||||
|
||||
private Level getLevel() {
|
||||
@ -98,22 +114,13 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
LevelChunk levelChunk, BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
if (Fawe.isMainThread()) {
|
||||
if (PaperweightPlatformAdapter.isTickThreadFor(levelChunk)) {
|
||||
return levelChunk.setBlockState(blockPos, blockState,
|
||||
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
);
|
||||
}
|
||||
// Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( )
|
||||
cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState));
|
||||
cachedChunksToSend.add(new IntPair(levelChunk.locX, levelChunk.locZ));
|
||||
boolean nextTick = lastTick.get() > currentTick;
|
||||
if (nextTick || cachedChanges.size() >= 1024) {
|
||||
if (nextTick) {
|
||||
lastTick.set(currentTick);
|
||||
}
|
||||
flushAsync(nextTick);
|
||||
}
|
||||
cache.insert(new CachedChange(levelChunk, blockPos, blockState));
|
||||
return blockState;
|
||||
}
|
||||
|
||||
@ -230,16 +237,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
getLevel().onBlockStateChange(blockPos, oldState, newState);
|
||||
}
|
||||
|
||||
private synchronized void flushAsync(final boolean sendChunks) {
|
||||
final Set<CachedChange> changes = Set.copyOf(cachedChanges);
|
||||
cachedChanges.clear();
|
||||
final Set<IntPair> toSend;
|
||||
if (sendChunks) {
|
||||
toSend = Set.copyOf(cachedChunksToSend);
|
||||
cachedChunksToSend.clear();
|
||||
} else {
|
||||
toSend = Collections.emptySet();
|
||||
}
|
||||
private synchronized void flushAsync(IntPair chunk, Set<CachedChange> changes, final boolean sendChunks) {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
@ -249,36 +247,17 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
if (!sendChunks) {
|
||||
return;
|
||||
}
|
||||
for (IntPair chunk : toSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
};
|
||||
// TODO
|
||||
// TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
||||
TaskManager.taskManager().async(
|
||||
() -> PaperweightPlatformAdapter.task(runnableVal, getLevel().getWorld().getHandle(), chunk.x(), chunk.z())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
for (IntPair chunk : cachedChunksToSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Fawe.isMainThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
// TODO
|
||||
// TaskManager.taskManager().sync(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
cachedChunksToSend.clear();
|
||||
this.cache.flush();
|
||||
}
|
||||
|
||||
private record CachedChange(
|
||||
|
@ -7,6 +7,7 @@ import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
@ -21,6 +22,7 @@ import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import io.papermc.paper.util.TickThread;
|
||||
import io.papermc.paper.world.ChunkEntitySlices;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
@ -292,9 +294,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||
try {
|
||||
CraftChunk chunk;
|
||||
try {
|
||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||
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.
|
||||
@ -302,13 +303,14 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
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();
|
||||
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);
|
||||
// chunk is loaded now, can access it directly
|
||||
return serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -678,6 +680,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
TaskManager.taskManager().task(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ);
|
||||
}
|
||||
|
||||
public static boolean isTickThreadFor(LevelChunk levelChunk) {
|
||||
if (FoliaSupport.isFolia()) {
|
||||
return TickThread.isTickThreadFor(levelChunk.level, levelChunk.locX, levelChunk.locZ);
|
||||
}
|
||||
return Fawe.isTickThread();
|
||||
}
|
||||
|
||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||
|
||||
@Override
|
||||
|
@ -135,7 +135,7 @@ public class Fawe {
|
||||
WEManager.weManager().addManagers(Fawe.this.implementation.getMaskManagers());
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}, 0);
|
||||
}, 1);
|
||||
|
||||
if (!FoliaSupport.isFolia()) {
|
||||
// TODO TaskManager.taskManager().repeat(timer, 1);
|
||||
|
@ -10,8 +10,8 @@ public final class FoliaSupport {
|
||||
static {
|
||||
boolean isFolia = false;
|
||||
try {
|
||||
// Assume API is present
|
||||
Class.forName("io.papermc.paper.threadedregions.scheduler.EntityScheduler");
|
||||
// Assume implementation details are present
|
||||
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
|
||||
isFolia = true;
|
||||
} catch (Exception unused) {
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
package com.fastasyncworldedit.core.util.collection;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.io.Flushable;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <P> the partition key type
|
||||
* @param <E> the element type
|
||||
* @param <C> the partition value type
|
||||
* @since TODO
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public class FlushingPartitionedCache<P, E, C extends Collection<E>> implements Flushable {
|
||||
|
||||
private final Map<P, C> map;
|
||||
private final Function<? super E, ? extends P> partition;
|
||||
private final Supplier<? extends C> constructor;
|
||||
private final BiConsumer<? super P, ? super C> flusher;
|
||||
private final int maxEntriesPerCollection;
|
||||
|
||||
public FlushingPartitionedCache(
|
||||
Function<? super E, ? extends P> partition,
|
||||
Supplier<? extends C> constructor,
|
||||
BiConsumer<? super P, ? super C> flusher,
|
||||
int maxEntriesPerCollection,
|
||||
int maxCollections
|
||||
) {
|
||||
this.partition = partition;
|
||||
this.constructor = constructor;
|
||||
this.flusher = flusher;
|
||||
this.maxEntriesPerCollection = maxEntriesPerCollection;
|
||||
this.map = new LinkedHashMap<>(16, 0.75f, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(final Map.Entry<P, C> eldest) {
|
||||
final boolean remove = size() > maxCollections;
|
||||
if (remove) {
|
||||
flusher.accept(eldest.getKey(), eldest.getValue());
|
||||
}
|
||||
return remove;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void insert(E element) {
|
||||
final P partition = this.partition.apply(element);
|
||||
final C coll = this.map.computeIfAbsent(partition, k -> this.constructor.get());
|
||||
coll.add(element);
|
||||
if (coll.size() > this.maxEntriesPerCollection) {
|
||||
this.flusher.accept(partition, coll);
|
||||
this.map.remove(partition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
for (final Map.Entry<P, C> entry : this.map.entrySet()) {
|
||||
this.flusher.accept(entry.getKey(), entry.getValue());
|
||||
}
|
||||
this.map.clear();
|
||||
}
|
||||
|
||||
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren