diff --git a/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/AbstractPosAwarePrioritizedTask.java b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/AbstractPosAwarePrioritizedTask.java new file mode 100644 index 0000000..8cdc25b --- /dev/null +++ b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/AbstractPosAwarePrioritizedTask.java @@ -0,0 +1,50 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021-2026 ishland + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.ishland.c2me.base.common.scheduler; + +import com.ishland.flowsched.executor.Task; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; + +import java.util.Objects; + +public abstract class AbstractPosAwarePrioritizedTask extends Task { + + protected final ReferenceArrayList postExec = new ReferenceArrayList<>(4); + private final long pos; + + public AbstractPosAwarePrioritizedTask(long pos) { + this.pos = pos; + } + + public long getPos() { + return this.pos; + } + + public void addPostExec(Runnable runnable) { + synchronized (this.postExec) { + postExec.add(Objects.requireNonNull(runnable)); + } + } +} diff --git a/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/LockTokenImpl.java b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/LockTokenImpl.java new file mode 100644 index 0000000..84b294a --- /dev/null +++ b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/LockTokenImpl.java @@ -0,0 +1,90 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021-2026 ishland + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.ishland.c2me.base.common.scheduler; + +import com.ishland.flowsched.executor.LockToken; + +import java.util.Objects; + +public final class LockTokenImpl implements LockToken { + private final int ownerTag; + private final long pos; + private final Usage usage; + + public LockTokenImpl(int ownerTag, long pos, Usage usage) { + this.ownerTag = ownerTag; + this.pos = pos; + this.usage = usage; + } + + public int ownerTag() { + return ownerTag; + } + + public long pos() { + return pos; + } + + public Usage usage() { + return usage; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (LockTokenImpl) obj; + return this.ownerTag == that.ownerTag && + this.pos == that.pos && + Objects.equals(this.usage, that.usage); + } + + @Override + public int hashCode() { + // inlined Objects.hash(ownerTag, usage, pos) + int result = 1; + + result = 31 * result + Integer.hashCode(ownerTag); + result = 31 * result + usage.hashCode(); + result = 31 * result + Long.hashCode(pos); + + return result; + } + + @Override + public String toString() { + return "LockTokenImpl[" + + "ownerTag=" + ownerTag + ", " + + "pos=" + pos + ", " + + "usage=" + usage + ']'; + } + + + public enum Usage { + WORLDGEN, + LIGHTING, + } + +} diff --git a/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/ScheduledTask.java b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/ScheduledTask.java new file mode 100644 index 0000000..85b8434 --- /dev/null +++ b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/ScheduledTask.java @@ -0,0 +1,76 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021-2026 ishland + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.ishland.c2me.base.common.scheduler; + +import com.ishland.flowsched.executor.LockToken; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public class ScheduledTask extends AbstractPosAwarePrioritizedTask { + + private final Supplier> action; + private final LockToken[] lockTokens; + private final CompletableFuture future = new CompletableFuture<>(); + + public ScheduledTask(long pos, Supplier> action, LockToken[] lockTokens) { + super(pos); + this.action = action; + this.lockTokens = lockTokens; + } + + @Override + public void run(Runnable releaseLocks) { + action.get().whenComplete((t, throwable) -> { + releaseLocks.run(); + if (throwable != null) { + future.completeExceptionally(throwable); + } else { + future.complete(t); + } + for (Runnable runnable : this.postExec) { + try { + runnable.run(); + } catch (Throwable t1) { + t1.printStackTrace(); + } + } + }); + } + + @Override + public void propagateException(Throwable t) { + future.completeExceptionally(t); + } + + @Override + public LockToken[] lockTokens() { + return this.lockTokens; + } + + public CompletableFuture getFuture() { + return this.future; + } +} diff --git a/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/SchedulingManager.java b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/SchedulingManager.java new file mode 100644 index 0000000..f5ccdc2 --- /dev/null +++ b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/SchedulingManager.java @@ -0,0 +1,266 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021-2026 ishland + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.ishland.c2me.base.common.scheduler; + +import com.ishland.c2me.base.common.GlobalExecutors; +import com.ishland.flowsched.executor.ExecutorManager; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArraySet; +import net.minecraft.server.level.ChunkLevel; +import net.minecraft.world.level.ChunkPos; + +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.StampedLock; + +public class SchedulingManager { + + private static final AtomicInteger COUNTER = new AtomicInteger(0); + + public static final int MAX_LEVEL = ChunkLevel.MAX_LEVEL + 1; + private final ConcurrentMap pos2Tasks = new ConcurrentHashMap<>(); + private final Long2IntOpenHashMap prioritiesFromLevel = new Long2IntOpenHashMap() { + @Override + protected void rehash(int newN) { + if (n < newN) { + super.rehash(newN); + } + } + }; + private final StampedLock prioritiesLock = new StampedLock(); + private final int id = COUNTER.getAndIncrement(); + private volatile ChunkPos currentSyncLoad = null; + + private boolean consolidatingLevelUpdates = false; + private Queue consolidatedLevelUpdates = new ArrayDeque<>(); + + private final Executor executor; + + { + prioritiesFromLevel.defaultReturnValue(MAX_LEVEL); + } + private final ExecutorManager worker; + + public SchedulingManager(Executor managementExecutor, ExecutorManager worker) { + this.executor = managementExecutor; + this.worker = worker; + } + + public void enqueue(AbstractPosAwarePrioritizedTask task) { + retry: + while (true) { + final long pos = task.getPos(); + final FreeableTaskList locks = this.pos2Tasks.computeIfAbsent(pos, unused -> new FreeableTaskList()); + synchronized (locks) { + if (locks.freed) continue retry; + locks.add(task); + } + int priority = this.getPriority(pos); + task.addPostExec(() -> { + final FreeableTaskList tasks = this.pos2Tasks.get(task.getPos()); + if (tasks != null) { + synchronized (tasks) { + if (tasks.freed) return; + tasks.remove(task); + if (tasks.isEmpty()) { + tasks.freed = true; + } + } + if (tasks.freed) { + this.pos2Tasks.remove(task.getPos()); + } + } + }); + this.worker.schedule(task, priority); + return; + } + } + + public void enqueue(long pos, Runnable command) { + this.enqueue(new WrappingTask(pos, command)); + } + + public Executor positionedExecutor(long pos) { + return command -> this.enqueue(pos, command); + } + + public void updatePriorityFromLevel(long pos, int level) { + this.executor.execute(() -> { + updatePriorityFromLevel0(pos, level); + }); + } + + private void updatePriorityFromLevel0(long pos, int level) { + if (this.getPriorityFromMap(pos) == level) return; + final long stamp = this.prioritiesLock.writeLock(); + try { + if (level < MAX_LEVEL) { + this.prioritiesFromLevel.put(pos, level); + } else { + this.prioritiesFromLevel.remove(pos); + } + } finally { + this.prioritiesLock.unlockWrite(stamp); + } + updatePriorityInternal(pos); + } + + public void updatePriorityFromLevelOnMain(long pos, int level) { + if (this.consolidatingLevelUpdates) { + this.consolidatedLevelUpdates.add(() -> updatePriorityFromLevel0(pos, level)); + } else { + updatePriorityFromLevel(pos, level); + } + } + + public void setConsolidatingLevelUpdates(boolean value) { + this.consolidatingLevelUpdates = value; + if (!value) { + if (!this.consolidatedLevelUpdates.isEmpty()) { + Queue runnables = this.consolidatedLevelUpdates; + this.consolidatedLevelUpdates = new ArrayDeque<>(); + this.executor.execute(() -> { + for (Runnable runnable : runnables) { + try { + runnable.run(); + } catch (Throwable t) { + t.printStackTrace(); + } + } + }); + } + } + } + + private void updatePriorityInternal(long pos) { + final int priority = getPriority(pos); + final FreeableTaskList locks = this.pos2Tasks.get(pos); + if (locks != null) { + synchronized (locks) { + if (locks.freed) return; + for (AbstractPosAwarePrioritizedTask lock : locks) { + this.worker.changePriority(lock, priority); + } + } + } + } + + public ExecutorManager getWorker() { + return this.worker; + } + + private int getPriority(long pos) { + final int fromLevel = getPriorityFromMap(pos); + int fromSyncLoad; + ChunkPos currentSyncLoad1 = currentSyncLoad; + if (currentSyncLoad1 != null) { + final int chebyshevDistance = chebyshev(ChunkPos.unpack(pos), currentSyncLoad1); + if (chebyshevDistance <= 8) { + fromSyncLoad = chebyshevDistance; +// System.out.println("dist for chunk [%d,%d] is %d".formatted(currentSyncLoad.x, currentSyncLoad.z, chebyshevDistance)); + } else { + fromSyncLoad = MAX_LEVEL; + } + } else { + fromSyncLoad = MAX_LEVEL; + } + return Math.min(fromLevel, fromSyncLoad); + } + + private int getPriorityFromMap(long pos) { + int fromLevel = MAX_LEVEL; + long stamp = this.prioritiesLock.tryOptimisticRead(); + try { + fromLevel = this.prioritiesFromLevel.get(pos); + } catch (Throwable t) { + } + if (!this.prioritiesLock.validate(stamp)) { + stamp = this.prioritiesLock.readLock(); + try { + fromLevel = this.prioritiesFromLevel.get(pos); + } finally { + this.prioritiesLock.unlockRead(stamp); + } + } + return fromLevel; + } + + public void setCurrentSyncLoad(ChunkPos pos) { + executor.execute(() -> { + if (this.currentSyncLoad != null) { + final ChunkPos lastSyncLoad = this.currentSyncLoad; + this.currentSyncLoad = null; + updateSyncLoadInternal(lastSyncLoad); + } + if (pos != null) { + this.currentSyncLoad = pos; + updateSyncLoadInternal(pos); + } + }); + } + + public int getId() { + return this.id; + } + + private void updateSyncLoadInternal(ChunkPos pos) { + long startTime = System.nanoTime(); + for (int xOff = -8; xOff <= 8; xOff++) { + for (int zOff = -8; zOff <= 8; zOff++) { + updatePriorityInternal(ChunkPos.pack(pos.x() + xOff, pos.z() + zOff)); + } + } + long endTime = System.nanoTime(); + } + + private static int chebyshev(ChunkPos a, ChunkPos b) { + return Math.max(Math.abs(a.x() - b.x()), Math.abs(a.z() - b.z())); + } + + private static int chebyshev(long a, long b) { + return Math.max(Math.abs(ChunkPos.getX(a) - ChunkPos.getX(b)), Math.abs(ChunkPos.getZ(a) - ChunkPos.getZ(b))); + } + + private static class FreeableTaskList extends ObjectArraySet { + + private boolean freed = false; + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + } + +} diff --git a/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/SimplePrioritizedTask.java b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/SimplePrioritizedTask.java new file mode 100644 index 0000000..7c0836d --- /dev/null +++ b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/SimplePrioritizedTask.java @@ -0,0 +1,60 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021-2026 ishland + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.ishland.c2me.base.common.scheduler; + +import com.ishland.flowsched.executor.LockToken; +import com.ishland.flowsched.executor.Task; + +import java.util.Objects; + +public class SimplePrioritizedTask extends Task { + + private final Runnable task; + private final LockToken[] lockTokens; + + public SimplePrioritizedTask(Runnable task, LockToken[] lockTokens) { + this.task = Objects.requireNonNull(task, "task"); + this.lockTokens = Objects.requireNonNull(lockTokens, "lockTokens"); + } + + @Override + public void run(Runnable releaseLocks) { + try { + this.task.run(); + } finally { + releaseLocks.run(); + } + } + + @Override + public void propagateException(Throwable t) { + t.printStackTrace(); + } + + @Override + public LockToken[] lockTokens() { + return this.lockTokens; + } +} diff --git a/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/SingleThreadExecutor.java b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/SingleThreadExecutor.java new file mode 100644 index 0000000..eb731be --- /dev/null +++ b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/SingleThreadExecutor.java @@ -0,0 +1,107 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021-2026 ishland + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.ishland.c2me.base.common.scheduler; + +import com.google.common.base.Preconditions; +import io.netty.util.internal.PlatformDependent; +import org.jetbrains.annotations.NotNull; + +import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.LockSupport; + +public class SingleThreadExecutor extends Thread implements Executor { + + private final AtomicInteger size = new AtomicInteger(); + public final Queue queue = PlatformDependent.newMpscQueue(); + private final AtomicBoolean shutdown = new AtomicBoolean(false); + private final Object sync = new Object(); + + @Override + public void run() { + main_loop: + while (true) { + if (pollTasks()) { + continue; + } + if (this.shutdown.get()) { + return; + } + +// // attempt to spin-wait before sleeping +// if (!pollTasks()) { +// Thread.interrupted(); // clear interrupt flag +// for (int i = 0; i < 5000; i ++) { +// if (pollTasks()) continue main_loop; +// LockSupport.parkNanos("Spin-waiting for tasks", 10_000); // 100us +// } +// } + + synchronized (sync) { + if (this.size.get() != 0 || this.shutdown.get()) continue main_loop; + try { + sync.wait(); + } catch (InterruptedException ignored) { + } + } + } + } + + private boolean pollTasks() { + boolean hasWork = false; + Runnable task; + while ((task = queue.poll()) != null) { + this.size.decrementAndGet(); + try { + task.run(); + } catch (Throwable t) { + t.printStackTrace(); + } + hasWork = true; + } + return hasWork; + } + + @Override + public void execute(@NotNull Runnable command) { + Preconditions.checkNotNull(command, "command"); + final boolean wasEmpty = this.size.getAndIncrement() == 0; + this.queue.add(command); + if (wasEmpty) { + synchronized (sync) { + sync.notify(); + } + } + } + + public void shutdown() { + this.shutdown.set(true); + synchronized (sync) { + sync.notify(); + } + } +} diff --git a/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/ThreadLocalWorldGenSchedulingState.java b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/ThreadLocalWorldGenSchedulingState.java new file mode 100644 index 0000000..66d3620 --- /dev/null +++ b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/ThreadLocalWorldGenSchedulingState.java @@ -0,0 +1,45 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021-2026 ishland + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.ishland.c2me.base.common.scheduler; + +import net.minecraft.server.world.ChunkHolder; + +public class ThreadLocalWorldGenSchedulingState { + + private static final ThreadLocal chunkHolder = new ThreadLocal<>(); + + public static ChunkHolder getChunkHolder() { + return chunkHolder.get(); + } + + public static void setChunkHolder(ChunkHolder holder) { + chunkHolder.set(holder); + } + + public static void clearChunkHolder() { + chunkHolder.remove(); + } + +} diff --git a/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/WrappingTask.java b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/WrappingTask.java new file mode 100644 index 0000000..a934083 --- /dev/null +++ b/camellia-server/src/main/java/com/ishland/c2me/base/common/scheduler/WrappingTask.java @@ -0,0 +1,67 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021-2026 ishland + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.ishland.c2me.base.common.scheduler; + +import com.ishland.flowsched.executor.LockToken; + +import java.util.Objects; + +public class WrappingTask extends AbstractPosAwarePrioritizedTask { + + private static final LockToken[] EMPTY_LOCK_TOKENS = new LockToken[0]; + + private final Runnable wrapped; + + public WrappingTask(long pos, Runnable wrapped) { + super(pos); + this.wrapped = Objects.requireNonNull(wrapped); + } + + @Override + public void run(Runnable releaseLocks) { + try { + wrapped.run(); + } finally { + releaseLocks.run(); + for (Runnable runnable : this.postExec) { + try { + runnable.run(); + } catch (Throwable t1) { + t1.printStackTrace(); + } + } + } + } + + @Override + public void propagateException(Throwable t) { + t.printStackTrace(); + } + + @Override + public LockToken[] lockTokens() { + return EMPTY_LOCK_TOKENS; + } +}