mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-29 03:49:21 +00:00
Merge branch 'ver/1.21.4' into dev/random-tick
This commit is contained in:
@@ -13,7 +13,7 @@ public class AsyncExecutor implements Runnable {
|
||||
|
||||
private final Logger LOGGER = LogManager.getLogger("Leaf");
|
||||
private final PriorityQueue<Runnable> jobs = PriorityQueues.synchronize(new ObjectArrayFIFOQueue<>());
|
||||
private final Thread thread;
|
||||
public final Thread thread;
|
||||
private volatile boolean killswitch = false;
|
||||
|
||||
public AsyncExecutor(String threadName) {
|
||||
@@ -28,10 +28,10 @@ public class AsyncExecutor implements Runnable {
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public void kill() throws InterruptedException {
|
||||
public void join(long millis) throws InterruptedException {
|
||||
killswitch = true;
|
||||
LockSupport.unpark(thread);
|
||||
thread.join();
|
||||
thread.join(millis);
|
||||
}
|
||||
|
||||
public void submit(Runnable runnable) {
|
||||
|
||||
@@ -12,20 +12,30 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class AsyncPlayerDataSaving {
|
||||
|
||||
public static final ExecutorService IO_POOL = new ThreadPoolExecutor(
|
||||
1, 1, 0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>(),
|
||||
new com.google.common.util.concurrent.ThreadFactoryBuilder()
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
.setNameFormat("Leaf IO Thread")
|
||||
.setUncaughtExceptionHandler(Util::onThreadException)
|
||||
.build(),
|
||||
new ThreadPoolExecutor.DiscardPolicy()
|
||||
);
|
||||
public static ExecutorService IO_POOL = null;
|
||||
|
||||
private AsyncPlayerDataSaving() {
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
if (IO_POOL == null) {
|
||||
IO_POOL = new ThreadPoolExecutor(
|
||||
1,
|
||||
1,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>(),
|
||||
new com.google.common.util.concurrent.ThreadFactoryBuilder()
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
.setNameFormat("Leaf IO Thread")
|
||||
.setUncaughtExceptionHandler(Util::onThreadException)
|
||||
.build(),
|
||||
new ThreadPoolExecutor.DiscardPolicy()
|
||||
);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<Future<?>> submit(Runnable runnable) {
|
||||
if (!AsyncPlayerDataSave.enabled) {
|
||||
runnable.run();
|
||||
|
||||
@@ -1,37 +1,59 @@
|
||||
package org.dreeam.leaf.async;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dreeam.leaf.async.ai.AsyncGoalThread;
|
||||
import org.dreeam.leaf.async.path.AsyncPathProcessor;
|
||||
import org.dreeam.leaf.async.tracker.MultithreadedTracker;
|
||||
|
||||
public class ShutdownExecutors {
|
||||
public static void shutdown(MinecraftServer server) {
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
if (server.mobSpawnExecutor != null) {
|
||||
public class ShutdownExecutors {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("Leaf");
|
||||
|
||||
public static void shutdown(MinecraftServer server) {
|
||||
if (server.mobSpawnExecutor != null && server.mobSpawnExecutor.thread.isAlive()) {
|
||||
LOGGER.info("Waiting for mob spawning thread to shutdown...");
|
||||
try {
|
||||
server.mobSpawnExecutor.kill();
|
||||
server.mobSpawnExecutor.join(3000L);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (AsyncPlayerDataSaving.IO_POOL != null) {
|
||||
LOGGER.info("Waiting for player I/O executor to shutdown...");
|
||||
AsyncPlayerDataSaving.IO_POOL.shutdown();
|
||||
try {
|
||||
AsyncPlayerDataSaving.IO_POOL.awaitTermination(300L, java.util.concurrent.TimeUnit.SECONDS);
|
||||
AsyncPlayerDataSaving.IO_POOL.awaitTermination(60L, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (server.asyncGoalThread != null) {
|
||||
LOGGER.info("Waiting for mob target finding thread to shutdown...");
|
||||
AsyncGoalThread.RUNNING = false;
|
||||
try {
|
||||
server.asyncGoalThread.join();
|
||||
server.asyncGoalThread.join(3000L);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (MultithreadedTracker.TRACKER_EXECUTOR != null) {
|
||||
LOGGER.info("Waiting for mob tracker executor to shutdown...");
|
||||
MultithreadedTracker.TRACKER_EXECUTOR.shutdown();
|
||||
try {
|
||||
MultithreadedTracker.TRACKER_EXECUTOR.awaitTermination(10L, java.util.concurrent.TimeUnit.SECONDS);
|
||||
MultithreadedTracker.TRACKER_EXECUTOR.awaitTermination(10L, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (AsyncPathProcessor.PATH_PROCESSING_EXECUTOR != null) {
|
||||
LOGGER.info("Waiting for mob pathfinding executor to shutdown...");
|
||||
AsyncPathProcessor.PATH_PROCESSING_EXECUTOR.shutdown();
|
||||
try {
|
||||
AsyncPathProcessor.PATH_PROCESSING_EXECUTOR.awaitTermination(10L, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
public class AsyncGoalThread extends Thread {
|
||||
|
||||
public static volatile boolean RUNNING = true;
|
||||
public AsyncGoalThread(final MinecraftServer server) {
|
||||
super(() -> run(server), "Leaf Async Goal Thread");
|
||||
this.setDaemon(false);
|
||||
@@ -18,7 +19,7 @@ public class AsyncGoalThread extends Thread {
|
||||
}
|
||||
|
||||
private static void run(MinecraftServer server) {
|
||||
while (server.isRunning()) {
|
||||
while (RUNNING) {
|
||||
boolean retry = false;
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
var exec = level.asyncGoalExecutor;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.dreeam.leaf.async.path;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.level.pathfinder.Path;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -15,6 +16,7 @@ import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@@ -28,49 +30,25 @@ public class AsyncPathProcessor {
|
||||
private static final String THREAD_PREFIX = "Leaf Async Pathfinding";
|
||||
private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX);
|
||||
private static long lastWarnMillis = System.currentTimeMillis();
|
||||
private static final ThreadPoolExecutor pathProcessingExecutor = new ThreadPoolExecutor(
|
||||
1,
|
||||
AsyncPathfinding.asyncPathfindingMaxThreads,
|
||||
AsyncPathfinding.asyncPathfindingKeepalive, TimeUnit.SECONDS,
|
||||
getQueueImpl(),
|
||||
new ThreadFactoryBuilder()
|
||||
.setNameFormat(THREAD_PREFIX + " Thread - %d")
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
.build(),
|
||||
new RejectedTaskHandler()
|
||||
);
|
||||
public static ThreadPoolExecutor PATH_PROCESSING_EXECUTOR = null;
|
||||
|
||||
private static class RejectedTaskHandler implements RejectedExecutionHandler {
|
||||
@Override
|
||||
public void rejectedExecution(Runnable rejectedTask, ThreadPoolExecutor executor) {
|
||||
BlockingQueue<Runnable> workQueue = executor.getQueue();
|
||||
if (!executor.isShutdown()) {
|
||||
switch (AsyncPathfinding.asyncPathfindingRejectPolicy) {
|
||||
case FLUSH_ALL -> {
|
||||
if (!workQueue.isEmpty()) {
|
||||
List<Runnable> pendingTasks = new ArrayList<>(workQueue.size());
|
||||
|
||||
workQueue.drainTo(pendingTasks);
|
||||
|
||||
for (Runnable pendingTask : pendingTasks) {
|
||||
pendingTask.run();
|
||||
}
|
||||
}
|
||||
rejectedTask.run();
|
||||
}
|
||||
case CALLER_RUNS -> rejectedTask.run();
|
||||
}
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - lastWarnMillis > 30000L) {
|
||||
LOGGER.warn("Async pathfinding processor is busy! Pathfinding tasks will be treated as policy defined in config. Increasing max-threads in Leaf config may help.");
|
||||
lastWarnMillis = System.currentTimeMillis();
|
||||
}
|
||||
public static void init() {
|
||||
if (PATH_PROCESSING_EXECUTOR == null) {
|
||||
PATH_PROCESSING_EXECUTOR = new ThreadPoolExecutor(
|
||||
getCorePoolSize(),
|
||||
getMaxPoolSize(),
|
||||
getKeepAliveTime(), TimeUnit.SECONDS,
|
||||
getQueueImpl(),
|
||||
getThreadFactory(),
|
||||
getRejectedPolicy()
|
||||
);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
protected static CompletableFuture<Void> queue(@NotNull AsyncPath path) {
|
||||
return CompletableFuture.runAsync(path::process, pathProcessingExecutor)
|
||||
return CompletableFuture.runAsync(path::process, PATH_PROCESSING_EXECUTOR)
|
||||
.orTimeout(60L, TimeUnit.SECONDS)
|
||||
.exceptionally(throwable -> {
|
||||
if (throwable instanceof TimeoutException e) {
|
||||
@@ -98,9 +76,57 @@ public class AsyncPathProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private static int getCorePoolSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int getMaxPoolSize() {
|
||||
return AsyncPathfinding.asyncPathfindingMaxThreads;
|
||||
}
|
||||
|
||||
private static long getKeepAliveTime() {
|
||||
return AsyncPathfinding.asyncPathfindingKeepalive;
|
||||
}
|
||||
|
||||
private static BlockingQueue<Runnable> getQueueImpl() {
|
||||
final int queueCapacity = AsyncPathfinding.asyncPathfindingQueueSize;
|
||||
|
||||
return new LinkedBlockingQueue<>(queueCapacity);
|
||||
}
|
||||
|
||||
private static @NotNull ThreadFactory getThreadFactory() {
|
||||
return new ThreadFactoryBuilder()
|
||||
.setNameFormat(THREAD_PREFIX + " Thread - %d")
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
.setUncaughtExceptionHandler(Util::onThreadException)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static @NotNull RejectedExecutionHandler getRejectedPolicy() {
|
||||
return (Runnable rejectedTask, ThreadPoolExecutor executor) -> {
|
||||
BlockingQueue<Runnable> workQueue = executor.getQueue();
|
||||
if (!executor.isShutdown()) {
|
||||
switch (AsyncPathfinding.asyncPathfindingRejectPolicy) {
|
||||
case FLUSH_ALL -> {
|
||||
if (!workQueue.isEmpty()) {
|
||||
List<Runnable> pendingTasks = new ArrayList<>(workQueue.size());
|
||||
|
||||
workQueue.drainTo(pendingTasks);
|
||||
|
||||
for (Runnable pendingTask : pendingTasks) {
|
||||
pendingTask.run();
|
||||
}
|
||||
}
|
||||
rejectedTask.run();
|
||||
}
|
||||
case CALLER_RUNS -> rejectedTask.run();
|
||||
}
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() - lastWarnMillis > 30000L) {
|
||||
LOGGER.warn("Async pathfinding processor is busy! Pathfinding tasks will be treated as policy defined in config. Increasing max-threads in Leaf config may help.");
|
||||
lastWarnMillis = System.currentTimeMillis();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,5 +60,9 @@ public class AsyncPathfinding extends ConfigModules {
|
||||
? PathfindTaskRejectPolicy.FLUSH_ALL.toString()
|
||||
: PathfindTaskRejectPolicy.CALLER_RUNS.toString())
|
||||
);
|
||||
|
||||
if (enabled) {
|
||||
org.dreeam.leaf.async.path.AsyncPathProcessor.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,5 +19,9 @@ public class AsyncPlayerDataSave extends ConfigModules {
|
||||
异步保存玩家数据.""");
|
||||
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
|
||||
if (enabled) {
|
||||
org.dreeam.leaf.async.AsyncPlayerDataSaving.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ public class MultithreadedTracker extends ConfigModules {
|
||||
|
||||
if (asyncEntityTrackerQueueSize <= 0)
|
||||
asyncEntityTrackerQueueSize = asyncEntityTrackerMaxThreads * 384;
|
||||
|
||||
if (enabled) {
|
||||
org.dreeam.leaf.async.tracker.MultithreadedTracker.init();
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class OptimiseBlockEntities extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName();
|
||||
}
|
||||
|
||||
public static boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".optimise-block-entities", enabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class OptimizeBlockEntities extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName();
|
||||
}
|
||||
|
||||
public static boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
// Transfer old config
|
||||
Boolean optimiseBlockEntities = config.getBoolean(getBasePath() + ".optimise-block-entities");
|
||||
if (optimiseBlockEntities != null && optimiseBlockEntities) {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
enabled = config.getBoolean(getBasePath() + ".optimize-block-entities", enabled);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,5 @@ public class OptimizePlayerMovementProcessing extends ConfigModules {
|
||||
Whether to optimize player movement processing by skipping unnecessary edge checks and avoiding redundant view distance updates.""",
|
||||
"""
|
||||
是否优化玩家移动处理,跳过不必要的边缘检查并避免冗余的视距更新。"""));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class PreloadNaturalMobSpawning extends ConfigModules {
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName() + ".preload-mob-spawning-position";
|
||||
}
|
||||
|
||||
public static boolean enabled = false;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ public class ReduceChunkSourceUpdates extends ConfigModules {
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled,
|
||||
config.pickStringRegionBased(
|
||||
"Reduces chunk source updates on inter-chunk player moves. (Recommended to enable)",
|
||||
"Reduces chunk source updates on inter-chunk player moves.",
|
||||
"减少玩家跨区块移动时的区块源更新。"
|
||||
)
|
||||
);
|
||||
|
||||
@@ -46,25 +46,25 @@ public final class BlockPosIterator extends AbstractIterator<BlockPos> {
|
||||
MutableBlockPos pos = this.pos;
|
||||
if (pos == null) {
|
||||
return this.pos = new MutableBlockPos(this.startX, this.startY, this.startZ);
|
||||
} else {
|
||||
int x = pos.getX();
|
||||
int y = pos.getY();
|
||||
int z = pos.getZ();
|
||||
|
||||
if (y < this.endY) {
|
||||
y += 1;
|
||||
} else if (x < this.endX) {
|
||||
x += 1;
|
||||
y = this.startY;
|
||||
} else if (z < this.endZ) {
|
||||
z += 1;
|
||||
x = this.startX;
|
||||
} else {
|
||||
return this.endOfData();
|
||||
}
|
||||
|
||||
pos.set(x, y, z);
|
||||
return pos;
|
||||
}
|
||||
int x = pos.getX();
|
||||
int y = pos.getY();
|
||||
int z = pos.getZ();
|
||||
|
||||
if (y < this.endY) {
|
||||
y += 1;
|
||||
} else if (x < this.endX) {
|
||||
x += 1;
|
||||
y = this.startY;
|
||||
} else if (z < this.endZ) {
|
||||
z += 1;
|
||||
x = this.startX;
|
||||
y = this.startY; // Reset y also!
|
||||
} else {
|
||||
return this.endOfData();
|
||||
}
|
||||
|
||||
pos.set(x, y, z);
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.dreeam.leaf.world.block;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.Level;
|
||||
@@ -8,8 +9,6 @@ import net.minecraft.world.level.block.PoweredRailBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.RailShape;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static net.minecraft.world.level.block.Block.*;
|
||||
import static net.minecraft.world.level.block.PoweredRailBlock.POWERED;
|
||||
import static net.minecraft.world.level.block.PoweredRailBlock.SHAPE;
|
||||
@@ -23,6 +22,8 @@ public class OptimizedPoweredRails {
|
||||
|
||||
private static int RAIL_POWER_LIMIT = 8;
|
||||
|
||||
private static final Object2BooleanOpenHashMap<BlockPos> CHECKED_POS_POOL = new Object2BooleanOpenHashMap<>();
|
||||
|
||||
private static void giveShapeUpdate(Level level, BlockState state, BlockPos pos, BlockPos fromPos, Direction direction) {
|
||||
BlockState oldState = level.getBlockState(pos);
|
||||
Block.updateOrDestroy(
|
||||
@@ -45,8 +46,8 @@ public class OptimizedPoweredRails {
|
||||
|
||||
public static void updateState(PoweredRailBlock self, BlockState state, Level level, BlockPos pos) {
|
||||
boolean shouldBePowered = level.hasNeighborSignal(pos) ||
|
||||
self.findPoweredRailSignal(level, pos, state, true, 0) ||
|
||||
self.findPoweredRailSignal(level, pos, state, false, 0);
|
||||
findPoweredRailSignalFaster(self, level, pos, state, true, 0, CHECKED_POS_POOL) ||
|
||||
findPoweredRailSignalFaster(self, level, pos, state, false, 0, CHECKED_POS_POOL);
|
||||
if (shouldBePowered != state.getValue(POWERED)) {
|
||||
RailShape railShape = state.getValue(SHAPE);
|
||||
if (railShape.isSlope()) {
|
||||
@@ -63,9 +64,9 @@ public class OptimizedPoweredRails {
|
||||
|
||||
private static boolean findPoweredRailSignalFaster(PoweredRailBlock self, Level world, BlockPos pos,
|
||||
boolean bl, int distance, RailShape shape,
|
||||
HashMap<BlockPos, Boolean> checkedPos) {
|
||||
Object2BooleanOpenHashMap<BlockPos> checkedPos) {
|
||||
BlockState blockState = world.getBlockState(pos);
|
||||
boolean speedCheck = checkedPos.containsKey(pos) && checkedPos.get(pos);
|
||||
boolean speedCheck = checkedPos.containsKey(pos) && checkedPos.getBoolean(pos);
|
||||
if (speedCheck) {
|
||||
return world.hasNeighborSignal(pos) ||
|
||||
findPoweredRailSignalFaster(self, world, pos, blockState, bl, distance + 1, checkedPos);
|
||||
@@ -95,7 +96,7 @@ public class OptimizedPoweredRails {
|
||||
|
||||
private static boolean findPoweredRailSignalFaster(PoweredRailBlock self, Level level,
|
||||
BlockPos pos, BlockState state, boolean bl, int distance,
|
||||
HashMap<BlockPos, Boolean> checkedPos) {
|
||||
Object2BooleanOpenHashMap<BlockPos> checkedPos) {
|
||||
if (distance >= RAIL_POWER_LIMIT - 1) return false;
|
||||
int i = pos.getX();
|
||||
int j = pos.getY();
|
||||
@@ -165,7 +166,8 @@ public class OptimizedPoweredRails {
|
||||
private static void powerLane(PoweredRailBlock self, Level world, BlockPos pos,
|
||||
BlockState mainState, RailShape railShape) {
|
||||
world.setBlock(pos, mainState.setValue(POWERED, true), UPDATE_FORCE_PLACE);
|
||||
HashMap<BlockPos, Boolean> checkedPos = new HashMap<>();
|
||||
Object2BooleanOpenHashMap<BlockPos> checkedPos = CHECKED_POS_POOL;
|
||||
checkedPos.clear();
|
||||
checkedPos.put(pos, true);
|
||||
int[] count = new int[2];
|
||||
if (railShape == RailShape.NORTH_SOUTH) { // Order: +z, -z
|
||||
@@ -179,6 +181,7 @@ public class OptimizedPoweredRails {
|
||||
}
|
||||
updateRails(self, true, world, pos, mainState, count);
|
||||
}
|
||||
checkedPos.clear();
|
||||
}
|
||||
|
||||
private static void dePowerLane(PoweredRailBlock self, Level world, BlockPos pos,
|
||||
@@ -199,39 +202,46 @@ public class OptimizedPoweredRails {
|
||||
}
|
||||
|
||||
private static void setRailPositionsPower(PoweredRailBlock self, Level world, BlockPos pos,
|
||||
HashMap<BlockPos, Boolean> checkedPos, int[] count, int i, Direction dir) {
|
||||
Object2BooleanOpenHashMap<BlockPos> checkedPos, int[] count, int i, Direction dir) {
|
||||
for (int z = 1; z < RAIL_POWER_LIMIT; z++) {
|
||||
BlockPos newPos = pos.relative(dir, z);
|
||||
BlockState state = world.getBlockState(newPos);
|
||||
if (checkedPos.containsKey(newPos)) {
|
||||
if (!checkedPos.get(newPos)) break;
|
||||
if (!checkedPos.getBoolean(newPos))
|
||||
break;
|
||||
count[i]++;
|
||||
} else if (!state.is(self) || state.getValue(POWERED) || !(
|
||||
world.hasNeighborSignal(newPos) ||
|
||||
} else if (!state.is(self) || state.getValue(POWERED) || !(world.hasNeighborSignal(newPos) ||
|
||||
findPoweredRailSignalFaster(self, world, newPos, state, true, 0, checkedPos) ||
|
||||
findPoweredRailSignalFaster(self, world, newPos, state, false, 0, checkedPos)
|
||||
)) {
|
||||
findPoweredRailSignalFaster(self, world, newPos, state, false, 0, checkedPos))) {
|
||||
checkedPos.put(newPos, false);
|
||||
break;
|
||||
} else {
|
||||
checkedPos.put(newPos, true);
|
||||
world.setBlock(newPos, state.setValue(POWERED, true), UPDATE_FORCE_PLACE);
|
||||
if (!state.getValue(POWERED)) {
|
||||
world.setBlock(newPos, state.setValue(POWERED, true), UPDATE_FORCE_PLACE);
|
||||
}
|
||||
count[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setRailPositionsDePower(PoweredRailBlock self, Level world, BlockPos pos,
|
||||
int[] count, int i, Direction dir) {
|
||||
int[] count, int i, Direction dir) {
|
||||
Object2BooleanOpenHashMap<BlockPos> checkedPos = CHECKED_POS_POOL;
|
||||
checkedPos.clear();
|
||||
for (int z = 1; z < RAIL_POWER_LIMIT; z++) {
|
||||
BlockPos newPos = pos.relative(dir, z);
|
||||
BlockState state = world.getBlockState(newPos);
|
||||
if (!state.is(self) || !state.getValue(POWERED) || world.hasNeighborSignal(newPos) ||
|
||||
self.findPoweredRailSignal(world, newPos, state, true, 0) ||
|
||||
self.findPoweredRailSignal(world, newPos, state, false, 0)) break;
|
||||
world.setBlock(newPos, state.setValue(POWERED, false), UPDATE_FORCE_PLACE);
|
||||
findPoweredRailSignalFaster(self, world, newPos, state, true, 0, checkedPos) ||
|
||||
findPoweredRailSignalFaster(self, world, newPos, state, false, 0, checkedPos))
|
||||
break;
|
||||
if (state.getValue(POWERED)) {
|
||||
world.setBlock(newPos, state.setValue(POWERED, false), UPDATE_FORCE_PLACE);
|
||||
}
|
||||
count[i]++;
|
||||
}
|
||||
checkedPos.clear();
|
||||
}
|
||||
|
||||
private static void shapeUpdateEnd(PoweredRailBlock self, Level world, BlockPos pos, BlockState mainState,
|
||||
|
||||
Reference in New Issue
Block a user