Compare commits

..

2 Commits

Author SHA1 Message Date
Jason Penilla
68c3c6ffa2 leaf formatting 2025-01-17 19:38:58 -07:00
Jason Penilla
b5684eae6d Update for Lithium 0.14.6 compat 2025-01-17 19:38:58 -07:00
21 changed files with 143 additions and 197 deletions

View File

@@ -18,10 +18,13 @@ def aw2at = Aw2AtTask.configureDefault(
sourceSets.main
)
sourceSets.create("lithium")
neoForge {
neoFormVersion = neoform_version
validateAccessTransformers = true
accessTransformers.files.setFrom(aw2at.flatMap { t -> t.getOutputFile() })
addModdingDependenciesTo sourceSets.lithium
}
runConfigCommon {
@@ -41,6 +44,13 @@ dependencies {
// todo: does cloth publish a platform-agnostic jar in mojang mappings?
compileOnly "me.shedaniel.cloth:cloth-config-neoforge:${rootProject.cloth_version}"
lithiumCompileOnly("maven.modrinth:lithium:${rootProject.neo_lithium_version}")
compileOnly(sourceSets.lithium.output)
}
tasks.jar {
from(sourceSets.lithium.output)
}
allprojects {

View File

@@ -13,6 +13,7 @@ dependencies {
testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}"
runtimeOnly(project(":").sourceSets.main.output)
runtimeOnly(project(":").sourceSets.lithium.output)
shadow(project(":"))
compileOnly(project(":"))
@@ -85,6 +86,7 @@ loom {
main {
sourceSet("main")
sourceSet("main", project.rootProject)
sourceSet("lithium", project.rootProject)
}
}
}

View File

@@ -42,6 +42,11 @@ public final class FabricHooks extends BaseChunkSystemHooks implements PlatformH
return "Moonrise";
}
@Override
public boolean isModLoaded(final String modId) {
return FabricLoader.getInstance().isModLoaded(modId);
}
@Override
public int getLightEmission(final BlockState blockState, final BlockGetter world, final BlockPos pos) {
return blockState.getLightEmission();

View File

@@ -8,9 +8,9 @@ org.gradle.configuration-cache=true
minecraft_version=1.21.4
loader_version=0.16.9
supported_minecraft_versions=1.21.4
neoforge_version=21.4.140
neoforge_version=21.4.59-beta
neoform_version=1.21.4-20241203.161809
fabric_api_version=0.119.3+1.21.4
fabric_api_version=0.114.3+1.21.4
snakeyaml_version=2.3
concurrentutil_version=0.0.3
yamlconfig_version=1.0.2
@@ -18,9 +18,9 @@ cloth_version=17.0.144
modmenu_version=13.0.0-beta.1
junit_version=5.11.3
# version ids from modrinth
fabric_lithium_version=t1FlWYl9
neo_lithium_version=iDqQi66g
fabric_lithium_version=zVOQw7YU
neo_lithium_version=CfXh2ZF6
# Mod Properties
mod_version=0.2.0-beta.11
mod_version=0.2.0-SNAPSHOT
maven_group=ca.spottedleaf.moonrise
archives_base_name=moonrise

View File

@@ -28,6 +28,7 @@ neoForge {
moonrise {
sourceSet sourceSets.main
sourceSet rootProject.sourceSets.main
sourceSet rootProject.sourceSets.lithium
}
}
runs {
@@ -46,6 +47,7 @@ neoForge {
dependencies {
runtimeOnly(project(":").sourceSets.main.output)
runtimeOnly(project(":").sourceSets.lithium.output)
shadow(project(":"))
compileOnly(project(":"))

View File

@@ -28,6 +28,8 @@ import net.minecraft.world.level.chunk.status.ChunkStatusTasks;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.phys.AABB;
import net.neoforged.fml.ModList;
import net.neoforged.fml.loading.LoadingModList;
import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.entity.PartEntity;
@@ -46,6 +48,15 @@ public final class NeoForgeHooks extends BaseChunkSystemHooks implements Platfor
return "Moonrise";
}
@Override
public boolean isModLoaded(final String modId) {
final ModList modList = ModList.get();
if (modList == null) {
return LoadingModList.get().getModFileById(modId) != null;
}
return modList.isLoaded(modId);
}
@Override
public int getLightEmission(final BlockState blockState, final BlockGetter world, final BlockPos pos) {
return blockState.getLightEmission(world, pos);

View File

@@ -0,0 +1,18 @@
package ca.spottledleaf.moonrise.compat.lithium;
import net.caffeinemc.mods.lithium.common.world.chunk.ChunkStatusTracker;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.LevelChunk;
public final class LithiumHooks {
private LithiumHooks() {}
public static void onChunkInaccessible(final ServerLevel serverLevel, final ChunkPos pos) {
ChunkStatusTracker.onChunkInaccessible(serverLevel, pos);
}
public static void onChunkAccessible(final ServerLevel serverLevel, final LevelChunk chunk) {
ChunkStatusTracker.onChunkAccessible(serverLevel, chunk);
}
}

View File

@@ -31,6 +31,10 @@ public interface PlatformHooks extends ChunkSystemHooks {
public String getBrand();
public default boolean isModLoaded(final String modId) {
return false;
}
public int getLightEmission(final BlockState blockState, final BlockGetter world, final BlockPos pos);
public Predicate<BlockState> maybeHasLightEmission();

View File

@@ -2,7 +2,6 @@ package ca.spottedleaf.moonrise.common.config.moonrise;
import ca.spottedleaf.moonrise.common.config.ui.ClothConfig;
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import ca.spottedleaf.yamlconfig.InitialiseHook;
import ca.spottedleaf.yamlconfig.annotation.Adaptable;
@@ -39,7 +38,7 @@ public final class MoonriseConfig {
@Adaptable
public static final class Basic implements InitialiseHook {
public static final class Basic {
@Serializable(
comment = """
The maximum rate of chunks to send to any given player, per second. If this value is <= 0,
@@ -73,20 +72,6 @@ public final class MoonriseConfig {
section = CHUNK_SYSTEM_SECTION
)
public double playerMaxGenRate = -1.0;
@Serializable(
comment = """
The delay before chunks are unloaded around players once they leave their view distance.
The Vanilla value is 0 ticks. Setting this value higher (i.e 5s) will allow pets to teleport
to their owners when they teleport.
"""
)
public Duration playerChunkUnloadDelay = Duration.parse("0t");
@Override
public void initialise() {
RegionizedPlayerChunkLoader.setUnloadDelay(this.playerChunkUnloadDelay.getTimeTicks());
}
}
@Serializable(

View File

@@ -1,11 +1,13 @@
package ca.spottedleaf.moonrise.common.util;
import ca.spottedleaf.concurrentutil.util.Priority;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import ca.spottledleaf.moonrise.compat.lithium.LithiumHooks;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerLevel;
@@ -19,6 +21,8 @@ import java.util.function.Consumer;
public abstract class BaseChunkSystemHooks implements ChunkSystemHooks {
private final boolean hasLithium = ((PlatformHooks)this).isModLoaded("lithium");
@Override
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
scheduleChunkTask(level, chunkX, chunkZ, run, Priority.NORMAL);
@@ -101,6 +105,9 @@ public abstract class BaseChunkSystemHooks implements ChunkSystemHooks {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getLoadedChunks().add(
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
if (this.hasLithium) {
LithiumHooks.onChunkAccessible((ServerLevel) chunk.getLevel(), chunk);
}
}
@Override
@@ -108,6 +115,9 @@ public abstract class BaseChunkSystemHooks implements ChunkSystemHooks {
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getLoadedChunks().remove(
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
if (this.hasLithium) {
LithiumHooks.onChunkInaccessible((ServerLevel) chunk.getLevel(), chunk.getPos());
}
}
@Override

View File

@@ -1,44 +0,0 @@
package ca.spottedleaf.moonrise.common.util;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
public final class EntityUtil {
private static final ThreadLocal<DecimalFormat> THREE_DECIMAL_PLACES = ThreadLocal.withInitial(() -> {
return new DecimalFormat("#,##0.000");
});
private static String formatVec(final Vec3 vec) {
final DecimalFormat format = THREE_DECIMAL_PLACES.get();
return "(" + format.format(vec.x) + "," + format.format(vec.y) + "," + format.format(vec.z) + ")";
}
private static String dumpEntityWithoutReferences(final Entity entity) {
if (entity == null) {
return "{null}";
}
return "{type=" + entity.getClass().getSimpleName() + ",id=" + entity.getId() + ",uuid=" + entity.getUUID() + ",pos=" + formatVec(entity.position())
+ ",mot=" + formatVec(entity.getDeltaMovement()) + ",aabb=" + entity.getBoundingBox() + ",removed=" + entity.getRemovalReason() + ",has_vehicle=" + (entity.getVehicle() != null)
+ ",passenger_count=" + entity.getPassengers().size();
}
public static String dumpEntity(final Entity entity) {
final List<Entity> passengers = entity.getPassengers();
final List<String> passengerStrings = new ArrayList<>(passengers.size());
for (final Entity passenger : passengers) {
passengerStrings.add("(" + dumpEntityWithoutReferences(passenger) + ")");
}
return "{root=[" + dumpEntityWithoutReferences(entity) + "], vehicle=[" + dumpEntityWithoutReferences(entity.getVehicle())
+ "], passengers=[" + String.join(",", passengerStrings) + "]";
}
private EntityUtil() {}
}

View File

@@ -15,25 +15,21 @@ public class TickThread extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(TickThread.class);
private static String getThreadContext() {
return "thread=" + Thread.currentThread().getName();
}
/**
* @deprecated
*/
@Deprecated
public static void ensureTickThread(final String reason) {
if (!isTickThread()) {
LOGGER.error("Thread failed main thread check: " + reason + ", context=" + getThreadContext(), new Throwable());
LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
throw new IllegalStateException(reason);
}
}
public static void ensureTickThread(final Level world, final BlockPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos;
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -41,8 +37,8 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final BlockPos pos, final int blockRadius, final String reason) {
if (!isTickThreadFor(world, pos, blockRadius)) {
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius;
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + pos + ", block_radius=" + blockRadius;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -50,8 +46,8 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final ChunkPos pos, final String reason) {
if (!isTickThreadFor(world, pos)) {
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + pos;
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
reason + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + pos;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -59,8 +55,8 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final int chunkX, final int chunkZ, final String reason) {
if (!isTickThreadFor(world, chunkX, chunkZ)) {
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ);
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
reason + ", world=" + WorldUtil.getWorldName(world) + ", chunk_pos=" + new ChunkPos(chunkX, chunkZ);
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -68,8 +64,8 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Entity entity, final String reason) {
if (!isTickThreadFor(entity)) {
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", entity=" + EntityUtil.dumpEntity(entity);
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
reason + ", entity=" + entity;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -77,8 +73,8 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final AABB aabb, final String reason) {
if (!isTickThreadFor(world, aabb)) {
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb;
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
reason + ", world=" + WorldUtil.getWorldName(world) + ", aabb=" + aabb;
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}
@@ -86,8 +82,8 @@ public class TickThread extends Thread {
public static void ensureTickThread(final Level world, final double blockX, final double blockZ, final String reason) {
if (!isTickThreadFor(world, blockX, blockZ)) {
final String ex = "Thread failed main thread check: " +
reason + ", context=" + getThreadContext() + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ);
final String ex = "Thread " + Thread.currentThread().getName() + " failed main thread check: " +
reason + ", world=" + WorldUtil.getWorldName(world) + ", block_pos=" + new Vec3(blockX, 0.0, blockZ);
LOGGER.error(ex, new Throwable());
throw new IllegalStateException(ex);
}

View File

@@ -1,25 +0,0 @@
package ca.spottedleaf.moonrise.mixin.chunk_system;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.gameevent.DynamicGameEventListener;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(DynamicGameEventListener.class)
abstract class DynamicGameEventListenerMixin {
@Shadow @Nullable private SectionPos lastSection;
@Inject(method = "remove", at = @At("RETURN"))
private void onRemove(final CallbackInfo ci) {
// We need to unset the last section when removed, otherwise if the same instance is re-added at the same position it
// will assume there was no change and fail to re-register.
// In vanilla, chunks rarely unload and re-load quickly enough to trigger this issue. However, our chunk system has a
// quirk where fast chunk reload cycles will often occur on player login (see PR #22).
// So we fix this vanilla oversight as our changes cause it to manifest in bugs much more often (see issue #87).
this.lastSection = null;
}
}

View File

@@ -74,7 +74,7 @@ interface EntityGetterMixin {
@Overwrite
default boolean isUnobstructed(final Entity entity, final VoxelShape voxel) {
if (voxel.isEmpty()) {
return true;
return false;
}
final AABB singleAABB = ((CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation();

View File

@@ -55,7 +55,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
public boolean isUnobstructed(final Entity entity) {
final AABB boundingBox = entity.getBoundingBox();
if (CollisionUtil.isEmpty(boundingBox)) {
return true;
return false;
}
final List<Entity> entities = this.getEntities(

View File

@@ -1,13 +1,10 @@
package ca.spottedleaf.moonrise.mixin.serverlist;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.multiplayer.resolver.ServerAddress;
import net.minecraft.client.multiplayer.resolver.ServerAddressResolver;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
@Mixin(ServerAddressResolver.class)
@@ -18,23 +15,24 @@ interface ServerAddressResolverMixin {
* @author Spottedleaf
*/
@Redirect(
method = {
"method_36903",
"lambda$static$0"
},
at = @At(
value = "NEW",
target = "(Ljava/net/InetAddress;I)Ljava/net/InetSocketAddress;"
)
method = {
"method_36903",
"*(Lnet/minecraft/client/multiplayer/resolver/ServerAddress;)Ljava/util/Optional;"
},
at = @At(
value = "INVOKE",
target = "Ljava/net/InetAddress;getByName(Ljava/lang/String;)Ljava/net/InetAddress;"
)
)
private static InetSocketAddress eliminateRDNS(InetAddress addr, final int port,
@Local(ordinal = 0, argsOnly = true) final ServerAddress serverAddress) throws UnknownHostException {
final byte[] address = addr.getAddress();
private static InetAddress eliminateRDNS(final String name) throws UnknownHostException {
final InetAddress ret = InetAddress.getByName(name);
final byte[] address = ret.getAddress();
if (address != null) {
// pass name to prevent rDNS
addr = InetAddress.getByAddress(serverAddress.getHost(), address);
return InetAddress.getByAddress(name, address);
}
return new InetSocketAddress(addr, port);
return ret;
}
}

View File

@@ -1143,7 +1143,7 @@ public final class MoonriseRegionFileIO {
LOGGER.error("Failed to decompress chunk data for task: " + this.toString(), thr);
}
if (throwable == null && compoundTag == null) {
if (compoundTag == null) {
// need to re-try from the start
this.scheduleReadIO();
return;

View File

@@ -46,7 +46,7 @@ import java.util.function.Function;
public final class RegionizedPlayerChunkLoader {
public static final TicketType<Long> PLAYER_TICKET = TicketType.create("chunk_system:player_ticket", Long::compareTo);
public static final TicketType<Long> PLAYER_TICKET_DELAYED = TicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 1);
public static final TicketType<Long> PLAYER_TICKET_DELAYED = TicketType.create("chunk_system:player_ticket_delayed", Long::compareTo, 5 * 20);
public static final int MIN_VIEW_DISTANCE = 2;
public static final int MAX_VIEW_DISTANCE = 32;
@@ -55,10 +55,6 @@ public final class RegionizedPlayerChunkLoader {
public static final int LOADED_TICKET_LEVEL = ChunkTaskScheduler.getTicketLevel(ChunkStatus.EMPTY);
public static final int TICK_TICKET_LEVEL = ChunkHolderManager.ENTITY_TICKING_TICKET_LEVEL;
public static void setUnloadDelay(final long ticks) {
PLAYER_TICKET_DELAYED.timeout = Math.max(1, ticks);
}
public static final class ViewDistanceHolder {
private volatile ViewDistances viewDistances;

View File

@@ -1,6 +1,5 @@
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
import ca.spottedleaf.concurrentutil.util.Priority;
@@ -82,7 +81,6 @@ public final class ChunkHolderManager {
private long currentTick;
private final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = new ArrayDeque<>();
private final MultiThreadedQueue<NewChunkHolder> offThreadPendingFullLoadUpdate = new MultiThreadedQueue<>();
private final ObjectRBTreeSet<NewChunkHolder> autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> {
if (c1 == c2) {
return 0;
@@ -111,20 +109,20 @@ public final class ChunkHolderManager {
this.unloadQueue = new ChunkUnloadQueue(((ChunkSystemServerLevel)world).moonrise$getRegionChunkShift());
}
public boolean processTicketUpdates(final int chunkX, final int chunkZ) {
public boolean processTicketUpdates(final int posX, final int posZ) {
final int ticketShift = ThreadedTicketLevelPropagator.SECTION_SHIFT;
final int ticketMask = (1 << ticketShift) - 1;
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
final boolean ret;
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
((chunkX >> ticketShift) - 1) << ticketShift,
((chunkZ >> ticketShift) - 1) << ticketShift,
(((chunkX >> ticketShift) + 1) << ticketShift) | ticketMask,
(((chunkZ >> ticketShift) + 1) << ticketShift) | ticketMask
((posX >> ticketShift) - 1) << ticketShift,
((posZ >> ticketShift) - 1) << ticketShift,
(((posX >> ticketShift) + 1) << ticketShift) | ticketMask,
(((posZ >> ticketShift) + 1) << ticketShift) | ticketMask
);
try {
ret = this.processTicketUpdatesNoLock(chunkX >> ticketShift, chunkZ >> ticketShift, scheduledTasks, changedFullStatus);
ret = this.processTicketUpdatesNoLock(posX >> ticketShift, posZ >> ticketShift, scheduledTasks, changedFullStatus);
} finally {
this.ticketLockArea.unlock(ticketLock);
}
@@ -722,9 +720,6 @@ public final class ChunkHolderManager {
return removeDelay <= 0L;
};
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
for (final PrimitiveIterator.OfLong iterator = this.sectionToChunkToExpireCount.keyIterator(); iterator.hasNext();) {
final long sectionKey = iterator.nextLong();
@@ -733,16 +728,9 @@ public final class ChunkHolderManager {
continue;
}
final int lowerChunkX = CoordinateUtils.getChunkX(sectionKey) << sectionShift;
final int lowerChunkZ = CoordinateUtils.getChunkZ(sectionKey) << sectionShift;
final int ticketShift = ThreadedTicketLevelPropagator.SECTION_SHIFT;
final int ticketMask = (1 << ticketShift) - 1;
final ReentrantAreaLock.Node ticketLock = this.ticketLockArea.lock(
((lowerChunkX >> ticketShift) - 1) << ticketShift,
((lowerChunkZ >> ticketShift) - 1) << ticketShift,
(((lowerChunkX >> ticketShift) + 1) << ticketShift) | ticketMask,
(((lowerChunkZ >> ticketShift) + 1) << ticketShift) | ticketMask
CoordinateUtils.getChunkX(sectionKey) << sectionShift,
CoordinateUtils.getChunkZ(sectionKey) << sectionShift
);
try {
@@ -789,23 +777,9 @@ public final class ChunkHolderManager {
if (chunkToExpireCount.isEmpty()) {
this.sectionToChunkToExpireCount.remove(sectionKey);
}
// In order to prevent a race condition where an off-thread invokes processTicketUpdates(), we need to process ticket updates here
// so that we catch any additions to the changed full status list. If an off-thread were to process tickets here, it would not be guaranteed
// that it would be added to the full changed status set by the end of the call - possibly allowing ticket level decreases to be processed
// outside of this call, which is not an intended or expected of this chunk system.
this.processTicketUpdatesNoLock(lowerChunkX >> ThreadedTicketLevelPropagator.SECTION_SHIFT, lowerChunkZ >> ThreadedTicketLevelPropagator.SECTION_SHIFT, scheduledTasks, changedFullStatus);
} finally {
this.ticketLockArea.unlock(ticketLock);
}
this.addChangedStatuses(changedFullStatus);
changedFullStatus.clear(); // clear for next loop iteration
for (int i = 0, len = scheduledTasks.size(); i < len; ++i) {
scheduledTasks.get(i).schedule();
}
scheduledTasks.clear(); // clear for next loop iteration
}
this.processTicketUpdates();
@@ -1032,9 +1006,14 @@ public final class ChunkHolderManager {
return;
}
if (!TickThread.isTickThread()) {
// These will be handled on the next ServerChunkCache$MainThreadExecutor#pollTask, as it runs the distance manager update
// which will invoke processTicketUpdates
this.offThreadPendingFullLoadUpdate.addAll(changedFullStatus);
this.taskScheduler.scheduleChunkTask(() -> {
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
pendingFullLoadUpdate.add(changedFullStatus.get(i));
}
ChunkHolderManager.this.processPendingFullUpdate();
}, Priority.HIGHEST);
} else {
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
@@ -1315,20 +1294,36 @@ public final class ChunkHolderManager {
}
public boolean processTicketUpdates() {
return this.processTicketUpdates(true, null);
}
private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
static List<ChunkProgressionTask> getCurrentTicketUpdateScheduling() {
return CURRENT_TICKET_UPDATE_SCHEDULING.get();
}
private boolean processTicketUpdates(final boolean processFullUpdates, List<ChunkProgressionTask> scheduledTasks) {
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager");
}
final boolean isTickThread = TickThread.isTickThread();
if (!PlatformHooks.get().allowAsyncTicketUpdates() && isTickThread) {
if (!PlatformHooks.get().allowAsyncTicketUpdates() && !TickThread.isTickThread()) {
TickThread.ensureTickThread("Cannot asynchronously process ticket updates");
}
List<NewChunkHolder> changedFullStatus = null;
final boolean isTickThread = TickThread.isTickThread();
boolean ret = false;
final boolean canProcessFullUpdates = processFullUpdates & isTickThread;
final boolean canProcessScheduling = scheduledTasks == null;
if (this.ticketLevelPropagator.hasPendingUpdates()) {
final List<ChunkProgressionTask> scheduledTasks = new ArrayList<>();
final List<NewChunkHolder> changedFullStatus = new ArrayList<>();
if (scheduledTasks == null) {
scheduledTasks = new ArrayList<>();
}
changedFullStatus = new ArrayList<>();
this.blockTicketUpdates();
try {
@@ -1339,42 +1334,27 @@ public final class ChunkHolderManager {
} finally {
this.unblockTicketUpdates(Boolean.FALSE);
}
}
if (changedFullStatus != null) {
this.addChangedStatuses(changedFullStatus);
}
if (canProcessScheduling && scheduledTasks != null) {
for (int i = 0, len = scheduledTasks.size(); i < len; ++i) {
scheduledTasks.get(i).schedule();
}
}
if (isTickThread) {
if (canProcessFullUpdates) {
ret |= this.processPendingFullUpdate();
}
return ret;
}
private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
static List<ChunkProgressionTask> getCurrentTicketUpdateScheduling() {
return CURRENT_TICKET_UPDATE_SCHEDULING.get();
}
// only call on tick thread
private void processOffThreadFullUpdates() {
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
final MultiThreadedQueue<NewChunkHolder> offThreadPendingFullLoadUpdate = this.offThreadPendingFullLoadUpdate;
NewChunkHolder toUpdate;
while ((toUpdate = offThreadPendingFullLoadUpdate.poll()) != null) {
pendingFullLoadUpdate.add(toUpdate);
}
}
// only call on tick thread
private boolean processPendingFullUpdate() {
this.processOffThreadFullUpdates();
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
boolean ret = false;

View File

@@ -220,7 +220,6 @@ accessible class net/minecraft/server/level/DistanceManager$FixedPlayerDistanceC
# Ticket
accessible field net/minecraft/server/level/Ticket key Ljava/lang/Object;
accessible field net/minecraft/server/level/TicketType timeout J
mutable field net/minecraft/server/level/TicketType timeout J
# ServerChunkCache

View File

@@ -26,7 +26,6 @@
"chunk_system.ChunkStepMixin",
"chunk_system.ChunkStorageMixin",
"chunk_system.DistanceManagerMixin",
"chunk_system.DynamicGameEventListenerMixin",
"chunk_system.EntityGetterMixin",
"chunk_system.EntityMixin",
"chunk_system.EntityTickListMixin",