Compare commits

..

5 Commits

Author SHA1 Message Date
Jason Penilla
68ea18bc51 0.1.0-beta.8 2024-10-26 11:03:18 -07:00
Jason Penilla
9944946b29 Update Fabric API and call ServerChunkEvents.CHUNK_GENERATE 2024-10-26 09:54:34 -07:00
Jason Penilla
ec1120ed10 Back to 0.1.0-SNAPSHOT 2024-10-24 11:48:08 -07:00
Jason Penilla
8040c7a264 0.1.0-beta.7 2024-10-24 11:40:46 -07:00
Jason Penilla
a9e36795e5 fabric: fix crash when fabric-lifecycle-events-v1 not present 2024-10-23 18:32:06 -07:00
94 changed files with 1889 additions and 1583 deletions

View File

@@ -98,8 +98,7 @@ subprojects {
}
loom.runs.all {
ideConfigGenerated true
property "mixin.debug", "true"
property "Moonrise.MaxViewDistance", "128"
// property "mixin.debug", "true"
}
plugins.apply("me.modmuss50.mod-publish-plugin")

View File

@@ -22,7 +22,7 @@ dependencies {
modImplementation "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
include "me.shedaniel.cloth:cloth-config-fabric:${rootProject.cloth_version}"
modImplementation "com.terraformersmc:modmenu:${rootProject.modmenu_version}"
modImplementation "com.terraformersmc:modmenu:11.0.1"
modImplementation fabricApiLibs.command.api.v2
modImplementation fabricApiLibs.lifecycle.events.v1

View File

@@ -2,30 +2,29 @@ package ca.spottedleaf.moonrise.fabric;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Explosion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
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.minecraft.world.phys.Vec3;
import java.util.List;
import java.util.function.Predicate;
@@ -33,6 +32,19 @@ public final class FabricHooks implements PlatformHooks {
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
public interface OnExplosionDetonate {
void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter);
}
public static final Event<OnExplosionDetonate> ON_EXPLOSION_DETONATE = EventFactory.createArrayBacked(
OnExplosionDetonate.class,
listeners -> (final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) -> {
for (int i = 0; i < listeners.length; i++) {
listeners[i].onExplosion(world, explosion, possiblyAffecting, diameter);
}
}
);
@Override
public String getBrand() {
return "Moonrise";
@@ -50,6 +62,16 @@ public final class FabricHooks implements PlatformHooks {
};
}
@Override
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) {
ON_EXPLOSION_DETONATE.invoker().onExplosion(world, explosion, possiblyAffecting, diameter);
}
@Override
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original) {
return original;
}
@Override
public boolean hasCurrentlyLoadingChunk() {
return false;
@@ -68,7 +90,10 @@ public final class FabricHooks implements PlatformHooks {
@Override
public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original) {
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel)newChunk.getLevel(), newChunk);
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel) newChunk.getLevel(), newChunk);
if (!(original instanceof ImposterProtoChunk)) {
ServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate((ServerLevel)newChunk.getLevel(), newChunk);
}
}
}
@@ -78,19 +103,19 @@ public final class FabricHooks implements PlatformHooks {
}
@Override
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
}
@Override
public void chunkUnloadFromWorld(final LevelChunk chunk) {
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel)chunk.getLevel(), chunk);
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel) chunk.getLevel(), chunk);
}
}
@Override
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
}
@@ -162,12 +187,12 @@ public final class FabricHooks implements PlatformHooks {
}
@Override
public long configAutoSaveInterval(final ServerLevel world) {
public long configAutoSaveInterval() {
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
}
@Override
public int configMaxAutoSavePerTick(final ServerLevel world) {
public int configMaxAutoSavePerTick() {
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
}
@@ -175,47 +200,4 @@ public final class FabricHooks implements PlatformHooks {
public boolean configFixMC159283() {
return ConfigHolder.getConfig().bugFixes.fixMC159283;
}
@Override
public boolean forceNoSave(final ChunkAccess chunk) {
return false;
}
@Override
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
final int fromVersion, final int toVersion) {
return (CompoundTag)dataFixer.update(
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
).getValue();
}
@Override
public boolean hasMainChunkLoadHook() {
return false;
}
@Override
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
}
@Override
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
return entities;
}
@Override
public void unloadEntity(final Entity entity) {
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
}
@Override
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
}
@Override
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
return currentRange;
}
}

View File

@@ -36,7 +36,7 @@
"accessWidener": "moonrise.accesswidener",
"depends": {
"fabricloader": ">=${loader_version}",
"minecraft": ">1.21.1 <1.21.4",
"minecraft": ">=1.21 <=1.21.1",
"fabric-command-api-v2": "*"
},
"custom": {

View File

@@ -3,19 +3,17 @@ org.gradle.jvmargs=-Xmx2G
org.gradle.daemon=false
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.21.3
loader_version=0.16.7
supported_minecraft_versions=1.21.3
neoforge_version=21.3.0-beta
fabric_api_version=0.106.1+1.21.3
minecraft_version=1.21.1
supported_minecraft_versions=1.21,1.21.1
loader_version=0.16.5
neoforge_version=21.1.62
snakeyaml_version=2.2
concurrentutil_version=0.0.2-SNAPSHOT
cloth_version=16.0.141
modmenu_version=12.0.0-beta.1
cloth_version=15.0.128
# version ids from modrinth
fabric_lithium_version=mc1.21.1-0.14.0-beta.1
neo_lithium_version=BrMIoIMv
# Mod Properties
mod_version=0.2.0-beta.1
mod_version=0.1.0-beta.8
maven_group=ca.spottedleaf.moonrise
archives_base_name=moonrise

View File

@@ -3,13 +3,9 @@ package ca.spottedleaf.moonrise.neoforge;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
@@ -17,16 +13,16 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.world.level.Explosion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
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.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.entity.PartEntity;
@@ -56,6 +52,16 @@ public final class NeoForgeHooks implements PlatformHooks {
};
}
@Override
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter) {
EventHooks.onExplosionDetonate(world, explosion, possiblyAffecting, diameter);
}
@Override
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original) {
return EventHooks.getExplosionKnockback(world, explosion, entity, original);
}
@Override
public boolean hasCurrentlyLoadingChunk() {
return true;
@@ -82,12 +88,10 @@ public final class NeoForgeHooks implements PlatformHooks {
}
@Override
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
final ChunkPos pos = holder.getPos();
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
EventHooks.fireChunkTicketLevelUpdated(
world, CoordinateUtils.getChunkKey(pos.x, pos.z),
oldLevel, newLevel, holder
world, CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ),
oldLevel, newLevel, holder.vanillaChunkHolder
);
}
@@ -97,7 +101,7 @@ public final class NeoForgeHooks implements PlatformHooks {
}
@Override
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Save(chunk, world, data));
}
@@ -189,12 +193,12 @@ public final class NeoForgeHooks implements PlatformHooks {
}
@Override
public long configAutoSaveInterval(final ServerLevel world) {
public long configAutoSaveInterval() {
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
}
@Override
public int configMaxAutoSavePerTick(final ServerLevel world) {
public int configMaxAutoSavePerTick() {
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
}
@@ -202,47 +206,4 @@ public final class NeoForgeHooks implements PlatformHooks {
public boolean configFixMC159283() {
return ConfigHolder.getConfig().bugFixes.fixMC159283;
}
@Override
public boolean forceNoSave(final ChunkAccess chunk) {
return false;
}
@Override
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
final int fromVersion, final int toVersion) {
return (CompoundTag)dataFixer.update(
type, new Dynamic<>(NbtOps.INSTANCE, nbt), fromVersion, toVersion
).getValue();
}
@Override
public boolean hasMainChunkLoadHook() {
return true;
}
@Override
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData) {
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, chunkData));
}
@Override
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities) {
return entities;
}
@Override
public void unloadEntity(final Entity entity) {
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
}
@Override
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk) {
ChunkStatusTasks.postLoadProtoChunk(world, chunk.getEntities());
}
@Override
public int modifyEntityTrackingRange(final Entity entity, final int currentRange) {
return currentRange;
}
}

View File

@@ -24,7 +24,7 @@ side = "BOTH"
[[dependencies.moonrise]]
modId = "minecraft"
type = "required"
versionRange = "(1.21.1,1.21.4)"
versionRange = "[1.21,1.21.2)"
ordering = "NONE"
side = "BOTH"

View File

@@ -24,7 +24,7 @@ pluginManagement {
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.297" apply false
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.295" apply false
id 'com.gradleup.shadow' version '8.3.0' apply false
}
@@ -37,7 +37,7 @@ dependencyResolutionManagement {
}
versionCatalogs {
create("fabricApiLibs") {
from("net.fabricmc.fabric-api:fabric-api-catalog:${fabric_api_version}")
from("net.fabricmc.fabric-api:fabric-api-catalog:0.107.0+1.21.1")
}
}
}

View File

@@ -1,25 +1,23 @@
package ca.spottedleaf.moonrise.common;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Explosion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import java.util.List;
import java.util.ServiceLoader;
import java.util.function.Predicate;
@@ -35,6 +33,10 @@ public interface PlatformHooks {
public Predicate<BlockState> maybeHasLightEmission();
public void onExplosion(final Level world, final Explosion explosion, final List<Entity> possiblyAffecting, final double diameter);
public Vec3 modifyExplosionKnockback(final Level world, final Explosion explosion, final Entity entity, final Vec3 original);
public boolean hasCurrentlyLoadingChunk();
public LevelChunk getCurrentlyLoadingChunk(final GenerationChunkHolder holder);
@@ -45,11 +47,11 @@ public interface PlatformHooks {
public boolean allowAsyncTicketUpdates();
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel);
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel);
public void chunkUnloadFromWorld(final LevelChunk chunk);
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data);
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data);
public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player);
@@ -80,30 +82,12 @@ public interface PlatformHooks {
public int configPlayerMaxConcurrentGens();
public long configAutoSaveInterval(final ServerLevel world);
public long configAutoSaveInterval();
public int configMaxAutoSavePerTick(final ServerLevel world);
public int configMaxAutoSavePerTick();
public boolean configFixMC159283();
// support for CB chunk mustNotSave
public boolean forceNoSave(final ChunkAccess chunk);
public CompoundTag convertNBT(final DSL.TypeReference type, final DataFixer dataFixer, final CompoundTag nbt,
final int fromVersion, final int toVersion);
public boolean hasMainChunkLoadHook();
public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData);
public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities);
public void unloadEntity(final Entity entity);
public void postLoadProtoChunk(final ServerLevel world, final ProtoChunk chunk);
public int modifyEntityTrackingRange(final Entity entity, final int currentRange);
public static final class Holder {
private Holder() {
}

View File

@@ -2,7 +2,6 @@ package ca.spottedleaf.moonrise.common.misc;
import ca.spottedleaf.concurrentutil.util.IntPairUtil;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.ReferenceSet;
@@ -15,10 +14,6 @@ public final class PositionCountingAreaMap<T> {
return this.counters.keySet();
}
public LongSet getPositions() {
return this.positions.keySet();
}
public int getTotalPositions() {
return this.positions.size();
}

View File

@@ -118,7 +118,7 @@ public final class ChunkSystem {
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
chunk.postProcessGeneration((ServerLevel)chunk.getLevel());
chunk.postProcessGeneration();
}
((ServerLevel)chunk.getLevel()).startTickingChunk(chunk);
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();

View File

@@ -1,6 +1,5 @@
package ca.spottedleaf.moonrise.common.util;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.config.adapter.TypeAdapterRegistry;
import ca.spottedleaf.moonrise.common.config.config.YamlConfig;
import ca.spottedleaf.moonrise.common.config.moonrise.MoonriseConfig;
@@ -14,7 +13,7 @@ public final class ConfigHolder {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigHolder.class);
private static final File CONFIG_FILE = new File(System.getProperty(PlatformHooks.get().getBrand() + ".ConfigFile", "config/moonrise.yml"));
private static final File CONFIG_FILE = new File(System.getProperty("Moonrise.ConfigFile", "config/moonrise.yml"));
private static final TypeAdapterRegistry CONFIG_ADAPTERS = new TypeAdapterRegistry();
private static final YamlConfig<MoonriseConfig> CONFIG;
static {
@@ -26,7 +25,7 @@ public final class ConfigHolder {
throw new RuntimeException(ex);
}
}
private static final String CONFIG_HEADER = String.format("""
private static final String CONFIG_HEADER = """
This is the configuration file for Moonrise.
Each configuration option is prefixed with a comment to explain what it does. Additional changes to this file
@@ -34,10 +33,10 @@ public final class ConfigHolder {
Below are the Moonrise startup flags. Note that startup flags must be placed in the JVM arguments, not
program arguments.
-D%1$s.ConfigFile=<file> - Override the config file location. Might be useful for multiple game versions.
-D%1$s.WorkerThreadCount=<number> - Override the auto configured worker thread counts (worker-threads).
-D%1$s.MaxViewDistance=<number> - Overrides the maximum view distance, should only use for debugging purposes.
""", PlatformHooks.get().getBrand());
-DMoonrise.ConfigFile=<file> - Override the config file location. Might be useful for multiple game versions.
-DMoonrise.WorkerThreadCount=<number> - Override the auto configured worker thread counts (worker-threads).
-DMoonrise.MaxViewDistance=<number> - Overrides the maximum view distance, should only use for debugging purposes.
""";
static {
reloadConfig();

View File

@@ -3,12 +3,8 @@ package ca.spottedleaf.moonrise.common.util;
public final class MixinWorkarounds {
// mixins tries to find the owner of the clone() method, which doesn't exist and NPEs
// https://github.com/FabricMC/Mixin/pull/147
public static long[] clone(final long[] values) {
return values.clone();
}
public static byte[] clone(final byte[] values) {
return values.clone();
}
}

View File

@@ -19,7 +19,7 @@ public final class MoonriseCommon {
@Override
public void accept(Thread thread) {
thread.setDaemon(true);
thread.setName(PlatformHooks.get().getBrand() + " Common Worker #" + this.idGenerator.getAndIncrement());
thread.setName("Moonrise Common Worker #" + this.idGenerator.getAndIncrement());
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(final Thread thread, final Throwable throwable) {
@@ -44,7 +44,7 @@ public final class MoonriseCommon {
} else {
defaultWorkerThreads = defaultWorkerThreads / 2;
}
defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
defaultWorkerThreads = Integer.getInteger("Moonrise.WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
int workerThreads = configWorkerThreads;

View File

@@ -1,10 +1,8 @@
package ca.spottedleaf.moonrise.common.util;
import ca.spottedleaf.moonrise.common.PlatformHooks;
public final class MoonriseConstants {
public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
public static final int MAX_VIEW_DISTANCE = Integer.getInteger("Moonrise.MaxViewDistance", 32);
private MoonriseConstants() {}

View File

@@ -39,6 +39,7 @@ public final class SimpleRandom extends LegacyRandomSource {
return (int)(seed >>> (BITS - Integer.SIZE));
}
@Override
public int nextInt(final int bound) {
if (bound <= 0) {

View File

@@ -8,19 +8,19 @@ public final class WorldUtil {
// min, max are inclusive
public static int getMaxSection(final LevelHeightAccessor world) {
return world.getMaxSectionY();
return world.getMaxSection() - 1; // getMaxSection() is exclusive
}
public static int getMaxSection(final Level world) {
return world.getMaxSectionY();
return world.getMaxSection() - 1; // getMaxSection() is exclusive
}
public static int getMinSection(final LevelHeightAccessor world) {
return world.getMinSectionY();
return world.getMinSection();
}
public static int getMinSection(final Level world) {
return world.getMinSectionY();
return world.getMinSection();
}
public static int getMaxLightSection(final LevelHeightAccessor world) {

View File

@@ -154,9 +154,9 @@ abstract class LevelChunkSectionMixin implements BlockCountingChunkSection {
if (this.maybeHas((final BlockState state) -> !state.isAir())) {
final PalettedContainer.Data<BlockState> data = this.states.data;
final Palette<BlockState> palette = data.palette();
final Palette<BlockState> palette = data.palette;
final int paletteSize = palette.getSize();
final BitStorage storage = data.storage();
final BitStorage storage = data.storage;
final Int2ObjectOpenHashMap<ShortArrayList> counts;
if (paletteSize == 1) {

View File

@@ -6,7 +6,6 @@ import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.Property;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -18,9 +17,6 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
super(string, class_);
}
@Unique
private static final Boolean[] BY_ID = new Boolean[]{ Boolean.FALSE, Boolean.TRUE };
@Override
public final int moonrise$getIdFor(final Boolean value) {
return value.booleanValue() ? 1 : 0;
@@ -37,6 +33,23 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
)
)
private void init(final CallbackInfo ci) {
this.moonrise$setById(BY_ID);
this.moonrise$setById(new Boolean[]{ Boolean.FALSE, Boolean.TRUE });
}
/**
* This skips all ops after the identity comparison in the original code.
*
* @reason Properties are identity comparable
* @author Spottedleaf
*/
@WrapOperation(
method = "equals",
constant = @Constant(
classValue = BooleanProperty.class,
ordinal = 0
)
)
private boolean skipFurtherComparison(final Object obj, final Operation<Boolean> orig) {
return false;
}
}

View File

@@ -18,6 +18,9 @@ import java.util.Collection;
@Mixin(EnumProperty.class)
abstract class EnumPropertyMixin<T extends Enum<T> & StringRepresentable> extends Property<T> implements PropertyAccess<T> {
@Shadow
public abstract Collection<T> getPossibleValues();
@Unique
private int[] idLookupTable;

View File

@@ -83,6 +83,7 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
}
// remove values arrays
this.values = null;
for (final Map.Entry<Map<Property<?>, Comparable<?>>, S> entry : map.entrySet()) {
final S value = entry.getValue();
((StateHolderMixin<O, S>)(Object)(StateHolder<O, S>)value).values = null;
@@ -125,8 +126,8 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
* @author Spottedleaf
*/
@Overwrite
public <T extends Comparable<T>> T getNullableValue(Property<T> property) {
return property == null ? null : this.optimisedTable.get(this.tableIndex, property);
public <T extends Comparable<T>> Optional<T> getOptionalValue(final Property<T> property) {
return property == null ? Optional.empty() : Optional.ofNullable(this.optimisedTable.get(this.tableIndex, property));
}
/**

View File

@@ -66,6 +66,9 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
@Unique
private final ReferenceList<ServerPlayer> playersSentChunkTo = new ReferenceList<>(EMPTY_PLAYER_ARRAY);
@Unique
private boolean isMarkedDirtyForPlayers;
@Unique
private ChunkMap getChunkMap() {
return (ChunkMap)this.playerProvider;
@@ -120,6 +123,16 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
return ret;
}
@Override
public final boolean moonrise$isMarkedDirtyForPlayers() {
return this.isMarkedDirtyForPlayers;
}
@Override
public final void moonrise$markDirtyForPlayers(final boolean value) {
this.isMarkedDirtyForPlayers = value;
}
@Unique
private static final ServerPlayer[] EMPTY_PLAYER_ARRAY = new ServerPlayer[0];
@@ -287,7 +300,14 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
// no players to sent to, so don't need to update anything
return null;
}
return this.getChunkToSend();
final LevelChunk ret = this.getChunkToSend();
if (ret != null) {
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
return ret;
}
return ret;
}
/**
@@ -306,7 +326,14 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
// no players to sent to, so don't need to update anything
return null;
}
return this.getChunkToSend();
final LevelChunk ret = this.getChunkToSend();
if (ret != null) {
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
return ret;
}
return ret;
}
/**

View File

@@ -11,17 +11,14 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import com.mojang.datafixers.DataFixer;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntConsumer;
import java.util.function.Supplier;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.StreamTagVisitor;
import net.minecraft.server.level.ChunkGenerationTask;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ChunkResult;
import net.minecraft.server.level.ChunkTaskDispatcher;
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
import net.minecraft.server.level.ChunkTrackingView;
import net.minecraft.server.level.GeneratingChunkMap;
import net.minecraft.server.level.GenerationChunkHolder;
@@ -31,6 +28,7 @@ import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.Mth;
import net.minecraft.util.StaticCache2D;
import net.minecraft.util.thread.BlockableEventLoop;
import net.minecraft.util.thread.ProcessorHandle;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
@@ -80,10 +78,13 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
private volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap;
@Shadow
private ChunkTaskDispatcher worldgenTaskDispatcher;
private ChunkTaskPriorityQueueSorter queueSorter;
@Shadow
private ChunkTaskDispatcher lightTaskDispatcher;
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> worldgenMailbox;
@Shadow
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> mainThreadMailbox;
@Shadow
private int serverViewDistance;
@@ -97,12 +98,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
@Shadow
private Queue<Runnable> unloadQueue;
@Shadow
private LongSet chunksToEagerlySave;
@Shadow
private AtomicInteger activeChunkWrites;
public ChunkMapMixin(RegionStorageInfo regionStorageInfo, Path path, DataFixer dataFixer, boolean bl) {
super(regionStorageInfo, path, dataFixer, bl);
}
@@ -133,12 +128,11 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
this.updatingChunkMap = null;
this.visibleChunkMap = null;
this.pendingUnloads = null;
this.worldgenTaskDispatcher = null;
this.lightTaskDispatcher = null;
this.queueSorter = null;
this.worldgenMailbox = null;
this.mainThreadMailbox = null;
this.pendingGenerationTasks = null;
this.unloadQueue = null;
this.chunksToEagerlySave = null;
this.activeChunkWrites = null;
// Dummy impl for mods that try to loadAsync directly
this.worker = new IOWorker(
@@ -158,7 +152,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
@Override
public CompletableFuture<Optional<CompoundTag>> loadAsync(final ChunkPos chunkPos) {
final CompletableFuture<Optional<CompoundTag>> future = new CompletableFuture<>();
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (final CompoundTag tag, final Throwable throwable) -> {
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (tag, throwable) -> {
if (throwable != null) {
future.completeExceptionally(throwable);
} else {
@@ -190,15 +184,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
};
}
/**
* @reason This map is not needed, we maintain our own ordered set of chunks to autosave.
* @author Spottedleaf
*/
@Overwrite
public void setChunkUnsaved(final ChunkPos pos) {
}
/**
* @reason Route to new chunk system hooks
* @author Spottedleaf
@@ -276,15 +261,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
throw new UnsupportedOperationException();
}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public void onLevelChange(final ChunkPos chunkPos, final IntSupplier intSupplier, final int i, final IntConsumer intConsumer) {
throw new UnsupportedOperationException();
}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
@@ -333,15 +309,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.autoSave();
}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public void saveChunksEagerly(final BooleanSupplier hasTime) {
throw new UnsupportedOperationException();
}
/**
* @reason Destroy old chunk system hooks
* @author Spottedleaf
@@ -436,7 +403,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
* @author Spottedleaf
*/
@Overwrite
public void onChunkReadyToSend(final ChunkHolder holder, final LevelChunk chunk) {
public void onChunkReadyToSend(final LevelChunk chunk) {
throw new UnsupportedOperationException();
}
@@ -455,7 +422,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
* @see NewChunkHolder#save(boolean)
*/
@Overwrite
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder, final long time) {
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder) {
throw new UnsupportedOperationException();
}
@@ -538,21 +505,6 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
throw new UnsupportedOperationException();
}
/**
* @reason Route to new chunk system
* @author Spottedleaf
*/
@Redirect(
method = "forEachSpawnCandidateChunk",
at = @At(
value = "INVOKE",
target = "Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;get(J)Ljava/lang/Object;"
)
)
private <V> V redirectChunkHolderGet(final Long2ObjectLinkedOpenHashMap<V> instance, final long key) {
return (V)this.getVisibleChunkIfPresent(key);
}
@Override
public CompletableFuture<Optional<CompoundTag>> read(final ChunkPos pos) {
final CompletableFuture<Optional<CompoundTag>> ret = new CompletableFuture<>();
@@ -572,11 +524,10 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
}
@Override
public CompletableFuture<Void> write(final ChunkPos pos, final Supplier<CompoundTag> tag) {
public CompletableFuture<Void> write(final ChunkPos pos, final CompoundTag tag) {
MoonriseRegionFileIO.scheduleSave(
this.level, pos.x, pos.z, tag.get(),
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA
);
this.level, pos.x, pos.z, tag,
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA);
return null;
}

View File

@@ -3,13 +3,13 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
import net.minecraft.core.SectionPos;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(SerializableChunkData.class)
abstract class SerializableChunkDataMixin {
@Mixin(ChunkSerializer.class)
abstract class ChunkSerializerMixin {
/**
* @reason Chunk system handles this during full transition
@@ -17,12 +17,12 @@ abstract class SerializableChunkDataMixin {
* @see ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkFullTask
*/
@Redirect(
method = "read",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;checkConsistencyWithBlocks(Lnet/minecraft/core/SectionPos;Lnet/minecraft/world/level/chunk/LevelChunkSection;)V"
)
method = "read",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/entity/ai/village/poi/PoiManager;checkConsistencyWithBlocks(Lnet/minecraft/core/SectionPos;Lnet/minecraft/world/level/chunk/LevelChunkSection;)V"
)
)
private void skipConsistencyCheck(final PoiManager instance, final SectionPos sectionPos, final LevelChunkSection levelChunkSection) {}
private static void skipConsistencyCheck(PoiManager instance, SectionPos sectionPos, LevelChunkSection levelChunkSection) {}
}

View File

@@ -22,13 +22,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
@Mixin(ChunkStorage.class)
abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseable {
@Shadow
public IOWorker worker;
private IOWorker worker;
@Unique
private static final Logger LOGGER = LogUtils.getLogger();
@@ -119,13 +118,13 @@ abstract class ChunkStorageMixin implements ChunkSystemChunkStorage, AutoCloseab
method = "write",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Ljava/util/function/Supplier;)Ljava/util/concurrent/CompletableFuture;"
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;"
)
)
private CompletableFuture<Void> redirectWrite(final IOWorker instance, final ChunkPos chunkPos,
final Supplier<CompoundTag> compoundTag) {
final CompoundTag compoundTag) {
try {
this.storage.write(chunkPos, compoundTag.get());
this.storage.write(chunkPos, compoundTag);
return CompletableFuture.completedFuture(null);
} catch (final Throwable throwable) {
return CompletableFuture.failedFuture(throwable);

View File

@@ -41,8 +41,8 @@ abstract class ClientLevelMixin extends Level implements ChunkSystemLevel {
@Final
private ClientChunkCache chunkSource;
protected ClientLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
protected ClientLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
}
/**
@@ -55,8 +55,9 @@ abstract class ClientLevelMixin extends Level implements ChunkSystemLevel {
value = "RETURN"
)
)
private void init(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey<Level> resourceKey,
Holder<DimensionType> holder, int i, int j, LevelRenderer levelRenderer, boolean bl, long l, int k, CallbackInfo ci) {
private void init(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData,
ResourceKey<Level> resourceKey, Holder<DimensionType> holder, int i, int j, Supplier<ProfilerFiller> supplier,
LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) {
this.entityStorage = null;
this.moonrise$setEntityLookup(new ClientEntityLookup(this, ((ClientLevel)(Object)this).new EntityCallbacks()));

View File

@@ -8,12 +8,13 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.ThrottlingChunkTaskDispatcher;
import net.minecraft.server.level.Ticket;
import net.minecraft.server.level.TicketType;
import net.minecraft.server.level.TickingTracker;
import net.minecraft.util.SortedArraySet;
import net.minecraft.util.thread.ProcessorHandle;
import net.minecraft.world.level.ChunkPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
@@ -44,7 +45,13 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
Set<ChunkHolder> chunksToUpdateFutures;
@Shadow
ThrottlingChunkTaskDispatcher ticketDispatcher;
ChunkTaskPriorityQueueSorter ticketThrottler;
@Shadow
ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> ticketThrottlerInput;
@Shadow
ProcessorHandle<ChunkTaskPriorityQueueSorter.Release> ticketThrottlerReleaser;
@Shadow
LongSet ticketsToRelease;
@@ -53,8 +60,10 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
Executor mainThreadExecutor;
@Shadow
private int simulationDistance;
private DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter;
@Shadow
private int simulationDistance;
@Override
public ChunkMap moonrise$getChunkMap() {
@@ -77,14 +86,16 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
this.tickingTicketsTracker = null;
this.playerTicketManager = null;
this.chunksToUpdateFutures = null;
this.ticketDispatcher = null;
this.ticketThrottler = null;
this.ticketThrottlerInput = null;
this.ticketThrottlerReleaser = null;
this.ticketsToRelease = null;
this.mainThreadExecutor = null;
this.simulationDistance = -1;
}
@Override
public final ChunkHolderManager moonrise$getChunkHolderManager() {
public ChunkHolderManager moonrise$getChunkHolderManager() {
return ((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getChunkTaskScheduler().chunkHolderManager;
}
@@ -315,15 +326,6 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
throw new UnsupportedOperationException();
}
/**
* @reason Remove old chunk system hooks
* @author Spottedleaf
*/
@Overwrite
public LongSet getTickingChunks() {
throw new UnsupportedOperationException();
}
/**
* @reason This hack is not required anymore, see {@link MinecraftServerMixin}
* @author Spottedleaf

View File

@@ -195,6 +195,15 @@ abstract class GenerationChunkHolderMixin {
throw new UnsupportedOperationException();
}
/**
* @reason Chunk system is not built on futures anymore
* @author Spottedleaf
*/
@Overwrite
public int getGenerationRefCount() {
throw new UnsupportedOperationException();
}
/**
* @reason Route to new chunk holder
* @author Spottedleaf

View File

@@ -93,15 +93,11 @@ abstract class LevelChunkMixin extends ChunkAccess implements ChunkSystemLevelCh
}
@Override
public boolean tryMarkSaved() {
if (!this.isUnsaved()) {
return false;
public void setUnsaved(final boolean needsSaving) {
if (!needsSaving) {
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
}
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
super.tryMarkSaved();
return true;
super.setUnsaved(needsSaving);
}
}

View File

@@ -10,7 +10,6 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEnti
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.util.profiling.Profiler;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
@@ -38,6 +37,9 @@ import java.util.function.Predicate;
@Mixin(Level.class)
abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter, LevelAccessor, AutoCloseable {
@Shadow
public abstract ProfilerFiller getProfiler();
@Shadow
public abstract LevelChunk getChunk(int i, int j);
@@ -57,7 +59,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
}
@Override
public final void moonrise$setEntityLookup(final EntityLookup entityLookup) {
public void moonrise$setEntityLookup(final EntityLookup entityLookup) {
if (this.entityLookup != null && !(this.entityLookup instanceof DefaultEntityLookup)) {
throw new IllegalStateException("Entity lookup already initialised");
}
@@ -85,7 +87,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
@Overwrite
@Override
public List<Entity> getEntities(final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate) {
Profiler.get().incrementCounter("getEntities");
this.getProfiler().incrementCounter("getEntities");
final List<Entity> ret = new ArrayList<>();
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entity, boundingBox, ret, predicate);
@@ -103,7 +105,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
public <T extends Entity> void getEntities(final EntityTypeTest<Entity, T> entityTypeTest,
final AABB boundingBox, final Predicate<? super T> predicate,
final List<? super T> into, final int maxCount) {
Profiler.get().incrementCounter("getEntities");
this.getProfiler().incrementCounter("getEntities");
if (entityTypeTest instanceof EntityType<T> byType) {
if (maxCount != Integer.MAX_VALUE) {
@@ -176,7 +178,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
*/
@Override
public final <T extends Entity> List<T> getEntitiesOfClass(final Class<T> entityClass, final AABB boundingBox, final Predicate<? super T> predicate) {
Profiler.get().incrementCounter("getEntities");
this.getProfiler().incrementCounter("getEntities");
final List<T> ret = new ArrayList<>();
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entityClass, null, boundingBox, ret, predicate);
@@ -194,7 +196,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
*/
@Override
public final List<Entity> moonrise$getHardCollidingEntities(final Entity entity, final AABB box, final Predicate<? super Entity> predicate) {
Profiler.get().incrementCounter("getEntities");
this.getProfiler().incrementCounter("getEntities");
final List<Entity> ret = new ArrayList<>();
((ChunkSystemLevel)this).moonrise$getEntityLookup().getHardCollidingEntities(entity, box, ret, predicate);

View File

@@ -240,7 +240,6 @@ abstract class MinecraftServerMixin extends ReentrantBlockableEventLoop<TickTask
private void closeIOThreads(final CallbackInfo ci) {
LOGGER.info("Waiting for I/O tasks to complete...");
MoonriseRegionFileIO.flush((MinecraftServer)(Object)this);
LOGGER.info("All I/O tasks to complete");
if ((Object)this instanceof DedicatedServer) {
MoonriseCommon.haltExecutors();
}

View File

@@ -36,7 +36,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -45,14 +44,7 @@ import java.util.stream.Stream;
@Mixin(PoiManager.class)
// Declare the generic type as Object so that our Overrides match the method signature of the superclass
// Specifically, getOrCreate must return Object so that existing invokes do not route to the superclass
public abstract class PoiManagerMixin extends SectionStorage<Object, Object> implements ChunkSystemPoiManager {
public PoiManagerMixin(final SimpleRegionStorage simpleRegionStorage, final Codec<Object> codec, final Function<Object, Object> function,
final BiFunction<Object, Runnable, Object> biFunction, final Function<Runnable, Object> function2,
final RegistryAccess registryAccess, final ChunkIOErrorReporter chunkIOErrorReporter,
final LevelHeightAccessor levelHeightAccessor) {
super(simpleRegionStorage, codec, function, biFunction, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
}
public abstract class PoiManagerMixin extends SectionStorage<Object> implements ChunkSystemPoiManager {
@Shadow
abstract boolean isVillageCenter(long l);
@@ -60,6 +52,10 @@ public abstract class PoiManagerMixin extends SectionStorage<Object, Object> imp
@Shadow
public abstract void checkConsistencyWithBlocks(SectionPos sectionPos, LevelChunkSection levelChunkSection);
public PoiManagerMixin(SimpleRegionStorage simpleRegionStorage, Function<Runnable, Codec<Object>> function, Function<Runnable, Object> function2, RegistryAccess registryAccess, ChunkIOErrorReporter chunkIOErrorReporter, LevelHeightAccessor levelHeightAccessor) {
super(simpleRegionStorage, function, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
}
@Unique
private ServerLevel world;
@@ -155,8 +151,8 @@ public abstract class PoiManagerMixin extends SectionStorage<Object, Object> imp
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
final PoiChunk ret = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
.getPoiChunkIfLoaded(chunkX, chunkZ, true);
final ChunkHolderManager manager = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager;
final PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
return ret == null ? Optional.empty() : (Optional)ret.getSectionForVanilla(chunkY);
}
@@ -210,13 +206,9 @@ public abstract class PoiManagerMixin extends SectionStorage<Object, Object> imp
public final void moonrise$onUnload(final long coordinate) { // Paper - rewrite chunk system
final int chunkX = CoordinateUtils.getChunkX(coordinate);
final int chunkZ = CoordinateUtils.getChunkZ(coordinate);
final int minY = WorldUtil.getMinSection(this.world);
final int maxY = WorldUtil.getMaxSection(this.world);
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Unloading poi chunk off-main");
for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
final long sectionPos = SectionPos.asLong(chunkX, sectionY, chunkZ);
for (int section = this.levelHeightAccessor.getMinSection(); section < this.levelHeightAccessor.getMaxSection(); ++section) {
final long sectionPos = SectionPos.asLong(chunkX, section, chunkZ);
this.updateDistanceTracking(sectionPos);
}
}
@@ -225,12 +217,8 @@ public abstract class PoiManagerMixin extends SectionStorage<Object, Object> imp
public final void moonrise$loadInPoiChunk(final PoiChunk poiChunk) {
final int chunkX = poiChunk.chunkX;
final int chunkZ = poiChunk.chunkZ;
final int minY = WorldUtil.getMinSection(this.world);
final int maxY = WorldUtil.getMaxSection(this.world);
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Loading poi chunk off-main");
for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
for (int sectionY = this.levelHeightAccessor.getMinSection(); sectionY < this.levelHeightAccessor.getMaxSection(); ++sectionY) {
final PoiSection section = poiChunk.getSection(sectionY);
if (section != null && !((ChunkSystemPoiSection)section).moonrise$isEmpty()) {
this.onSectionLoad(SectionPos.asLong(chunkX, sectionY, chunkZ));
@@ -266,4 +254,20 @@ public abstract class PoiManagerMixin extends SectionStorage<Object, Object> imp
private <T> Stream<T> skipLoadedSet(final Stream<T> instance, final Predicate<? super T> predicate) {
return instance;
}
@Override
public final void moonrise$close() throws IOException {}
@Override
public final CompoundTag moonrise$read(final int chunkX, final int chunkZ) throws IOException {
return MoonriseRegionFileIO.loadData(
this.world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.POI_DATA,
MoonriseRegionFileIO.getIOBlockingPriorityForCurrentThread()
);
}
@Override
public final void moonrise$write(final int chunkX, final int chunkZ, final CompoundTag data) throws IOException {
MoonriseRegionFileIO.scheduleSave(this.world, chunkX, chunkZ, data, MoonriseRegionFileIO.RegionFileType.POI_DATA);
}
}

View File

@@ -30,7 +30,7 @@ abstract class PoiSectionMixin implements ChunkSystemPoiSection {
@Unique
private final Optional<PoiSection> noAllocOptional = Optional.of((PoiSection)(Object)this);
private final Optional<PoiSection> noAllocOptional = Optional.of((PoiSection)(Object)this);;
@Override
public final boolean moonrise$isEmpty() {

View File

@@ -2,6 +2,8 @@ package ca.spottedleaf.moonrise.mixin.chunk_system;
import ca.spottedleaf.moonrise.patches.chunk_system.level.storage.ChunkSystemSectionStorage;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.RegistryOps;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
import net.minecraft.world.level.chunk.storage.SectionStorage;
@@ -21,11 +23,15 @@ import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Mixin(SectionStorage.class)
abstract class SectionStorageMixin<R, P> implements ChunkSystemSectionStorage, AutoCloseable {
abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoCloseable {
@Shadow
private SimpleRegionStorage simpleRegionStorage;
@Shadow
@Final
private static Logger LOGGER;
@Unique
private RegionFileStorage storage;
@@ -35,9 +41,6 @@ abstract class SectionStorageMixin<R, P> implements ChunkSystemSectionStorage, A
return this.storage;
}
@Override
public void moonrise$close() throws IOException {}
/**
* @reason Retrieve storage from IOWorker, and then nuke it
* @author Spottedleaf
@@ -58,8 +61,12 @@ abstract class SectionStorageMixin<R, P> implements ChunkSystemSectionStorage, A
* @author Spottedleaf
*/
@Overwrite
public final CompletableFuture<Optional<SectionStorage.PackedChunk<P>>> tryRead(final ChunkPos pos) {
throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName());
public final CompletableFuture<Optional<CompoundTag>> tryRead(final ChunkPos pos) {
try {
return CompletableFuture.completedFuture(Optional.ofNullable(this.moonrise$read(pos.x, pos.z)));
} catch (final Throwable thr) {
return CompletableFuture.failedFuture(thr);
}
}
/**
@@ -67,17 +74,29 @@ abstract class SectionStorageMixin<R, P> implements ChunkSystemSectionStorage, A
* @author Spottedleaf
*/
@Overwrite
public void unpackChunk(final ChunkPos chunkPos, final SectionStorage.PackedChunk<P> packedChunk) {
public void readColumn(final ChunkPos pos, final RegistryOps<Tag> ops, final CompoundTag data) {
throw new IllegalStateException("Only chunk system can load in state, offending class:" + this.getClass().getName());
}
/**
* @reason Destroy old chunk system hook
* @reason Route to new chunk system hook
* @author Spottedleaf
*/
@Overwrite
private void writeChunk(final ChunkPos chunkPos) {
throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName());
@Redirect(
method = "writeColumn(Lnet/minecraft/world/level/ChunkPos;)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/chunk/storage/SimpleRegionStorage;write(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;"
)
)
private CompletableFuture<Void> redirectWrite(final SimpleRegionStorage instance, final ChunkPos pos,
final CompoundTag tag) {
try {
this.moonrise$write(pos.x, pos.z, tag);
} catch (final IOException ex) {
LOGGER.error("Error writing poi chunk data to disk for chunk " + pos, ex);
}
return null;
}
/**
@@ -94,4 +113,4 @@ abstract class SectionStorageMixin<R, P> implements ChunkSystemSectionStorage, A
private void redirectClose(final SimpleRegionStorage instance) throws IOException {
this.moonrise$close();
}
}
}

View File

@@ -24,7 +24,6 @@ import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LightChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.storage.DimensionDataStorage;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
@@ -52,10 +51,6 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
@Final
public ServerLevel level;
@Shadow
@Final
private DimensionDataStorage dataStorage;
@Unique
private final ConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new ConcurrentLong2ReferenceChainedHashTable<>();
@@ -266,7 +261,6 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
@Override
@Overwrite
public void close() throws IOException {
this.dataStorage.close();
((ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.close(true, true);
}
@@ -320,7 +314,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
* @author Spottedleaf
*/
@Inject(
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
method = "tickChunks",
at = @At(
value = "INVOKE",
shift = At.Shift.AFTER,
@@ -341,13 +335,60 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerLevel;shouldTickBlocksAt(J)Z"
)
method = "tickChunks",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerLevel;isNaturalSpawningAllowed(Lnet/minecraft/world/level/ChunkPos;)Z"
)
)
private boolean shortNaturalSpawning(final ServerLevel instance, final ChunkPos chunkPos) {
return true;
}
/**
* @reason In the chunk system, ticking chunks always have loaded entities. Of course, they are also always
* marked to be as ticking as well.
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ServerLevel;shouldTickBlocksAt(J)Z"
)
)
private boolean shortShouldTickBlocks(final ServerLevel instance, final long pos) {
return true;
}
/**
* @reason Since chunks in non-simulation range are only brought up to FULL status, not TICKING,
* those chunks may not be present in the ticking list and as a result we need to use our own list
* to ensure these chunks broadcast changes
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks",
at = @At(
value = "INVOKE",
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V"
)
)
private void fixBroadcastChanges(final List<ServerChunkCache.ChunkAndHolder> instance,
final Consumer<ServerChunkCache.ChunkAndHolder> consumer) {
final ReferenceList<ChunkHolder> unsyncedChunks = ((ChunkSystemServerLevel)this.level).moonrise$getUnsyncedChunks();
final ChunkHolder[] chunkHolders = unsyncedChunks.getRawDataUnchecked();
final int totalUnsyncedChunks = unsyncedChunks.size();
Objects.checkFromToIndex(0, totalUnsyncedChunks, chunkHolders.length);
for (int i = 0; i < totalUnsyncedChunks; ++i) {
final ChunkHolder chunkHolder = chunkHolders[i];
final LevelChunk chunk = chunkHolder.getChunkToSend();
if (chunk != null) {
chunkHolder.broadcastChanges(chunk);
}
}
((ChunkSystemServerLevel)this.level).moonrise$clearUnsyncedChunks();
}
}

View File

@@ -86,8 +86,8 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
@Final
private ServerChunkCache chunkSource;
protected ServerLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
protected ServerLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
}
@Unique
@@ -123,6 +123,9 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
@Unique
private static final ServerChunkCache.ChunkAndHolder[] EMPTY_CHUNK_AND_HOLDERS = new ServerChunkCache.ChunkAndHolder[0];
@Unique
private static final ChunkHolder[] EMPTY_CHUNK_HOLDERS = new ChunkHolder[0];
@Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> loadedChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
@@ -132,6 +135,9 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
@Unique
private final ReferenceList<ServerChunkCache.ChunkAndHolder> entityTickingChunks = new ReferenceList<>(EMPTY_CHUNK_AND_HOLDERS);
@Unique
private final ReferenceList<ChunkHolder> unsyncedChunks = new ReferenceList<>(EMPTY_CHUNK_HOLDERS);
/**
* @reason Initialise fields / destroy entity manager state
* @author Spottedleaf
@@ -348,6 +354,45 @@ abstract class ServerLevelMixin extends Level implements ChunkSystemServerLevel,
return this.entityTickingChunks;
}
@Override
public final ReferenceList<ChunkHolder> moonrise$getUnsyncedChunks() {
return this.unsyncedChunks;
}
@Override
public final void moonrise$addUnsyncedChunk(final ChunkHolder chunkHolder) {
if (((ChunkSystemChunkHolder)chunkHolder).moonrise$isMarkedDirtyForPlayers()) {
return;
}
((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(true);
this.unsyncedChunks.add(chunkHolder);
}
@Override
public final void moonrise$removeUnsyncedChunk(final ChunkHolder chunkHolder) {
if (!((ChunkSystemChunkHolder)chunkHolder).moonrise$isMarkedDirtyForPlayers()) {
return;
}
((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(false);
this.unsyncedChunks.remove(chunkHolder);
}
@Override
public final void moonrise$clearUnsyncedChunks() {
final ChunkHolder[] chunkHolders = this.unsyncedChunks.getRawDataUnchecked();
final int totalUnsyncedChunks = this.unsyncedChunks.size();
Objects.checkFromToIndex(0, totalUnsyncedChunks, chunkHolders.length);
for (int i = 0; i < totalUnsyncedChunks; ++i) {
final ChunkHolder chunkHolder = chunkHolders[i];
((ChunkSystemChunkHolder)chunkHolder).moonrise$markDirtyForPlayers(false);
}
this.unsyncedChunks.clear();
}
@Override
public final boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ) {
final ServerChunkCache chunkSource = this.chunkSource;

View File

@@ -84,21 +84,12 @@ abstract class ChunkMapMixin {
((ChunkTickDistanceManager)this.distanceManager).moonrise$removePlayer(player, SectionPos.of(player));
}
/**
* @reason Avoid checking first if there are nearby players, as we make internal perform this implicitly.
* @author Spottedleaf
*/
@Overwrite
public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) {
return this.anyPlayerCloseEnoughForSpawningInternal(pos);
}
/**
* @reason Use nearby players to avoid iterating over all online players
* @author Spottedleaf
*/
@Overwrite
public boolean anyPlayerCloseEnoughForSpawningInternal(final ChunkPos pos) {
public boolean anyPlayerCloseEnoughForSpawning(final ChunkPos pos) {
final ReferenceList<ServerPlayer> players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers(
pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE
);

View File

@@ -4,7 +4,6 @@ import ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager;
import it.unimi.dsi.fastutil.longs.LongIterator;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.DistanceManager;
import net.minecraft.server.level.ServerPlayer;
@@ -107,13 +106,4 @@ abstract class DistanceManagerMixin implements ChunkTickDistanceManager {
public boolean hasPlayersNearby(final long pos) {
return this.spawnChunkTracker.hasObjectsNear(CoordinateUtils.getChunkX(pos), CoordinateUtils.getChunkZ(pos));
}
/**
* @reason Use spawnChunkTracker instead
* @author Spottedleaf
*/
@Overwrite
public LongIterator getSpawnCandidateChunks() {
return this.spawnChunkTracker.getPositions().iterator();
}
}

View File

@@ -7,6 +7,8 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
import com.llamalad7.mixinextras.sugar.Local;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.Util;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache;
@@ -16,15 +18,22 @@ import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.LevelChunk;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@Mixin(ServerChunkCache.class)
abstract class ServerChunkCacheMixin extends ChunkSource {
@@ -33,18 +42,110 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
@Final
public ServerLevel level;
@Shadow
@Final
public ChunkMap chunkMap;
@Unique
private ServerChunkCache.ChunkAndHolder[] iterationCopy;
@Unique
private int iterationCopyLen;
@Unique
private final SimpleRandom shuffleRandom = new SimpleRandom(0L);
@Unique
private boolean isChunkNearPlayer(final ChunkMap chunkMap, final ChunkPos chunkPos, final LevelChunk levelChunk) {
final ChunkData chunkData = ((ChunkSystemChunkHolder)((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkAndHolder().holder())
.moonrise$getRealChunkHolder().holderData;
/**
* @reason Avoid creating the list, which is sized at the chunkholder count. The actual number of ticking
* chunks is always lower. The mixin below will initialise the list to non-null.
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks",
at = @At(
value = "INVOKE",
target = "Lcom/google/common/collect/Lists;newArrayListWithCapacity(I)Ljava/util/ArrayList;"
)
)
private <T> ArrayList<T> avoidListCreation(final int initialArraySize) {
return null;
}
/**
* @reason Initialise the list to contain only the ticking chunks.
* @author Spottedleaf
*/
@ModifyVariable(
method = "tickChunks",
at = @At(
value = "STORE",
opcode = Opcodes.ASTORE,
ordinal = 0
)
)
private List<ServerChunkCache.ChunkAndHolder> initTickChunks(final List<ServerChunkCache.ChunkAndHolder> shouldBeNull) {
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
final int size = tickingChunks.size();
if (this.iterationCopy == null || this.iterationCopy.length < size) {
this.iterationCopy = new ServerChunkCache.ChunkAndHolder[raw.length];
}
this.iterationCopyLen = size;
System.arraycopy(raw, 0, this.iterationCopy, 0, size);
return ObjectArrayList.wrap(
this.iterationCopy, size
);
}
/**
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
* function
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
)
)
private <T> void useBetterRandom(final List<T> list, final RandomSource randomSource) {
this.shuffleRandom.setSeed(randomSource.nextLong());
Util.shuffle(list, this.shuffleRandom);
}
/**
* @reason Do not initialise ticking chunk list, as we did that above.
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks",
at = @At(
value = "INVOKE",
target = "Ljava/util/Iterator;hasNext()Z",
ordinal = 0
)
)
private <E> boolean skipTickAdd(final Iterator<E> instance) {
return false;
}
/**
* @reason Use the nearby players cache
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/level/ChunkMap;anyPlayerCloseEnoughForSpawning(Lnet/minecraft/world/level/ChunkPos;)Z"
)
)
private boolean useNearbyCache(final ChunkMap instance, final ChunkPos chunkPos,
@Local(ordinal = 0, argsOnly = false) final LevelChunk levelChunk) {
final ChunkData chunkData =
((ChunkSystemChunkHolder)((ChunkSystemLevelChunk)levelChunk).moonrise$getChunkAndHolder().holder())
.moonrise$getRealChunkHolder().holderData;
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
if (nearbyPlayers == null) {
return false;
@@ -61,7 +162,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
Objects.checkFromIndexSize(0, len, raw.length);
for (int i = 0; i < len; ++i) {
if (chunkMap.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
if (instance.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
return true;
}
}
@@ -70,47 +171,19 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
}
/**
* @reason Use the player ticking chunks list, which already contains chunks that are:
* 1. block ticking
* 2. within spawn range (8 chunks on any axis)
* @reason Clear the iteration array after the list is done being used.
* @author Spottedleaf
*/
@Overwrite
private void collectTickingChunks(final List<LevelChunk> list) {
final ReferenceList<ServerChunkCache.ChunkAndHolder> tickingChunks =
((ChunkTickServerLevel)this.level).moonrise$getPlayerTickingChunks();
final ServerChunkCache.ChunkAndHolder[] raw = tickingChunks.getRawDataUnchecked();
final int size = tickingChunks.size();
final ChunkMap chunkMap = this.chunkMap;
for (int i = 0; i < size; ++i) {
final ServerChunkCache.ChunkAndHolder chunkAndHolder = raw[i];
final LevelChunk levelChunk = chunkAndHolder.chunk();
if (!this.isChunkNearPlayer(chunkMap, levelChunk.getPos(), levelChunk)) {
continue;
}
list.add(levelChunk);
}
}
/**
* @reason Use random implementation which does not use CAS and has a faster nextInt(int)
* function
* @author Spottedleaf
*/
@Redirect(
method = "tickChunks()V",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/Util;shuffle(Ljava/util/List;Lnet/minecraft/util/RandomSource;)V"
)
@Inject(
method = "tickChunks",
at = @At(
value = "INVOKE",
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V",
ordinal = 0,
shift = At.Shift.AFTER
)
)
private <T> void useBetterRandom(final List<T> list, final RandomSource randomSource) {
this.shuffleRandom.setSeed(randomSource.nextLong());
Util.shuffle(list, this.shuffleRandom);
private void broadcastChanges(final CallbackInfo ci) {
Arrays.fill(this.iterationCopy, 0, this.iterationCopyLen, null);
}
}

View File

@@ -6,32 +6,37 @@ import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.vehicle.AbstractMinecart;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import java.util.List;
import java.util.function.Predicate;
@Mixin(ArmorStand.class)
abstract class ArmorStandMixin extends LivingEntity {
@Shadow
@Final
private static Predicate<Entity> RIDABLE_MINECARTS;
protected ArmorStandMixin(EntityType<? extends LivingEntity> entityType, Level level) {
super(entityType, level);
}
/**
* @reason Optimise by making it use the class lookup
* @reason Optimise this method by making it use the class lookup
* @author Spottedleaf
*/
@Redirect(
method = "pushEntities",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/Level;getEntities(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/AABB;Ljava/util/function/Predicate;)Ljava/util/List;"
)
)
private List<Entity> redirectGetEntities(final Level instance, final Entity entity, final AABB list, final Predicate<? super Entity> arg) {
return (List)this.level().getEntitiesOfClass(AbstractMinecart.class, this.getBoundingBox(), arg);
@Overwrite
public void pushEntities() {
final List<AbstractMinecart> nearby = this.level().getEntitiesOfClass(AbstractMinecart.class, this.getBoundingBox(), RIDABLE_MINECARTS);
for (int i = 0, len = nearby.size(); i < len; ++i) {
final AbstractMinecart minecart = nearby.get(i);
if (this.distanceToSqr(minecart) <= 0.2) {
minecart.push(this);
}
}
}
}

View File

@@ -34,12 +34,6 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
@Shadow
public abstract VoxelShape getCollisionShape(BlockGetter blockGetter, BlockPos blockPos, CollisionContext collisionContext);
@Shadow
public VoxelShape[] occlusionShapesByFace;
@Shadow
public VoxelShape occlusionShape;
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> reference2ObjectArrayMap, MapCodec<BlockState> mapCodec) {
super(object, reference2ObjectArrayMap, mapCodec);
}
@@ -80,7 +74,7 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
}
if (neighbours) {
for (final Direction direction : DIRECTIONS_CACHED) {
initCaches(((CollisionVoxelShape)shape).moonrise$getFaceShapeClamped(direction), false);
initCaches(Shapes.getFaceShape(shape, direction), false);
initCaches(shape.getFaceShape(direction), false);
}
}
@@ -113,21 +107,17 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
if (this.constantCollisionShape != null) {
initCaches(this.constantCollisionShape, true);
}
if (this.cache.occlusionShapes != null) {
for (final VoxelShape shape : this.cache.occlusionShapes) {
initCaches(shape, false);
}
}
} else {
this.occludesFullBlock = false;
this.emptyCollisionShape = false;
this.emptyConstantCollisionShape = false;
this.constantCollisionShape = null;
}
if (this.occlusionShape != null) {
initCaches(this.occlusionShape, true);
}
if (this.occlusionShapesByFace != null) {
for (final VoxelShape shape : this.occlusionShapesByFace) {
initCaches(shape, true);
}
}
}
@Override

View File

@@ -1,16 +1,20 @@
package ca.spottedleaf.moonrise.mixin.collisions;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
import ca.spottedleaf.moonrise.patches.collisions.util.NoneMatchStream;
import it.unimi.dsi.fastutil.floats.FloatArraySet;
import it.unimi.dsi.fastutil.floats.FloatArrays;
import net.minecraft.core.BlockPos;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.LevelChunkSection;
@@ -23,8 +27,11 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
@Mixin(Entity.class)
abstract class EntityMixin {
@@ -50,6 +57,12 @@ abstract class EntityMixin {
@Shadow
private boolean onGround;
@Shadow
public abstract boolean isAlive();
@Shadow
protected abstract void onInsideBlock(final BlockState blockState);
@Unique
private static float[] calculateStepHeights(final AABB box, final List<VoxelShape> voxels, final List<AABB> aabbs, final float stepHeight,
final float collidedY) {
@@ -283,4 +296,163 @@ abstract class EntityMixin {
return false;
}
/**
* @reason Avoid streams for retrieving blocks
* @author Spottedleaf
*/
@Redirect(
method = "move",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/Level;getBlockStatesIfLoaded(Lnet/minecraft/world/phys/AABB;)Ljava/util/stream/Stream;",
ordinal = 0
)
)
private <T> Stream<T> avoidStreams(final Level world, final AABB boundingBox) {
final int minBlockX = Mth.floor(boundingBox.minX);
final int minBlockY = Mth.floor(boundingBox.minY);
final int minBlockZ = Mth.floor(boundingBox.minZ);
final int maxBlockX = Mth.floor(boundingBox.maxX);
final int maxBlockY = Mth.floor(boundingBox.maxY);
final int maxBlockZ = Mth.floor(boundingBox.maxZ);
final int minChunkX = minBlockX >> 4;
final int minChunkY = minBlockY >> 4;
final int minChunkZ = minBlockZ >> 4;
final int maxChunkX = maxBlockX >> 4;
final int maxChunkY = maxBlockY >> 4;
final int maxChunkZ = maxBlockZ >> 4;
if (!((ChunkSystemLevel)world).moonrise$areChunksLoaded(minChunkX, minChunkZ, maxChunkX, maxChunkZ)) {
return new NoneMatchStream<>(true);
}
final int minSection = WorldUtil.getMinSection(world);
final ChunkSource chunkSource = world.getChunkSource();
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
final int sectionIdx = currChunkY - minSection;
if (sectionIdx < 0 || sectionIdx >= sections.length) {
continue;
}
final LevelChunkSection section = sections[sectionIdx];
if (section.hasOnlyAir()) {
// empty
continue;
}
final PalettedContainer<BlockState> blocks = section.states;
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));
if (blockState.is(Blocks.LAVA) || blockState.is(BlockTags.FIRE)) {
return new NoneMatchStream<>(false);
}
}
}
}
}
}
}
return new NoneMatchStream<>(true);
}
/**
* @reason Retrieve blocks more efficiently
* @author Spottedleaf
*/
@Overwrite
public void checkInsideBlocks() {
final AABB boundingBox = this.getBoundingBox();
final int minBlockX = Mth.floor(boundingBox.minX + CollisionUtil.COLLISION_EPSILON);
final int minBlockY = Mth.floor(boundingBox.minY + CollisionUtil.COLLISION_EPSILON);
final int minBlockZ = Mth.floor(boundingBox.minZ + CollisionUtil.COLLISION_EPSILON);
final int maxBlockX = Mth.floor(boundingBox.maxX - CollisionUtil.COLLISION_EPSILON);
final int maxBlockY = Mth.floor(boundingBox.maxY - CollisionUtil.COLLISION_EPSILON);
final int maxBlockZ = Mth.floor(boundingBox.maxZ - CollisionUtil.COLLISION_EPSILON);
final int minChunkX = minBlockX >> 4;
final int minChunkY = minBlockY >> 4;
final int minChunkZ = minBlockZ >> 4;
final int maxChunkX = maxBlockX >> 4;
final int maxChunkY = maxBlockY >> 4;
final int maxChunkZ = maxBlockZ >> 4;
final Level world = this.level;
if (!((ChunkSystemLevel)world).moonrise$areChunksLoaded(minChunkX, minChunkZ, maxChunkX, maxChunkZ)) {
return;
}
final int minSection = WorldUtil.getMinSection(world);
final ChunkSource chunkSource = world.getChunkSource();
final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
final LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false).getSections();
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
final int sectionIdx = currChunkY - minSection;
if (sectionIdx < 0 || sectionIdx >= sections.length) {
continue;
}
final LevelChunkSection section = sections[sectionIdx];
if (section.hasOnlyAir()) {
// empty
continue;
}
final PalettedContainer<BlockState> blocks = section.states;
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
mutablePos.setY(currY | (currChunkY << 4));
for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
mutablePos.setZ(currZ | (currChunkZ << 4));
for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
mutablePos.setX(currX | (currChunkX << 4));
final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));
if (!this.isAlive()) {
return;
}
blockState.entityInside(world, mutablePos, (Entity)(Object)this);
this.onInsideBlock(blockState);
}
}
}
}
}
}
}
}

View File

@@ -1,5 +1,6 @@
package ca.spottedleaf.moonrise.mixin.collisions;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk;
import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
@@ -9,15 +10,20 @@ import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.item.PrimedTnt;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Explosion;
import net.minecraft.world.level.ExplosionDamageCalculator;
import net.minecraft.world.level.ServerExplosion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
@@ -27,20 +33,33 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Mixin(ServerExplosion.class)
abstract class ServerExplosionMixin {
@Mixin(Explosion.class)
abstract class ExplosionMixin {
@Shadow
@Final
private ServerLevel level;
private Level level;
@Shadow
@Final
private Entity source;
@Shadow
@Final
private double x;
@Shadow
@Final
private double y;
@Shadow
@Final
private double z;
@Shadow
@Final
@@ -50,13 +69,21 @@ abstract class ServerExplosionMixin {
@Final
private float radius;
@Shadow
@Final
private ObjectArrayList<BlockPos> toBlow;
@Shadow
@Final
private Map<Player, Vec3> hitPlayers;
@Shadow
@Final
private boolean fire;
@Shadow
@Final
private Vec3 center;
private DamageSource damageSource;
@Unique
@@ -115,12 +142,6 @@ abstract class ServerExplosionMixin {
@Unique
private LevelChunk[] chunkCache = null;
@Unique
private ExplosionBlockCache[] directMappedBlockCache;
@Unique
private BlockPos.MutableBlockPos mutablePos;
@Unique
private ExplosionBlockCache getOrCacheExplosionBlock(final int x, final int y, final int z,
final long key, final boolean calculateResistance) {
@@ -322,45 +343,29 @@ abstract class ServerExplosionMixin {
return (float)missedRays / (float)totalRays;
}
/**
* @reason Init cache fields
* @author Spottedleaf
*/
@Inject(
method = "explode",
at = @At(
value = "HEAD"
)
)
private void initCacheFields(final CallbackInfo ci) {
this.blockCache = new Long2ObjectOpenHashMap<>();
this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
Arrays.fill(this.chunkPosCache, ChunkPos.INVALID_CHUNK_POS);
this.chunkCache = new LevelChunk[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
this.directMappedBlockCache = new ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
this.mutablePos = new BlockPos.MutableBlockPos();
}
/**
* @reason Rewrite ray casting for performance
* @reason Rewrite ray casting and seen fraction calculation for performance
* @author Spottedleaf
*/
@Overwrite
public List<BlockPos> calculateExplodedPositions() {
final ObjectArrayList<BlockPos> ret = new ObjectArrayList<>();
public void explode() {
this.level.gameEvent(this.source, GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z));
final Vec3 center = this.center;
this.blockCache = new Long2ObjectOpenHashMap<>();
final ExplosionBlockCache[] blockCache = this.directMappedBlockCache;
this.chunkPosCache = new long[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
Arrays.fill(this.chunkPosCache, ChunkPos.INVALID_CHUNK_POS);
this.chunkCache = new LevelChunk[CHUNK_CACHE_WIDTH * CHUNK_CACHE_WIDTH];
final ExplosionBlockCache[] blockCache = new ExplosionBlockCache[BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH * BLOCK_EXPLOSION_CACHE_WIDTH];
// use initial cache value that is most likely to be used: the source position
final ExplosionBlockCache initialCache;
{
final int blockX = Mth.floor(center.x);
final int blockY = Mth.floor(center.y);
final int blockZ = Mth.floor(center.z);
final int blockX = Mth.floor(this.x);
final int blockY = Mth.floor(this.y);
final int blockZ = Mth.floor(this.z);
final long key = BlockPos.asLong(blockX, blockY, blockZ);
@@ -376,9 +381,9 @@ abstract class ServerExplosionMixin {
for (int ray = 0, len = CACHED_RAYS.length; ray < len;) {
ExplosionBlockCache cachedBlock = initialCache;
double currX = center.x;
double currY = center.y;
double currZ = center.z;
double currX = this.x;
double currY = this.y;
double currZ = this.z;
final double incX = CACHED_RAYS[ray];
final double incY = CACHED_RAYS[ray + 1];
@@ -397,7 +402,7 @@ abstract class ServerExplosionMixin {
if (cachedBlock.key != key) {
final int cacheKey =
(blockX & BLOCK_EXPLOSION_CACHE_MASK) |
(blockX & BLOCK_EXPLOSION_CACHE_MASK) |
(blockY & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT) |
(blockZ & BLOCK_EXPLOSION_CACHE_MASK) << (BLOCK_EXPLOSION_CACHE_SHIFT + BLOCK_EXPLOSION_CACHE_SHIFT);
cachedBlock = blockCache[cacheKey];
@@ -419,7 +424,7 @@ abstract class ServerExplosionMixin {
cachedBlock.shouldExplode = shouldExplode ? Boolean.TRUE : Boolean.FALSE;
if (shouldExplode) {
if (this.fire || !cachedBlock.blockState.isAir()) {
ret.add(cachedBlock.immutablePos);
this.toBlow.add(cachedBlock.immutablePos);
}
}
}
@@ -431,39 +436,83 @@ abstract class ServerExplosionMixin {
} while (power > 0.0f);
}
return ret;
}
final double diameter = (double)this.radius * 2.0;
final List<Entity> entities = this.level.getEntities(this.source,
new AABB(
(double)Mth.floor(this.x - (diameter + 1.0)),
(double)Mth.floor(this.y - (diameter + 1.0)),
(double)Mth.floor(this.z - (diameter + 1.0)),
/**
* @reason Use optimised getSeenPercent implementation
* @author Spottedleaf
*/
@Redirect(
method = "hurtEntities",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/ServerExplosion;getSeenPercent(Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/entity/Entity;)F"
)
)
private float useBetterSeenPercent(final Vec3 center, final Entity target) {
return this.getSeenFraction(center, target, this.directMappedBlockCache, this.mutablePos);
}
(double)Mth.floor(this.x + (diameter + 1.0)),
(double)Mth.floor(this.y + (diameter + 1.0)),
(double)Mth.floor(this.z + (diameter + 1.0))
)
);
final Vec3 center = new Vec3(this.x, this.y, this.z);
final BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
final PlatformHooks platformHooks = PlatformHooks.get();
platformHooks.onExplosion(this.level, (Explosion)(Object)this, entities, diameter);
for (int i = 0, len = entities.size(); i < len; ++i) {
final Entity entity = entities.get(i);
if (entity.ignoreExplosion((Explosion)(Object)this)) {
continue;
}
final double normalizedDistanceToCenter = Math.sqrt(entity.distanceToSqr(center)) / diameter;
if (normalizedDistanceToCenter > 1.0) {
continue;
}
double distX = entity.getX() - this.x;
double distY = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - this.y;
double distZ = entity.getZ() - this.z;
final double distMag = Math.sqrt(distX * distX + distY * distY + distZ * distZ);
if (distMag == 0.0) {
continue;
}
distX /= distMag;
distY /= distMag;
distZ /= distMag;
// route to new visible fraction calculation, using the existing block cache
final double seenFraction = (double)this.getSeenFraction(center, entity, blockCache, blockPos);
if (this.damageCalculator.shouldDamageEntity((Explosion)(Object)this, entity)) {
// inline getEntityDamageAmount so that we can avoid double calling getSeenPercent, which is the MOST
// expensive part of this loop!!!!
final double factor = (1.0 - normalizedDistanceToCenter) * seenFraction;
entity.hurt(this.damageSource, (float)((factor * factor + factor) / 2.0 * 7.0 * diameter + 1.0));
}
final double intensityFraction = (1.0 - normalizedDistanceToCenter) * seenFraction * (double)this.damageCalculator.getKnockbackMultiplier(entity);
final double knockbackFraction;
if (entity instanceof LivingEntity livingEntity) {
knockbackFraction = intensityFraction * (1.0 - livingEntity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE));
} else {
knockbackFraction = intensityFraction;
}
Vec3 knockback = new Vec3(distX * knockbackFraction, distY * knockbackFraction, distZ * knockbackFraction);
knockback = platformHooks.modifyExplosionKnockback(this.level, (Explosion)(Object)this, entity, knockback);
entity.setDeltaMovement(entity.getDeltaMovement().add(knockback));
if (entity instanceof Player player) {
if (!player.isSpectator() && (!player.isCreative() || !player.getAbilities().flying)) {
this.hitPlayers.put(player, knockback);
}
}
entity.onExplosionHit(this.source);
}
/**
* @reason Destroy cache fields
* @author Spottedleaf
*/
@Inject(
method = "explode",
at = @At(
value = "RETURN"
)
)
private void destroyCacheFields(final CallbackInfo ci) {
this.blockCache = null;
this.chunkPosCache = null;
this.chunkCache = null;
this.directMappedBlockCache = null;
this.mutablePos = null;
}
}

View File

@@ -83,7 +83,7 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
final Vec3 to = clipContext.getTo();
final Vec3 from = clipContext.getFrom();
return BlockHitResult.miss(to, Direction.getApproximateNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
return BlockHitResult.miss(to, Direction.getNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
}
@Unique

View File

@@ -4,7 +4,9 @@ import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil;
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import net.minecraft.client.renderer.block.LiquidBlockRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.ArrayVoxelShape;
import net.minecraft.world.phys.shapes.BooleanOp;
@@ -21,7 +23,12 @@ abstract class LiquidBlockRendererMixin {
* @author Spottedleaf
*/
@Overwrite
private static boolean isFaceOccludedByState(final Direction direction, final float height, final BlockState state) {
private static boolean isFaceOccludedByState(final BlockGetter world, final Direction direction, final float height,
final BlockPos pos, final BlockState state) {
if (!state.canOcclude()) {
return false;
}
// check for created shape is empty
if (height < (float)CollisionUtil.COLLISION_EPSILON) {
return false;
@@ -43,26 +50,25 @@ abstract class LiquidBlockRendererMixin {
} else {
// the extrusion includes the height
heightShape = new ArrayVoxelShape(
Shapes.block().shape,
CollisionUtil.ZERO_ONE,
DoubleArrayList.wrap(new double[] { 0.0, heightDouble }),
CollisionUtil.ZERO_ONE
Shapes.block().shape,
CollisionUtil.ZERO_ONE,
DoubleArrayList.wrap(new double[] { 0.0, heightDouble }),
CollisionUtil.ZERO_ONE
);
}
final VoxelShape occlusionShape = ((CollisionVoxelShape)state.getFaceOcclusionShape(direction.getOpposite()))
.moonrise$getFaceShapeClamped(direction.getOpposite());
final VoxelShape stateShape = ((CollisionVoxelShape)state.getOcclusionShape(world, pos)).moonrise$getFaceShapeClamped(direction.getOpposite());
if (occlusionShape.isEmpty()) {
if (stateShape.isEmpty()) {
// cannot occlude
return false;
}
// fast check for box
if (heightShape == occlusionShape) {
if (heightShape == stateShape) {
return true;
}
return !Shapes.joinIsNotEmpty(heightShape, occlusionShape, BooleanOp.ONLY_FIRST);
return !Shapes.joinIsNotEmpty(heightShape, stateShape, BooleanOp.ONLY_FIRST);
}
}

View File

@@ -0,0 +1,55 @@
package ca.spottedleaf.moonrise.mixin.collisions;
import net.minecraft.world.entity.Attackable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import java.util.List;
@Mixin(LivingEntity.class)
abstract class LivingEntityMixin extends Entity implements Attackable {
@Shadow
protected abstract void doPush(Entity entity);
public LivingEntityMixin(EntityType<?> entityType, Level level) {
super(entityType, level);
}
/**
* @reason Optimise this method
* @author Spottedleaf
*/
@Overwrite
public void pushEntities() {
if (this.level().isClientSide()) {
final List<Player> players = this.level().getEntitiesOfClass(Player.class, this.getBoundingBox(), EntitySelector.pushableBy(this));
for (int i = 0, len = players.size(); i < len; ++i) {
this.doPush(players.get(i));
}
} else {
final List<Entity> nearby = this.level().getEntities(this, this.getBoundingBox(), EntitySelector.pushableBy(this));
// only iterate ONCE
int nonPassengers = 0;
for (int i = 0, len = nearby.size(); i < len; ++i) {
final Entity entity = nearby.get(i);
nonPassengers += (entity.isPassenger() ? 0 : 1);
this.doPush(entity);
}
int maxCramming;
if (nonPassengers != 0 && (maxCramming = this.level().getGameRules().getInt(GameRules.RULE_MAX_ENTITY_CRAMMING)) > 0
&& nonPassengers > (maxCramming - 1) && this.random.nextInt(4) == 0) {
this.hurt(this.damageSources().cramming(), 6.0F);
}
}
}
}

View File

@@ -79,7 +79,8 @@ abstract class ParticleMixin {
final List<VoxelShape> voxels = new ArrayList<>();
final boolean collided = CollisionUtil.getCollisionsForBlocksOrWorldBorder(
world, entity, collisionBox, voxels, boxes,
0, null
0,
null
);
if (!collided) {

View File

@@ -278,6 +278,15 @@ abstract class ShapesMixin {
return ret;
}
/**
* @reason Route to use cache
* @author Spottedleaf
*/
@Overwrite
public static VoxelShape getFaceShape(final VoxelShape shape, final Direction direction) {
return ((CollisionVoxelShape)shape).moonrise$getFaceShapeClamped(direction);
}
@Unique
private static boolean mergedMayOccludeBlock(final VoxelShape shape1, final VoxelShape shape2) {
// if the combined bounds of the two shapes cannot occlude, then neither can the merged

View File

@@ -1,7 +1,9 @@
package ca.spottedleaf.moonrise.mixin.collisions;
import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.shapes.SliceShape;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@@ -20,7 +22,7 @@ abstract class SliceShapeMixin {
value = "RETURN"
)
)
private void initState(final CallbackInfo ci) {
private void initState(final VoxelShape parent, final Direction.Axis forAxis, final int forIndex, final CallbackInfo ci) {
((CollisionVoxelShape)this).moonrise$initCache();
}
}

View File

@@ -687,13 +687,13 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
final AABB singleAABB = this.singleAABBRepresentation;
if (singleAABB != null) {
if (singleAABB.contains(fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) {
return new BlockHitResult(fromBehind, Direction.getApproximateNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
return new BlockHitResult(fromBehind, Direction.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
}
return clip(singleAABB, from, to, offset);
}
if (CollisionUtil.strictlyContains((VoxelShape)(Object)this, fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) {
return new BlockHitResult(fromBehind, Direction.getApproximateNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
return new BlockHitResult(fromBehind, Direction.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
}
return AABB.clip(((VoxelShape)(Object)this).toAabbs(), from, to, offset);

View File

@@ -1,6 +1,5 @@
package ca.spottedleaf.moonrise.mixin.entity_tracker;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.common.util.TickThread;
@@ -145,8 +144,8 @@ abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
*/
@Overwrite
public int getEffectiveRange() {
int range = this.range;
final Entity entity = this.entity;
int range = PlatformHooks.get().modifyEntityTrackingRange(entity, this.range);
if (entity.getPassengers() == ImmutableList.<Entity>of()) {
return this.scaledRange(range);
@@ -155,9 +154,8 @@ abstract class TrackedEntityMixin implements EntityTrackerTrackedEntity {
// note: we change to List
final List<Entity> passengers = (List<Entity>)entity.getIndirectPassengers();
for (int i = 0, len = passengers.size(); i < len; ++i) {
final Entity passenger = passengers.get(i);
// note: max should be branchless
range = Math.max(range, PlatformHooks.get().modifyEntityTrackingRange(passenger, passenger.getType().clientTrackingRange() << 4));
range = Math.max(range, passengers.get(i).getType().clientTrackingRange() << 4);
}
return this.scaledRange(range);

View File

@@ -24,7 +24,7 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
private void updateData(final PalettedContainer.Data<T> data) {
if (data != null) {
((FastPaletteData<T>)(Object)data).moonrise$setPalette(
((FastPalette<T>)data.palette()).moonrise$getRawPalette((FastPaletteData<T>)(Object)data)
((FastPalette<T>)data.palette).moonrise$getRawPalette((FastPaletteData<T>)(Object)data)
);
}
}
@@ -80,7 +80,7 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
@Unique
private T readPaletteSlow(final PalettedContainer.Data<T> data, final int paletteIdx) {
return data.palette().valueFor(paletteIdx);
return data.palette.valueFor(paletteIdx);
}
@Unique
@@ -103,9 +103,9 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
*/
@Overwrite
public T getAndSet(final int index, final T value) {
final int paletteIdx = this.data.palette().idFor(value);
final int paletteIdx = this.data.palette.idFor(value);
final PalettedContainer.Data<T> data = this.data;
final int prev = data.storage().getAndSet(index, paletteIdx);
final int prev = data.storage.getAndSet(index, paletteIdx);
return this.readPalette(data, prev);
}
@@ -116,6 +116,6 @@ abstract class PalettedContainerMixin<T> implements PaletteResize<T>, PalettedCo
@Overwrite
public T get(final int index) {
final PalettedContainer.Data<T> data = this.data;
return this.readPalette(data, data.storage().get(index));
return this.readPalette(data, data.storage.get(index));
}
}

View File

@@ -114,16 +114,16 @@ abstract class FlowingFluidMixin extends Fluid {
private static final int COLLISION_OCCLUSION_CACHE_SIZE = 2048;
@Unique
private static final ThreadLocal<FluidOcclusionCacheKey[]> COLLISION_OCCLUSION_CACHE = ThreadLocal.withInitial(() -> new FluidOcclusionCacheKey[COLLISION_OCCLUSION_CACHE_SIZE]);
private static final FluidOcclusionCacheKey[] COLLISION_OCCLUSION_CACHE = new FluidOcclusionCacheKey[COLLISION_OCCLUSION_CACHE_SIZE];
/**
* @reason Try to avoid going to the cache for simple cases; additionally use better caching strategy
* @author Spottedleaf
*/
@Overwrite
public static boolean canPassThroughWall(final Direction direction, final BlockGetter level,
final BlockPos fromPos, final BlockState fromState,
final BlockPos toPos, final BlockState toState) {
private boolean canPassThroughWall(final Direction direction, final BlockGetter level,
final BlockPos fromPos, final BlockState fromState,
final BlockPos toPos, final BlockState toState) {
if (((CollisionBlockState)fromState).moonrise$emptyCollisionShape() & ((CollisionBlockState)toState).moonrise$emptyCollisionShape()) {
// don't even try to cache simple cases
return true;
@@ -135,7 +135,7 @@ abstract class FlowingFluidMixin extends Fluid {
}
final FluidOcclusionCacheKey[] cache = ((CollisionBlockState)fromState).moonrise$hasCache() & ((CollisionBlockState)toState).moonrise$hasCache() ?
COLLISION_OCCLUSION_CACHE.get() : null;
COLLISION_OCCLUSION_CACHE : null;
final int keyIndex
= (((CollisionBlockState)fromState).moonrise$uniqueId1() ^ ((CollisionBlockState)toState).moonrise$uniqueId2() ^ ((CollisionDirection)(Object)direction).moonrise$uniqueId())

View File

@@ -42,7 +42,7 @@ abstract class FluidStateMixin extends StateHolder<Fluid, FluidState> implements
private BlockState legacyBlock;
@Override
public final void moonrise$initCaches() {
public void moonrise$initCaches() {
this.amount = this.getType().getAmount((FluidState)(Object)this);
this.isEmpty = this.getType().isEmpty();
this.isSource = this.getType().isSource((FluidState)(Object)this);

View File

@@ -29,9 +29,9 @@ abstract class MappedRegistryMixin<T> {
final RegistrationInfo registrationInfo,
final CallbackInfoReturnable<Holder.Reference<T>> cir
) {
if (resourceKey.registryKey() == (Object)Registries.FLUID) {
for (final FluidState possibleState : ((Fluid)object).getStateDefinition().getPossibleStates()) {
((FluidFluidState)(Object)possibleState).moonrise$initCaches();
if (resourceKey.registryKey() == (Object) Registries.FLUID) {
for (final FluidState possibleState : ((Fluid) object).getStateDefinition().getPossibleStates()) {
((FluidFluidState) (Object) possibleState).moonrise$initCaches();
}
}
}

View File

@@ -16,20 +16,20 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = Level.class, priority = 1100)
abstract class LevelMixin implements LevelAccessor, AutoCloseable {
@Unique
private int minY;
@Unique
private int height;
@Unique
private int maxY;
private int minBuildHeight;
@Unique
private int minSectionY;
private int maxBuildHeight;
@Unique
private int maxSectionY;
private int minSection;
@Unique
private int maxSection;
@Unique
private int sectionsCount;
@@ -47,17 +47,12 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
private void init(final CallbackInfo ci,
@Local(ordinal = 0, argsOnly = true) final Holder<DimensionType> dimensionTypeHolder) {
final DimensionType dimType = dimensionTypeHolder.value();
this.minY = dimType.minY();
this.height = dimType.height();
this.maxY = this.minY + this.height - 1;
this.minSectionY = this.minY >> 4;
this.maxSectionY = this.maxY >> 4;
this.sectionsCount = this.maxSectionY - this.minSectionY + 1;
}
@Override
public int getMinY() {
return this.minY;
this.minBuildHeight = dimType.minY();
this.maxBuildHeight = this.minBuildHeight + this.height;
this.minSection = this.minBuildHeight >> 4;
this.maxSection = ((this.maxBuildHeight - 1) >> 4) + 1;
this.sectionsCount = this.maxSection - this.minSection;
}
@Override
@@ -66,52 +61,52 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
}
@Override
public int getMaxY() {
return this.maxY;
public int getMinBuildHeight() {
return this.minBuildHeight;
}
@Override
public int getMaxBuildHeight() {
return this.maxBuildHeight;
}
@Override
public int getMinSection() {
return this.minSection;
}
@Override
public int getMaxSection() {
return this.maxSection;
}
@Override
public boolean isOutsideBuildHeight(final int y) {
return y < this.minBuildHeight || y >= this.maxBuildHeight;
}
@Override
public boolean isOutsideBuildHeight(final BlockPos blockPos) {
return this.isOutsideBuildHeight(blockPos.getY());
}
@Override
public int getSectionIndex(final int blockY) {
return (blockY >> 4) - this.minSection;
}
@Override
public int getSectionIndexFromSectionY(final int sectionY) {
return sectionY - this.minSection;
}
@Override
public int getSectionYFromSectionIndex(final int sectionIdx) {
return sectionIdx + this.minSection;
}
@Override
public int getSectionsCount() {
return this.sectionsCount;
}
@Override
public int getMinSectionY() {
return this.minSectionY;
}
@Override
public int getMaxSectionY() {
return this.maxSectionY;
}
@Override
public boolean isInsideBuildHeight(final int blockY) {
return blockY >= this.minY && blockY <= this.maxY;
}
@Override
public boolean isOutsideBuildHeight(final BlockPos pos) {
return this.isOutsideBuildHeight(pos.getY());
}
@Override
public boolean isOutsideBuildHeight(final int blockY) {
return blockY < this.minY || blockY > this.maxY;
}
@Override
public int getSectionIndex(final int blockY) {
return (blockY >> 4) - this.minSectionY;
}
@Override
public int getSectionIndexFromSectionY(final int sectionY) {
return sectionY - this.minSectionY;
}
@Override
public int getSectionYFromSectionIndex(final int sectionIdx) {
return sectionIdx + this.minSectionY;
}
}

View File

@@ -20,17 +20,16 @@ import org.spongepowered.asm.mixin.Overwrite;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
@Mixin(PoiManager.class)
abstract class PoiManagerMixin extends SectionStorage<PoiSection, PoiSection.Packed> {
abstract class PoiManagerMixin extends SectionStorage<PoiSection> {
public PoiManagerMixin(final SimpleRegionStorage simpleRegionStorage, final Codec<PoiSection.Packed> codec, final Function<PoiSection, PoiSection.Packed> function, final BiFunction<PoiSection.Packed, Runnable, PoiSection> biFunction, final Function<Runnable, PoiSection> function2, final RegistryAccess registryAccess, final ChunkIOErrorReporter chunkIOErrorReporter, final LevelHeightAccessor levelHeightAccessor) {
super(simpleRegionStorage, codec, function, biFunction, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
public PoiManagerMixin(SimpleRegionStorage simpleRegionStorage, Function<Runnable, Codec<PoiSection>> function, Function<Runnable, PoiSection> function2, RegistryAccess registryAccess, ChunkIOErrorReporter chunkIOErrorReporter, LevelHeightAccessor levelHeightAccessor) {
super(simpleRegionStorage, function, function2, registryAccess, chunkIOErrorReporter, levelHeightAccessor);
}
/**

View File

@@ -49,7 +49,7 @@ abstract class MinecraftMixin extends ReentrantBlockableEventLoop<Runnable> impl
return;
}
cir.setReturnValue(ret == null || ret == InactiveProfiler.INSTANCE ? this.leafProfiler : ProfilerFiller.combine(this.leafProfiler, ret));
cir.setReturnValue(ret == null || ret == InactiveProfiler.INSTANCE ? this.leafProfiler : ProfilerFiller.tee(this.leafProfiler, ret));
}
/**

View File

@@ -10,14 +10,14 @@ import org.spongepowered.asm.mixin.Shadow;
abstract class BiomeMixin {
@Shadow
protected abstract float getHeightAdjustedTemperature(BlockPos blockPos, int seaLevel);
protected abstract float getHeightAdjustedTemperature(BlockPos blockPos);
/**
* @reason Cache appears ineffective
* @author Spottedleaf
*/
@Overwrite
public float getTemperature(final BlockPos pos, final int seaLevel) {
return this.getHeightAdjustedTemperature(pos, seaLevel);
public float getTemperature(final BlockPos pos) {
return this.getHeightAdjustedTemperature(pos);
}
}

View File

@@ -12,6 +12,7 @@ import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.WorldGenLevel;
@@ -26,12 +27,13 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.function.Supplier;
@Mixin(ServerLevel.class)
abstract class ServerLevelMixin extends Level implements WorldGenLevel {
protected ServerLevelMixin(final WritableLevelData writableLevelData, final ResourceKey<Level> resourceKey, final RegistryAccess registryAccess, final Holder<DimensionType> holder, final boolean bl, final boolean bl2, final long l, final int i) {
super(writableLevelData, resourceKey, registryAccess, holder, bl, bl2, l, i);
protected ServerLevelMixin(WritableLevelData writableLevelData, ResourceKey<Level> resourceKey, RegistryAccess registryAccess, Holder<DimensionType> holder, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
super(writableLevelData, resourceKey, registryAccess, holder, supplier, bl, bl2, l, i);
}
@Unique

View File

@@ -10,7 +10,7 @@ abstract class ServerSelectionListMixin {
/**
* @reason Massively increase the threadpool count so that slow servers do not stall the pinging of other servers
* on the status list
* on the status list
* @author Spottedleaf
*/
@ModifyConstant(

View File

@@ -19,10 +19,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(BlockBehaviour.BlockStateBase.class)
abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implements StarlightAbstractBlockState {
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> reference2ObjectArrayMap, MapCodec<BlockState> mapCodec) {
super(object, reference2ObjectArrayMap, mapCodec);
}
@Shadow
@Final
private boolean useShapeForLightOcclusion;
@@ -31,26 +27,29 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
@Final
private boolean canOcclude;
@Shadow
protected BlockBehaviour.BlockStateBase.Cache cache;
@Unique
private boolean isConditionallyFullOpaque;
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> reference2ObjectArrayMap, MapCodec<BlockState> mapCodec) {
super(object, reference2ObjectArrayMap, mapCodec);
}
/**
* Initialises our light state for this block.
*/
@Inject(
method = "initCache",
at = @At("RETURN")
)
public void initLightAccessState(final CallbackInfo ci) {
this.isConditionallyFullOpaque = this.canOcclude & this.useShapeForLightOcclusion;
}
@Override
public final boolean starlight$isConditionallyFullOpaque() {
return this.isConditionallyFullOpaque;
}
/**
* @reason Initialises our light state for this block.
* @author Spottedleaf
*/
@Inject(
method = "initCache",
at = @At(
value = "RETURN"
)
)
public void initLightAccessState(final CallbackInfo ci) {
this.isConditionallyFullOpaque = this.canOcclude & this.useShapeForLightOcclusion;
}
}

View File

@@ -2,16 +2,11 @@ package ca.spottedleaf.moonrise.mixin.starlight.chunk;
import ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk;
import ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray;
import net.minecraft.core.Registry;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.levelgen.blending.BlendingData;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -23,8 +18,8 @@ abstract class ImposterProtoChunkMixin extends ProtoChunk implements StarlightCh
@Shadow
private LevelChunk wrapped;
public ImposterProtoChunkMixin(final ChunkPos chunkPos, final UpgradeData upgradeData, final LevelHeightAccessor levelHeightAccessor, final Registry<Biome> registry, @Nullable final BlendingData blendingData) {
super(chunkPos, upgradeData, levelHeightAccessor, registry, blendingData);
public ImposterProtoChunkMixin(final LevelChunk levelChunk, final boolean bl) {
super(levelChunk.getPos(), UpgradeData.EMPTY, levelChunk, levelChunk.getLevel().registryAccess().registryOrThrow(Registries.BIOME), levelChunk.getBlendingData());
}
@Override

View File

@@ -54,24 +54,7 @@ abstract class LevelLightEngineMixin implements LightEventListener, StarLightLig
* TODO since this is a constructor inject, check on update for new constructors
*/
@Inject(
method = "<init>()V",
at = @At(
value = "TAIL"
)
)
public void constructEmpty(final CallbackInfo ci) {
this.lightEngine = new StarLightInterface(null, false, false, (LevelLightEngine)(Object)this);
}
/**
*
* TODO since this is a constructor inject, check on update for new constructors
*/
@Inject(
method = "<init>(Lnet/minecraft/world/level/chunk/LightChunkGetter;ZZ)V",
at = @At(
value = "TAIL"
)
method = "<init>", at = @At("TAIL")
)
public void construct(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight,
final CallbackInfo ci) {
@@ -196,10 +179,8 @@ abstract class LevelLightEngineMixin implements LightEventListener, StarLightLig
* @author Spottedleaf
*/
@Overwrite
public boolean lightOnInColumn(final long pos) {
final long key = CoordinateUtils.getChunkKey(
CoordinateUtils.getChunkSectionX(pos), CoordinateUtils.getChunkSectionZ(pos)
);
public boolean lightOnInSection(final SectionPos pos) {
final long key = CoordinateUtils.getChunkKey(pos.getX(), pos.getZ());
return (!this.lightEngine.hasBlockLight() || this.blockLightMap.get(key) != null) && (!this.lightEngine.hasSkyLight() || this.skyLightMap.get(key) != null);
}

View File

@@ -10,12 +10,13 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
import net.minecraft.server.level.ChunkTaskDispatcher;
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.util.thread.ConsecutiveExecutor;
import net.minecraft.util.thread.ProcessorHandle;
import net.minecraft.util.thread.ProcessorMailbox;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess;
@@ -49,10 +50,10 @@ import java.util.function.Supplier;
abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements StarLightLightingProvider {
@Shadow
private ConsecutiveExecutor consecutiveExecutor;
private ProcessorMailbox<Runnable> taskMailbox;
@Shadow
private ChunkTaskDispatcher taskDispatcher;
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> sorterMailbox;
public ThreadedLevelLightEngineMixin(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) {
super(chunkProvider, hasBlockLight, hasSkyLight);
@@ -183,8 +184,8 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
)
)
private void initHook(final CallbackInfo ci) {
this.consecutiveExecutor = null;
this.taskDispatcher = null;
this.taskMailbox = null;
this.sorterMailbox = null;
}
/**
@@ -193,7 +194,7 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
*/
@Overwrite
public void addTask(final int x, final int z, final ThreadedLevelLightEngine.TaskType type,
final Runnable task) {
final Runnable task) {
throw new UnsupportedOperationException();
}

View File

@@ -40,8 +40,7 @@ abstract class ClientPacketListenerMixin implements ClientGamePacketListener {
*/
@Shadow
protected abstract void applyLightData(final int chunkX, final int chunkZ, final ClientboundLightUpdatePacketData clientboundLightUpdatePacketData,
final boolean markDirty);
protected abstract void applyLightData(final int chunkX, final int chunkZ, final ClientboundLightUpdatePacketData clientboundLightUpdatePacketData);
@Shadow
protected abstract void enableChunkLight(final LevelChunk levelChunk, final int chunkX, final int chunkZ);
@@ -128,7 +127,7 @@ abstract class ClientPacketListenerMixin implements ClientGamePacketListener {
return;
}
// load in light data from packet immediately
this.applyLightData(chunkX, chunkZ, clientboundLevelChunkWithLightPacket.getLightData(), false);
this.applyLightData(chunkX, chunkZ, clientboundLevelChunkWithLightPacket.getLightData());
((StarLightLightingProvider)this.level.getChunkSource().getLightEngine()).starlight$clientChunkLoad(new ChunkPos(chunkX, chunkZ), chunk);
// we need this for the update chunk status call, so that it can tell starlight what sections are empty and such

View File

@@ -0,0 +1,45 @@
package ca.spottedleaf.moonrise.mixin.starlight.world;
import ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ChunkSerializer.class)
abstract class ChunkSerializerMixin {
/**
* Overwrites vanilla's light data with our own.
* TODO this needs to be checked on update to account for format changes
*/
@Inject(
method = "write",
at = @At("RETURN")
)
private static void saveLightHook(final ServerLevel world, final ChunkAccess chunk, final CallbackInfoReturnable<CompoundTag> cir) {
SaveUtil.saveLightHook(world, chunk, cir.getReturnValue());
}
/**
* Loads our light data into the returned chunk object from the tag.
* TODO this needs to be checked on update to account for format changes
*/
@Inject(
method = "read",
at = @At("RETURN")
)
private static void loadLightHook(final ServerLevel serverLevel, final PoiManager poiManager,
final RegionStorageInfo regionStorageInfo, final ChunkPos chunkPos,
final CompoundTag compoundTag, final CallbackInfoReturnable<ProtoChunk> cir) {
SaveUtil.loadLightHook(serverLevel, chunkPos, compoundTag, cir.getReturnValue());
}
}

View File

@@ -1,36 +0,0 @@
package ca.spottedleaf.moonrise.mixin.starlight.world;
import ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(SerializableChunkData.SectionData.class)
abstract class SerializableChunkData$SectionData implements StarlightSectionData {
@Unique
private int blockLightState = -1;
@Unique
private int skyLightState = -1;
@Override
public final int starlight$getBlockLightState() {
return this.blockLightState;
}
@Override
public final void starlight$setBlockLightState(final int state) {
this.blockLightState = state;
}
@Override
public final int starlight$getSkyLightState() {
return this.skyLightState;
}
@Override
public final void starlight$setSkyLightState(final int state) {
this.skyLightState = state;
}
}

View File

@@ -1,255 +0,0 @@
package ca.spottedleaf.moonrise.mixin.starlight.world;
import ca.spottedleaf.moonrise.common.util.MixinWorkarounds;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk;
import ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray;
import ca.spottedleaf.moonrise.patches.starlight.light.StarLightEngine;
import ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData;
import ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.ai.village.poi.PoiManager;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.DataLayer;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.lighting.LevelLightEngine;
import org.spongepowered.asm.mixin.Final;
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.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
// note: keep in-sync with SaveUtil
@Mixin(SerializableChunkData.class)
abstract class SerializableChunkDataMixin {
@Shadow
@Final
private ChunkStatus chunkStatus;
@Shadow
@Final
private boolean lightCorrect;
@Shadow
@Final
private List<SerializableChunkData.SectionData> sectionData;
/**
* @reason Replace light correctness check with our own
* Our light check is versioned in case we change the light format OR fix a bug
* @author Spottedleaf
*/
@Redirect(
method = "parse",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/nbt/CompoundTag;getBoolean(Ljava/lang/String;)Z",
ordinal = 0
)
)
private static boolean setLightCorrect(final CompoundTag instance, final String string,
@Local(ordinal = 0, argsOnly = false) final ChunkStatus status) {
final boolean starlightCorrect = instance.get("isLightOn") != null && instance.getInt(SaveUtil.STARLIGHT_VERSION_TAG) == SaveUtil.STARLIGHT_LIGHT_VERSION;
return status.isOrAfter(ChunkStatus.LIGHT) && starlightCorrect;
}
/**
* @reason Add starlight block/sky state to SectionData
* @author Spottedleaf
*/
@Redirect(
method = "parse",
at = @At(
value = "NEW",
target = "(ILnet/minecraft/world/level/chunk/LevelChunkSection;Lnet/minecraft/world/level/chunk/DataLayer;Lnet/minecraft/world/level/chunk/DataLayer;)Lnet/minecraft/world/level/chunk/storage/SerializableChunkData$SectionData;",
ordinal = 0
)
)
private static SerializableChunkData.SectionData readStarlightState(final int y, final LevelChunkSection chunkSection,
final DataLayer blockLight, final DataLayer skyLight,
@Local(ordinal = 3, argsOnly = false) final CompoundTag sectionData) {
final SerializableChunkData.SectionData ret = new SerializableChunkData.SectionData(
y, chunkSection, blockLight, skyLight
);
if (sectionData.contains(SaveUtil.BLOCKLIGHT_STATE_TAG, Tag.TAG_ANY_NUMERIC)) {
((StarlightSectionData)(Object)ret).starlight$setBlockLightState(sectionData.getInt(SaveUtil.BLOCKLIGHT_STATE_TAG));
}
if (sectionData.contains(SaveUtil.SKYLIGHT_STATE_TAG, Tag.TAG_ANY_NUMERIC)) {
((StarlightSectionData)(Object)ret).starlight$setSkyLightState(sectionData.getInt(SaveUtil.SKYLIGHT_STATE_TAG));
}
return ret;
}
/**
* @reason Load light data from the section data and store them in the returned value's SWMRNibbleArrays
* @author Spottedleaf
*/
@Inject(
method = "read",
at = @At(
value = "RETURN"
)
)
private void loadStarlightLightData(final ServerLevel world, final PoiManager poiManager,
final RegionStorageInfo regionStorageInfo, final ChunkPos pos,
final CallbackInfoReturnable<ProtoChunk> cir) {
final ProtoChunk ret = cir.getReturnValue();
final boolean hasSkyLight = world.dimensionType().hasSkyLight();
final int minSection = WorldUtil.getMinLightSection(world);
final SWMRNibbleArray[] blockNibbles = StarLightEngine.getFilledEmptyLight(world);
final SWMRNibbleArray[] skyNibbles = StarLightEngine.getFilledEmptyLight(world);
for (final SerializableChunkData.SectionData sectionData : this.sectionData) {
final int y = sectionData.y();
final DataLayer blockLight = sectionData.blockLight();
final DataLayer skyLight = sectionData.skyLight();
final int blockState = ((StarlightSectionData)(Object)sectionData).starlight$getBlockLightState();
final int skyState = ((StarlightSectionData)(Object)sectionData).starlight$getSkyLightState();
if (blockState >= 0) {
if (blockLight != null) {
blockNibbles[y - minSection] = new SWMRNibbleArray(MixinWorkarounds.clone(blockLight.getData()), blockState); // clone for data safety
} else {
blockNibbles[y - minSection] = new SWMRNibbleArray(null, blockState);
}
}
if (skyState >= 0 && hasSkyLight) {
if (skyLight != null) {
skyNibbles[y - minSection] = new SWMRNibbleArray(MixinWorkarounds.clone(skyLight.getData()), skyState); // clone for data safety
} else {
skyNibbles[y - minSection] = new SWMRNibbleArray(null, skyState);
}
}
}
((StarlightChunk)ret).starlight$setBlockNibbles(blockNibbles);
((StarlightChunk)ret).starlight$setSkyNibbles(skyNibbles);
}
/**
* @reason Rewrite the section copying so that we can store Starlight's data
* @author Spottedleaf
*/
@Redirect(
method = "copyOf",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;getMinLightSection()I",
ordinal = 0
)
)
private static int rewriteSectionCopy(final LevelLightEngine instance,
@Local(ordinal = 0, argsOnly = true) final ServerLevel world,
@Local(ordinal = 0, argsOnly = true) final ChunkAccess chunk,
@Local(ordinal = 0, argsOnly = false) final List<SerializableChunkData.SectionData> sections) {
final int minLightSection = WorldUtil.getMinLightSection(world);
final int maxLightSection = WorldUtil.getMaxLightSection(world);
final int minBlockSection = WorldUtil.getMinSection(world);
final LevelChunkSection[] chunkSections = chunk.getSections();
final SWMRNibbleArray[] blockNibbles = ((StarlightChunk)chunk).starlight$getBlockNibbles();
final SWMRNibbleArray[] skyNibbles = ((StarlightChunk)chunk).starlight$getSkyNibbles();
for (int lightSection = minLightSection; lightSection <= maxLightSection; ++lightSection) {
final int lightSectionIdx = lightSection - minLightSection;
final int blockSectionIdx = lightSection - minBlockSection;
final LevelChunkSection chunkSection = (blockSectionIdx >= 0 && blockSectionIdx < chunkSections.length) ? chunkSections[blockSectionIdx].copy() : null;
final SWMRNibbleArray.SaveState blockNibble = blockNibbles[lightSectionIdx].getSaveState();
final SWMRNibbleArray.SaveState skyNibble = skyNibbles[lightSectionIdx].getSaveState();
if (chunkSection == null && blockNibble == null && skyNibble == null) {
continue;
}
final SerializableChunkData.SectionData sectionData = new SerializableChunkData.SectionData(
lightSection, chunkSection,
blockNibble == null ? null : (blockNibble.data == null ? null : new DataLayer(blockNibble.data)),
skyNibble == null ? null : (skyNibble.data == null ? null : new DataLayer(skyNibble.data))
);
if (blockNibble != null) {
((StarlightSectionData)(Object)sectionData).starlight$setBlockLightState(blockNibble.state);
}
if (skyNibble != null) {
((StarlightSectionData)(Object)sectionData).starlight$setSkyLightState(skyNibble.state);
}
sections.add(sectionData);
}
// force the Vanilla loop to never run
return Integer.MAX_VALUE;
}
/**
* @reason Store the per-section block/sky state from Starlight
* @author Spottedleaf
*/
@Inject(
method = "write",
at = @At(
value = "FIELD",
target = "Lnet/minecraft/world/level/chunk/storage/SerializableChunkData$SectionData;chunkSection:Lnet/minecraft/world/level/chunk/LevelChunkSection;",
ordinal = 0
)
)
private void storeStarlightState(final CallbackInfoReturnable<CompoundTag> cir,
@Local(ordinal = 0, argsOnly = false) final SerializableChunkData.SectionData sectionData,
@Local(ordinal = 1, argsOnly = false) final CompoundTag sectionNBT) {
final int blockState = ((StarlightSectionData)(Object)sectionData).starlight$getBlockLightState();
final int skyState = ((StarlightSectionData)(Object)sectionData).starlight$getSkyLightState();
if (blockState > 0) {
sectionNBT.putInt(SaveUtil.BLOCKLIGHT_STATE_TAG, blockState);
}
if (skyState > 0) {
sectionNBT.putInt(SaveUtil.SKYLIGHT_STATE_TAG, skyState);
}
}
/**
* @reason Store Starlight's light version
* @author Spottedleaf
*/
@Inject(
method = "write",
at = @At(
value = "RETURN"
)
)
private void writeStarlightCorrectLight(final CallbackInfoReturnable<CompoundTag> cir) {
if (this.chunkStatus.isBefore(ChunkStatus.LIGHT) || !this.lightCorrect) {
return;
}
final CompoundTag ret = cir.getReturnValue();
// clobber vanilla value to force vanilla to relight
ret.putBoolean("isLightOn", false);
// store our light version
ret.putInt(SaveUtil.STARLIGHT_VERSION_TAG, SaveUtil.STARLIGHT_LIGHT_VERSION);
}
}

View File

@@ -1,11 +1,10 @@
package ca.spottedleaf.moonrise.patches.chunk_system;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import net.minecraft.SharedConstants;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.datafix.fixes.References;
import net.minecraft.util.datafix.DataFixTypes;
public final class ChunkSystemConverters {
@@ -26,13 +25,13 @@ public final class ChunkSystemConverters {
public static CompoundTag convertPoiCompoundTag(final CompoundTag data, final ServerLevel world) {
final int dataVersion = getDataVersion(data, DEFAULT_POI_DATA_VERSION);
return PlatformHooks.get().convertNBT(References.POI_CHUNK, world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
return DataFixTypes.POI_CHUNK.update(world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
}
public static CompoundTag convertEntityChunkCompoundTag(final CompoundTag data, final ServerLevel world) {
final int dataVersion = getDataVersion(data, DEFAULT_ENTITY_CHUNK_DATA_VERSION);
return PlatformHooks.get().convertNBT(References.ENTITY_CHUNK, world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
return DataFixTypes.ENTITY_CHUNK.update(world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
}
private ChunkSystemConverters() {}

View File

@@ -0,0 +1,36 @@
package ca.spottedleaf.moonrise.patches.chunk_system;
import ca.spottedleaf.moonrise.patches.chunk_system.async_save.AsyncChunkSaveData;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
public final class ChunkSystemFeatures {
public static boolean supportsAsyncChunkSave() {
// uncertain how to properly pass AsyncSaveData to ChunkSerializer#write
// additionally, there may be mods hooking into the write() call which may not be thread-safe to call
return false;
}
public static AsyncChunkSaveData getAsyncSaveData(final ServerLevel world, final ChunkAccess chunk) {
throw new UnsupportedOperationException();
}
public static CompoundTag saveChunkAsync(final ServerLevel world, final ChunkAccess chunk, final AsyncChunkSaveData asyncSaveData) {
throw new UnsupportedOperationException();
}
public static boolean forceNoSave(final ChunkAccess chunk) {
// support for CB chunk mustNotSave
return false;
}
public static boolean supportsAsyncChunkDeserialization() {
// as it stands, the current problem with supporting this in Moonrise is that we are unsure that any mods
// hooking into ChunkSerializer#read() are thread-safe to call
return false;
}
private ChunkSystemFeatures() {}
}

View File

@@ -0,0 +1,11 @@
package ca.spottedleaf.moonrise.patches.chunk_system.async_save;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
public record AsyncChunkSaveData(
Tag blockTickList, // non-null if we had to go to the server's tick list
Tag fluidTickList, // non-null if we had to go to the server's tick list
ListTag blockEntities,
long worldTime
) {}

View File

@@ -1,8 +1,6 @@
package ca.spottedleaf.moonrise.patches.chunk_system.io;
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
import ca.spottedleaf.concurrentutil.completable.CallbackCompletable;
import ca.spottedleaf.concurrentutil.completable.Completable;
import ca.spottedleaf.concurrentutil.executor.Cancellable;
import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
import ca.spottedleaf.concurrentutil.executor.queue.PrioritisedTaskQueue;
@@ -14,7 +12,6 @@ import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
@@ -26,12 +23,14 @@ import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
public final class MoonriseRegionFileIO {
@@ -216,6 +215,30 @@ public final class MoonriseRegionFileIO {
return Priority.HIGHEST;
}
/**
* Returns the current {@code CompoundTag} pending for write for the specified chunk & regionfile type.
* Note that this does not copy the result, so do not modify the result returned.
*
* @param world Specified world.
* @param chunkX Specified chunk x.
* @param chunkZ Specified chunk z.
* @param type Specified regionfile type.
*
* @return The compound tag associated for the specified chunk. {@code null} if no write was pending, or if {@code null} is the write pending.
*/
public static CompoundTag getPendingWrite(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
final RegionDataController taskController = getControllerFor(world, type);
final ChunkIOTask task = taskController.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
if (task == null) {
return null;
}
final CompoundTag ret = task.inProgressWrite;
return ret == ChunkIOTask.NOTHING_TO_WRITE ? null : ret;
}
/**
* Returns the priority for the specified regionfile type for the specified chunk.
* @param world Specified world.
@@ -421,106 +444,27 @@ public final class MoonriseRegionFileIO {
*/
public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
final RegionFileType type, final Priority priority) {
scheduleSave(
world, chunkX, chunkZ,
(final BiConsumer<CompoundTag, Throwable> consumer) -> {
consumer.accept(data, null);
}, null, type, priority
);
}
/**
* Schedules the chunk data to be written asynchronously.
* <p>
* Impl notes:
* </p>
* <li>
* This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
* saves must be scheduled before a chunk is unloaded.
* </li>
* <li>
* Writes may be called concurrently, although only the "later" write will go through.
* </li>
* <li>
* The specified write task, if not null, will have its priority controlled by the scheduler.
* </li>
*
* @param world Chunk's world
* @param chunkX Chunk's x coordinate
* @param chunkZ Chunk's z coordinate
* @param completable Chunk's pending data
* @param writeTask The task responsible for completing the pending chunk data
* @param type The regionfile type to write to.
* @param priority The minimum priority to schedule at.
*
* @throws IllegalStateException If the file io thread has shutdown.
*/
public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CallbackCompletable<CompoundTag> completable,
final PrioritisedExecutor.PrioritisedTask writeTask, final RegionFileType type, final Priority priority) {
scheduleSave(world, chunkX, chunkZ, completable::addWaiter, writeTask, type, priority);
}
/**
* Schedules the chunk data to be written asynchronously.
* <p>
* Impl notes:
* </p>
* <li>
* This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
* saves must be scheduled before a chunk is unloaded.
* </li>
* <li>
* Writes may be called concurrently, although only the "later" write will go through.
* </li>
* <li>
* The specified write task, if not null, will have its priority controlled by the scheduler.
* </li>
*
* @param world Chunk's world
* @param chunkX Chunk's x coordinate
* @param chunkZ Chunk's z coordinate
* @param completable Chunk's pending data
* @param writeTask The task responsible for completing the pending chunk data
* @param type The regionfile type to write to.
* @param priority The minimum priority to schedule at.
*
* @throws IllegalStateException If the file io thread has shutdown.
*/
public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final Completable<CompoundTag> completable,
final PrioritisedExecutor.PrioritisedTask writeTask, final RegionFileType type, final Priority priority) {
scheduleSave(world, chunkX, chunkZ, completable::whenComplete, writeTask, type, priority);
}
private static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final Consumer<BiConsumer<CompoundTag, Throwable>> scheduler,
final PrioritisedExecutor.PrioritisedTask writeTask, final RegionFileType type, final Priority priority) {
final RegionDataController taskController = getControllerFor(world, type);
final boolean[] created = new boolean[1];
final ChunkIOTask.InProgressWrite write = new ChunkIOTask.InProgressWrite(writeTask);
final ChunkIOTask task = taskController.chunkTasks.compute(CoordinateUtils.getChunkKey(chunkX, chunkZ),
(final long keyInMap, final ChunkIOTask taskRunning) -> {
if (taskRunning == null || taskRunning.failedWrite) {
// no task is scheduled or the previous write failed - meaning we need to overwrite it
final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ);
final ChunkIOTask task = taskController.chunkTasks.compute(key, (final long keyInMap, final ChunkIOTask taskRunning) -> {
if (taskRunning == null || taskRunning.failedWrite) {
// no task is scheduled or the previous write failed - meaning we need to overwrite it
// create task
final ChunkIOTask newTask = new ChunkIOTask(
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead()
);
// create task
final ChunkIOTask newTask = new ChunkIOTask(
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead(), data
);
created[0] = true;
newTask.pushPendingWrite(write);
created[0] = true;
return newTask;
}
taskRunning.pushPendingWrite(write);
return taskRunning;
return newTask;
}
);
write.schedule(task, scheduler);
taskRunning.inProgressWrite = data;
return taskRunning;
});
if (created[0]) {
taskController.startTask(task);
@@ -767,7 +711,7 @@ public final class MoonriseRegionFileIO {
// set up task
final ChunkIOTask newTask = new ChunkIOTask(
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead()
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead(), ChunkIOTask.NOTHING_TO_WRITE
);
newTask.inProgressRead.addToAsyncWaiters(onComplete);
@@ -775,34 +719,22 @@ public final class MoonriseRegionFileIO {
return newTask;
}
final ChunkIOTask.InProgressWrite pendingWrite = running.inProgressWrite;
final CompoundTag pendingWrite = running.inProgressWrite;
if (pendingWrite == null) {
if (pendingWrite == ChunkIOTask.NOTHING_TO_WRITE) {
// need to add to waiters here, because the regionfile thread will use compute() to lock and check for cancellations
if (!running.inProgressRead.addToAsyncWaiters(onComplete)) {
callbackInfo.data = running.inProgressRead.value;
callbackInfo.throwable = running.inProgressRead.throwable;
callbackInfo.completeNow = true;
return running;
}
callbackInfo.read = running.inProgressRead;
return running;
}
// at this stage we have to use the in progress write's data to avoid an order issue
if (!pendingWrite.addToAsyncWaiters(onComplete)) {
// data is ready now
callbackInfo.data = pendingWrite.value;
callbackInfo.throwable = pendingWrite.throwable;
callbackInfo.completeNow = true;
return running;
}
callbackInfo.write = pendingWrite;
callbackInfo.data = pendingWrite;
callbackInfo.throwable = null;
callbackInfo.completeNow = true;
return running;
};
@@ -823,7 +755,7 @@ public final class MoonriseRegionFileIO {
ret.raisePriority(priority);
}
return new CancellableRead(onComplete, callbackInfo.read, callbackInfo.write);
return new CancellableRead(onComplete, ret);
}
private static final class ImmediateCallbackCompletion {
@@ -832,8 +764,6 @@ public final class MoonriseRegionFileIO {
private Throwable throwable;
private boolean completeNow;
private boolean tasksNeedReadScheduling;
private ChunkIOTask.InProgressRead read;
private ChunkIOTask.InProgressWrite write;
}
@@ -872,40 +802,26 @@ public final class MoonriseRegionFileIO {
private static final class CancellableRead implements Cancellable {
private BiConsumer<CompoundTag, Throwable> callback;
private ChunkIOTask.InProgressRead read;
private ChunkIOTask.InProgressWrite write;
private ChunkIOTask task;
private CancellableRead(final BiConsumer<CompoundTag, Throwable> callback,
final ChunkIOTask.InProgressRead read,
final ChunkIOTask.InProgressWrite write) {
private CancellableRead(final BiConsumer<CompoundTag, Throwable> callback, final ChunkIOTask task) {
this.callback = callback;
this.read = read;
this.write = write;
this.task = task;
}
@Override
public boolean cancel() {
final BiConsumer<CompoundTag, Throwable> callback = this.callback;
final ChunkIOTask.InProgressRead read = this.read;
final ChunkIOTask.InProgressWrite write = this.write;
final ChunkIOTask task = this.task;
if (callback == null || (read == null && write == null)) {
if (callback == null || task == null) {
return false;
}
this.callback = null;
this.read = null;
this.write = null;
if (read != null) {
return read.cancel(callback);
}
if (write != null) {
return write.cancel(callback);
}
// unreachable
throw new InternalError();
this.task = null;
return task.inProgressRead.cancel(callback);
}
}
@@ -938,6 +854,8 @@ public final class MoonriseRegionFileIO {
private static final class ChunkIOTask {
private static final CompoundTag NOTHING_TO_WRITE = new CompoundTag();
private final ServerLevel world;
private final RegionDataController regionDataController;
private final int chunkX;
@@ -946,21 +864,22 @@ public final class MoonriseRegionFileIO {
private PrioritisedExecutor.PrioritisedTask currentTask;
private final InProgressRead inProgressRead;
private volatile InProgressWrite inProgressWrite;
private final ReferenceOpenHashSet<InProgressWrite> allPendingWrites = new ReferenceOpenHashSet<>();
private volatile CompoundTag inProgressWrite;
private RegionDataController.ReadData readData;
private RegionDataController.WriteData writeData;
private boolean failedWrite;
public ChunkIOTask(final ServerLevel world, final RegionDataController regionDataController,
final int chunkX, final int chunkZ, final Priority priority, final InProgressRead inProgressRead) {
final int chunkX, final int chunkZ, final Priority priority, final InProgressRead inProgressRead,
final CompoundTag inProgressWrite) {
this.world = world;
this.regionDataController = regionDataController;
this.chunkX = chunkX;
this.chunkZ = chunkZ;
this.priority = priority;
this.inProgressRead = inProgressRead;
this.inProgressWrite = inProgressWrite;
}
public Priority getPriority() {
@@ -969,26 +888,16 @@ public final class MoonriseRegionFileIO {
}
}
// must hold lock on this object
private void updatePriority(final Priority priority) {
this.priority = priority;
if (this.currentTask != null) {
this.currentTask.setPriority(priority);
}
for (final InProgressWrite write : this.allPendingWrites) {
if (write.writeTask != null) {
write.writeTask.setPriority(priority);
}
}
}
public boolean setPriority(final Priority priority) {
synchronized (this) {
if (this.priority == priority) {
return false;
}
this.updatePriority(priority);
this.priority = priority;
if (this.currentTask != null) {
this.currentTask.setPriority(priority);
}
return true;
}
@@ -1000,7 +909,10 @@ public final class MoonriseRegionFileIO {
return false;
}
this.updatePriority(priority);
this.priority = priority;
if (this.currentTask != null) {
this.currentTask.setPriority(priority);
}
return true;
}
@@ -1012,28 +924,15 @@ public final class MoonriseRegionFileIO {
return false;
}
this.updatePriority(priority);
this.priority = priority;
if (this.currentTask != null) {
this.currentTask.setPriority(priority);
}
return true;
}
}
private void pushPendingWrite(final InProgressWrite write) {
this.inProgressWrite = write;
synchronized (this) {
this.allPendingWrites.add(write);
if (write.writeTask != null) {
write.writeTask.setPriority(this.priority);
}
}
}
private void pendingWriteComplete(final InProgressWrite write) {
synchronized (this) {
this.allPendingWrites.remove(write);
}
}
public void scheduleReadIO() {
final PrioritisedExecutor.PrioritisedTask task;
synchronized (this) {
@@ -1065,7 +964,7 @@ public final class MoonriseRegionFileIO {
canRead[0] = false;
}
if (valueInMap.inProgressWrite != null) {
if (valueInMap.inProgressWrite != NOTHING_TO_WRITE) {
return valueInMap;
}
@@ -1151,7 +1050,8 @@ public final class MoonriseRegionFileIO {
this.finishRead(compoundTag, throwable);
if (!this.tryAbortWrite()) {
this.scheduleWriteCompress();
// we are already on the compression executor, don't bother scheduling
this.performWriteCompress();
}
}
@@ -1160,24 +1060,17 @@ public final class MoonriseRegionFileIO {
}
public void scheduleWriteCompress() {
final InProgressWrite inProgressWrite = this.inProgressWrite;
final PrioritisedExecutor.PrioritisedTask task;
synchronized (this) {
task = this.regionDataController.compressionExecutor.createTask(() -> {
ChunkIOTask.this.performWriteCompress(inProgressWrite);
}, this.priority);
task = this.regionDataController.compressionExecutor.createTask(this::performWriteCompress, this.priority);
this.currentTask = task;
}
inProgressWrite.addToWaiters(this, (final CompoundTag data, final Throwable throwable) -> {
task.queue();
});
task.queue();
}
private boolean tryAbortWrite() {
final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
if (this.inProgressWrite == null) {
if (this.inProgressWrite == NOTHING_TO_WRITE) {
final ChunkIOTask inMap = this.regionDataController.chunkTasks.compute(chunkKey, (final long keyInMap, final ChunkIOTask valueInMap) -> {
if (valueInMap == null) {
throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkIOTask.this.toString() + ", report this!");
@@ -1186,7 +1079,7 @@ public final class MoonriseRegionFileIO {
throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkIOTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
}
if (valueInMap.inProgressWrite != null) {
if (valueInMap.inProgressWrite != NOTHING_TO_WRITE) {
return valueInMap;
}
@@ -1202,62 +1095,61 @@ public final class MoonriseRegionFileIO {
return false;
}
private void performWriteCompress(final InProgressWrite inProgressWrite) {
final CompoundTag write = inProgressWrite.value;
if (!inProgressWrite.isComplete()) {
throw new IllegalStateException("Should be writable");
}
private void performWriteCompress() {
for (;;) {
final CompoundTag write = this.inProgressWrite;
if (write == NOTHING_TO_WRITE) {
throw new IllegalStateException("Should be writable");
}
RegionDataController.WriteData writeData = null;
boolean failedWrite = false;
RegionDataController.WriteData writeData = null;
boolean failedWrite = false;
try {
writeData = this.regionDataController.startWrite(this.chunkX, this.chunkZ, write);
} catch (final Throwable thr) {
// TODO implement this?
try {
writeData = this.regionDataController.startWrite(this.chunkX, this.chunkZ, write);
} catch (final Throwable thr) {
// TODO implement this?
/*if (thr instanceof RegionFileStorage.RegionFileSizeException) {
final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024);
LOGGER.error("Chunk at (" + this.chunkX + "," + this.chunkZ + ") in '" + WorldUtil.getWorldName(this.world) + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk.");
} else */
{
failedWrite = thr instanceof IOException;
LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
{
failedWrite = thr instanceof IOException;
LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
}
}
}
if (writeData == null) {
// null if a throwable was encountered
if (writeData == null) {
// null if a throwable was encountered
// we cannot continue to the I/O stage here, so try to complete
// we cannot continue to the I/O stage here, so try to complete
if (this.tryCompleteWrite(inProgressWrite, failedWrite)) {
return;
if (this.tryCompleteWrite(write, failedWrite)) {
return;
} else {
// fetch new data and try again
continue;
}
} else {
// fetch new data and try again
this.scheduleWriteCompress();
// writeData != null && !failedWrite
// we can continue to I/O stage
this.writeData = writeData;
this.scheduleWriteIO();
return;
}
} else {
// writeData != null && !failedWrite
// we can continue to I/O stage
this.writeData = writeData;
this.scheduleWriteIO(inProgressWrite);
return;
}
}
private void scheduleWriteIO(final InProgressWrite inProgressWrite) {
private void scheduleWriteIO() {
final PrioritisedExecutor.PrioritisedTask task;
synchronized (this) {
task = this.regionDataController.ioScheduler.createTask(this.chunkX, this.chunkZ, () -> {
ChunkIOTask.this.runWriteIO(inProgressWrite);
}, this.priority);
task = this.regionDataController.ioScheduler.createTask(this.chunkX, this.chunkZ, this::runWriteIO, this.priority);
this.currentTask = task;
}
task.queue();
}
private void runWriteIO(final InProgressWrite inProgressWrite) {
private void runWriteIO() {
RegionDataController.WriteData writeData = this.writeData;
this.writeData = null;
@@ -1270,14 +1162,14 @@ public final class MoonriseRegionFileIO {
LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
}
if (!this.tryCompleteWrite(inProgressWrite, failedWrite)) {
if (!this.tryCompleteWrite(writeData.input(), failedWrite)) {
// fetch new data and try again
this.scheduleWriteCompress();
}
return;
}
private boolean tryCompleteWrite(final InProgressWrite written, final boolean failedWrite) {
private boolean tryCompleteWrite(final CompoundTag written, final boolean failedWrite) {
final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
final boolean[] done = new boolean[] { false };
@@ -1324,6 +1216,14 @@ public final class MoonriseRegionFileIO {
return this.callbacks.isEmpty();
}
public CompoundTag getValue() {
return this.value;
}
public Throwable getThrowable() {
return this.throwable;
}
public boolean addToAsyncWaiters(final BiConsumer<CompoundTag, Throwable> callback) {
return this.callbacks.add(callback);
}
@@ -1341,72 +1241,11 @@ public final class MoonriseRegionFileIO {
try {
consumer.accept(value == null ? null : value.copy(), throwable);
} catch (final Throwable thr) {
LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data (read) for task " + task.toString(), thr);
LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data for task " + task.toString(), thr);
}
}
}
}
private static final class InProgressWrite {
private static final Logger LOGGER = LoggerFactory.getLogger(InProgressWrite.class);
private CompoundTag value;
private Throwable throwable;
private volatile boolean complete;
private final MultiThreadedQueue<BiConsumer<CompoundTag, Throwable>> callbacks = new MultiThreadedQueue<>();
private final PrioritisedExecutor.PrioritisedTask writeTask;
public InProgressWrite(final PrioritisedExecutor.PrioritisedTask writeTask) {
this.writeTask = writeTask;
}
public boolean isComplete() {
return this.complete;
}
public void schedule(final ChunkIOTask task, final Consumer<BiConsumer<CompoundTag, Throwable>> scheduler) {
scheduler.accept((final CompoundTag data, final Throwable throwable) -> {
InProgressWrite.this.complete(task, data, throwable);
});
}
public boolean addToAsyncWaiters(final BiConsumer<CompoundTag, Throwable> callback) {
return this.callbacks.add(callback);
}
public void addToWaiters(final ChunkIOTask task, final BiConsumer<CompoundTag, Throwable> consumer) {
if (!this.callbacks.add(consumer)) {
this.syncAccept(task, consumer, this.value, this.throwable);
}
}
private void syncAccept(final ChunkIOTask task, final BiConsumer<CompoundTag, Throwable> consumer, final CompoundTag value, final Throwable throwable) {
try {
consumer.accept(value == null ? null : value.copy(), throwable);
} catch (final Throwable thr) {
LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data (write) for task " + task.toString(), thr);
}
}
public void complete(final ChunkIOTask task, final CompoundTag value, final Throwable throwable) {
this.value = value;
this.throwable = throwable;
this.complete = true;
task.pendingWriteComplete(this);
BiConsumer<CompoundTag, Throwable> consumer;
while ((consumer = this.callbacks.pollOrBlockAdds()) != null) {
this.syncAccept(task, consumer, value, throwable);
}
}
public boolean cancel(final BiConsumer<CompoundTag, Throwable> callback) {
return this.callbacks.remove(callback);
}
}
}
public static abstract class RegionDataController {

View File

@@ -61,4 +61,12 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel {
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getTickingChunks();
public ReferenceList<ServerChunkCache.ChunkAndHolder> moonrise$getEntityTickingChunks();
public ReferenceList<ChunkHolder> moonrise$getUnsyncedChunks();
public void moonrise$addUnsyncedChunk(final ChunkHolder chunkHolder);
public void moonrise$removeUnsyncedChunk(final ChunkHolder chunkHolder);
public void moonrise$clearUnsyncedChunks();
}

View File

@@ -23,4 +23,8 @@ public interface ChunkSystemChunkHolder {
public LevelChunk moonrise$getFullChunk();
public boolean moonrise$isMarkedDirtyForPlayers();
public void moonrise$markDirtyForPlayers(final boolean value);
}

View File

@@ -1,6 +1,5 @@
package ca.spottedleaf.moonrise.patches.chunk_system.level.entity;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.list.EntityList;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
@@ -15,7 +14,6 @@ import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
@@ -76,7 +74,7 @@ public final class ChunkEntitySlices {
public static List<Entity> readEntities(final ServerLevel world, final CompoundTag compoundTag) {
// TODO check this and below on update for format changes
return EntityType.loadEntitiesRecursive(compoundTag.getList("Entities", 10), world, EntitySpawnReason.LOAD).collect(ImmutableList.toImmutableList());
return EntityType.loadEntitiesRecursive(compoundTag.getList("Entities", 10), world).collect(ImmutableList.toImmutableList());
}
// Paper start - rewrite chunk system
@@ -104,7 +102,7 @@ public final class ChunkEntitySlices {
}
final ListTag entitiesTag = new ListTag();
for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x, chunkPos.z, entities)) {
for (final Entity entity : entities) {
CompoundTag compoundTag = new CompoundTag();
if (entity.save(compoundTag)) {
entitiesTag.add(compoundTag);
@@ -151,12 +149,12 @@ public final class ChunkEntitySlices {
continue;
}
if (entity.shouldBeSaved()) {
PlatformHooks.get().unloadEntity(entity);
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
if (entity.isVehicle()) {
// we cannot assume that these entities are contained within this chunk, because entities can
// desync - so we need to remove them all
for (final Entity passenger : entity.getIndirectPassengers()) {
PlatformHooks.get().unloadEntity(passenger);
passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
}
}
}
@@ -165,7 +163,7 @@ public final class ChunkEntitySlices {
return this.entities.size() != 0;
}
public List<Entity> getAllEntities() {
private List<Entity> getAllEntities() {
final int len = this.entities.size();
if (len == 0) {
return new ArrayList<>();

View File

@@ -959,7 +959,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
public ChunkEntitySlices getOrCreateChunk(final int chunkX, final int chunkZ) {
final ChunkSlicesRegion region = this.getRegion(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
final ChunkEntitySlices ret;
ChunkEntitySlices ret;
if (region == null || (ret = region.get((chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT))) == null) {
return this.createEntityChunk(chunkX, chunkZ, true);
}
@@ -1057,7 +1057,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
@Override
public void onRemove(final Entity.RemovalReason reason) {
final Entity entity = this.entity;
EntityLookup.this.checkThread(entity, "Cannot remove entity off-main");
EntityLookup.this.checkThread(entity, "Cannot remove entity off-main"); // Paper - rewrite chunk system
final Visibility tickingState = EntityLookup.getEntityStatus(entity);
EntityLookup.this.removeEntity(entity);

View File

@@ -3,6 +3,7 @@ package ca.spottedleaf.moonrise.patches.chunk_system.level.poi;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import net.minecraft.SharedConstants;
import net.minecraft.nbt.CompoundTag;
@@ -122,6 +123,7 @@ public final class PoiChunk {
ret.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
final ServerLevel world = this.world;
final PoiManager poiManager = world.getPoiManager();
final int chunkX = this.chunkX;
final int chunkZ = this.chunkZ;
@@ -131,8 +133,13 @@ public final class PoiChunk {
continue;
}
// I do not believe asynchronously converting to CompoundTag is worth the scheduling.
final DataResult<Tag> serializedResult = PoiSection.Packed.CODEC.encodeStart(registryOps, section.pack());
final long key = CoordinateUtils.getChunkSectionKey(chunkX, sectionY, chunkZ);
// codecs are honestly such a fucking disaster. What the fuck is this trash?
final Codec<PoiSection> codec = PoiSection.codec(() -> {
poiManager.setDirty(key);
});
final DataResult<Tag> serializedResult = codec.encodeStart(registryOps, section);
final int finalSectionY = sectionY;
final Tag serialized = serializedResult.resultOrPartial((final String description) -> {
LOGGER.error("Failed to serialize poi chunk for world: " + WorldUtil.getWorldName(world) + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
@@ -176,18 +183,19 @@ public final class PoiChunk {
continue;
}
final CompoundTag section = sections.getCompound(key);
final DataResult<PoiSection.Packed> deserializeResult = PoiSection.Packed.CODEC.parse(registryOps, section);
final int finalSectionY = sectionY;
final PoiSection.Packed packed = deserializeResult.resultOrPartial((final String description) -> {
LOGGER.error("Failed to deserialize poi chunk for world: " + WorldUtil.getWorldName(world) + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
}).orElse(null);
final long coordinateKey = CoordinateUtils.getChunkSectionKey(chunkX, sectionY, chunkZ);
final PoiSection deserialized = packed == null ? null : packed.unpack(() -> {
// codecs are honestly such a fucking disaster. What the fuck is this trash?
final Codec<PoiSection> codec = PoiSection.codec(() -> {
poiManager.setDirty(coordinateKey);
});
final CompoundTag section = sections.getCompound(key);
final DataResult<PoiSection> deserializeResult = codec.parse(registryOps, section);
final int finalSectionY = sectionY;
final PoiSection deserialized = deserializeResult.resultOrPartial((final String description) -> {
LOGGER.error("Failed to deserialize poi chunk for world: " + WorldUtil.getWorldName(world) + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
}).orElse(null);
if (deserialized == null || ((ChunkSystemPoiSection)deserialized).moonrise$isEmpty()) {
// completely empty, no point in storing this
continue;

View File

@@ -1,10 +1,19 @@
package ca.spottedleaf.moonrise.patches.chunk_system.level.storage;
import com.mojang.serialization.Dynamic;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public interface ChunkSystemSectionStorage {
public CompoundTag moonrise$read(final int chunkX, final int chunkZ) throws IOException;
public void moonrise$write(final int chunkX, final int chunkZ, final CompoundTag data) throws IOException;
public RegionFileStorage moonrise$getRegionStorage();
public void moonrise$close() throws IOException;

View File

@@ -6,6 +6,7 @@ import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.misc.AllocatingRateLimiter;
import ca.spottedleaf.moonrise.common.misc.SingleUserAreaMap;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
@@ -15,10 +16,13 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManage
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration;
import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongComparator;
import it.unimi.dsi.fastutil.longs.LongHeapPriorityQueue;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket;
@@ -38,6 +42,8 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.levelgen.BelowZeroRetrogen;
import java.lang.invoke.VarHandle;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
@@ -427,11 +433,11 @@ public final class RegionizedPlayerChunkLoader {
if (!this.sentChunks.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) {
return;
}
PlatformHooks.get().onChunkUnWatch(this.world, new ChunkPos(chunkX, chunkZ), this.player);
this.sendUnloadChunkRaw(chunkX, chunkZ);
}
private void sendUnloadChunkRaw(final int chunkX, final int chunkZ) {
PlatformHooks.get().onChunkUnWatch(this.world, new ChunkPos(chunkX, chunkZ), this.player);
// Note: Check PlayerChunkSender#dropChunk for other logic
// Note: drop isAlive() check so that chunks properly unload client-side when the player dies
((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
@@ -825,7 +831,7 @@ public final class RegionizedPlayerChunkLoader {
}
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
// not yet post-processed, need to do this so that tile entities can properly be sent to clients
chunk.postProcessGeneration(this.world);
chunk.postProcessGeneration();
// check if there was any recursive action
if (this.removed || this.sendQueue.isEmpty() || this.sendQueue.firstLong() != pendingSend) {
return;
@@ -1077,9 +1083,5 @@ public final class RegionizedPlayerChunkLoader {
// now all tickets should be removed, which is all of our external state
}
public LongOpenHashSet getSentChunksRaw() {
return this.sentChunks;
}
}
}

View File

@@ -231,8 +231,8 @@ public final class ChunkHolderManager {
public void autoSave() {
final List<NewChunkHolder> reschedule = new ArrayList<>();
final long currentTick = this.currentTick;
final long maxSaveTime = currentTick - Math.max(1L, PlatformHooks.get().configAutoSaveInterval(this.world));
final int maxToSave = PlatformHooks.get().configMaxAutoSavePerTick(this.world);
final long maxSaveTime = currentTick - Math.max(1L, PlatformHooks.get().configAutoSaveInterval());
final int maxToSave = PlatformHooks.get().configMaxAutoSavePerTick();
for (int autoSaved = 0; autoSaved < maxToSave && !this.autoSaveQueue.isEmpty();) {
final NewChunkHolder holder = this.autoSaveQueue.first();

View File

@@ -6,6 +6,7 @@ import ca.spottedleaf.concurrentutil.executor.thread.PrioritisedThreadPool;
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
import ca.spottedleaf.concurrentutil.util.Priority;
import ca.spottedleaf.moonrise.common.config.moonrise.MoonriseConfig;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.JsonUtil;
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
@@ -120,7 +121,6 @@ public final class ChunkTaskScheduler {
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor loadExecutor;
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor ioExecutor;
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor compressionExecutor;
public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor saveExecutor;
private final PrioritisedTaskQueue mainThreadExecutor = new PrioritisedTaskQueue();
@@ -288,7 +288,6 @@ public final class ChunkTaskScheduler {
this.ioExecutor = MoonriseCommon.SERVER_REGION_IO_GROUP.createExecutor(-1, MoonriseCommon.IO_QUEUE_HOLD_TIME, 0);
// we need a separate executor here so that on shutdown we can continue to process I/O tasks
this.compressionExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
this.saveExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
this.chunkHolderManager = new ChunkHolderManager(world, this);
}
@@ -856,14 +855,12 @@ public final class ChunkTaskScheduler {
public boolean haltIO(final boolean sync, final long maxWaitNS) {
this.ioExecutor.halt();
this.saveExecutor.halt();
this.compressionExecutor.halt();
if (sync) {
final long time = System.nanoTime();
for (long failures = 9L;; failures = ConcurrentUtil.linearLongBackoff(failures, 500_000L, 50_000_000L)) {
if (
!this.ioExecutor.isActive() &&
!this.saveExecutor.isActive() &&
!this.compressionExecutor.isActive()
) {
return true;

View File

@@ -12,6 +12,8 @@ import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.TickThread;
import ca.spottedleaf.moonrise.common.util.WorldUtil;
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemFeatures;
import ca.spottedleaf.moonrise.patches.chunk_system.async_save.AsyncChunkSaveData;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
@@ -45,7 +47,7 @@ import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.VarHandle;
@@ -758,6 +760,7 @@ public final class NewChunkHolder {
void onUnload() {
this.unloaded = true;
((ChunkSystemServerLevel)this.world).moonrise$removeUnsyncedChunk(this.vanillaChunkHolder);
((ChunkSystemLevel)this.world).moonrise$releaseChunkData(CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ));
}
@@ -860,7 +863,7 @@ public final class NewChunkHolder {
if (chunk != null) {
final LazyRunnable toRun = new LazyRunnable();
this.chunkDataUnload = new UnloadTask(new CallbackCompletable<>(), this.scheduler.saveExecutor.createTask(toRun), toRun);
this.chunkDataUnload = new UnloadTask(new CallbackCompletable<>(), this.scheduler.loadExecutor.createTask(toRun), toRun);
}
if (poiChunk != null) {
this.poiDataUnload = new UnloadTask(new CallbackCompletable<>(), null, null);
@@ -895,7 +898,7 @@ public final class NewChunkHolder {
final ChunkEntitySlices entityChunk = state.entityChunk();
final PoiChunk poiChunk = state.poiChunk();
final boolean shouldLevelChunkNotSave = PlatformHooks.get().forceNoSave(chunk);
final boolean shouldLevelChunkNotSave = ChunkSystemFeatures.forceNoSave(chunk);
// unload chunk data
if (chunk != null) {
@@ -1078,7 +1081,7 @@ public final class NewChunkHolder {
}
// Don't really have a choice but to place this hook here
PlatformHooks.get().onChunkHolderTicketChange(this.world, this.vanillaChunkHolder, oldLevel, newLevel);
PlatformHooks.get().onChunkHolderTicketChange(this.world, this, oldLevel, newLevel);
}
static final int NEIGHBOUR_RADIUS = 2;
@@ -1710,7 +1713,7 @@ public final class NewChunkHolder {
}
}
final boolean forceNoSaveChunk = PlatformHooks.get().forceNoSave(chunk);
final boolean forceNoSaveChunk = ChunkSystemFeatures.forceNoSave(chunk);
// can only synchronously save worldgen chunks during shutdown
boolean canSaveChunk = !forceNoSaveChunk && (chunk != null && ((shutdown || chunk instanceof LevelChunk) && chunk.isUnsaved()));
@@ -1740,6 +1743,56 @@ public final class NewChunkHolder {
: null;
}
static final class AsyncChunkSerializeTask implements Runnable {
private final ServerLevel world;
private final ChunkAccess chunk;
private final AsyncChunkSaveData asyncSaveData;
private final NewChunkHolder toComplete;
public AsyncChunkSerializeTask(final ServerLevel world, final ChunkAccess chunk, final AsyncChunkSaveData asyncSaveData,
final NewChunkHolder toComplete) {
this.world = world;
this.chunk = chunk;
this.asyncSaveData = asyncSaveData;
this.toComplete = toComplete;
}
@Override
public void run() {
final CompoundTag toSerialize;
try {
toSerialize = ChunkSystemFeatures.saveChunkAsync(this.world, this.chunk, this.asyncSaveData);
} catch (final Throwable throwable) {
LOGGER.error("Failed to asynchronously save chunk " + this.chunk.getPos() + " for world '" + WorldUtil.getWorldName(this.world) + "', falling back to synchronous save", throwable);
final ChunkPos pos = this.chunk.getPos();
((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().scheduleChunkTask(pos.x, pos.z, () -> {
final CompoundTag synchronousSave;
try {
synchronousSave = ChunkSystemFeatures.saveChunkAsync(AsyncChunkSerializeTask.this.world, AsyncChunkSerializeTask.this.chunk, AsyncChunkSerializeTask.this.asyncSaveData);
} catch (final Throwable throwable2) {
LOGGER.error("Failed to synchronously save chunk " + AsyncChunkSerializeTask.this.chunk.getPos() + " for world '" + WorldUtil.getWorldName(AsyncChunkSerializeTask.this.world) + "', chunk data will be lost", throwable2);
AsyncChunkSerializeTask.this.toComplete.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, null);
return;
}
AsyncChunkSerializeTask.this.toComplete.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, synchronousSave);
LOGGER.info("Successfully serialized chunk " + AsyncChunkSerializeTask.this.chunk.getPos() + " for world '" + WorldUtil.getWorldName(AsyncChunkSerializeTask.this.world) + "' synchronously");
}, Priority.HIGHEST);
return;
}
this.toComplete.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, toSerialize);
}
@Override
public String toString() {
return "AsyncChunkSerializeTask{" +
"chunk={pos=" + this.chunk.getPos() + ",world=\"" + WorldUtil.getWorldName(this.world) + "\"}" +
"}";
}
}
private boolean saveChunk(final ChunkAccess chunk, final boolean unloading) {
if (!chunk.isUnsaved()) {
if (unloading) {
@@ -1747,39 +1800,45 @@ public final class NewChunkHolder {
}
return false;
}
boolean completing = false;
boolean failedAsyncPrepare = false;
try {
final SerializableChunkData chunkData = SerializableChunkData.copyOf(this.world, chunk);
PlatformHooks.get().chunkSyncSave(this.world, chunk, chunkData);
if (unloading && ChunkSystemFeatures.supportsAsyncChunkSave()) {
try {
final AsyncChunkSaveData asyncSaveData = ChunkSystemFeatures.getAsyncSaveData(this.world, chunk);
chunk.tryMarkSaved();
this.chunkDataUnload.toRun().setRunnable(new AsyncChunkSerializeTask(this.world, chunk, asyncSaveData, this));
final CallbackCompletable<CompoundTag> completable = new CallbackCompletable<>();
chunk.setUnsaved(false);
final Runnable run = () -> {
final CompoundTag data = chunkData.write();
this.chunkDataUnload.task().queue();
completable.complete(data);
if (unloading) {
NewChunkHolder.this.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, data);
return true;
} catch (final Throwable thr) {
LOGGER.error("Failed to prepare async chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "', falling back to synchronous save", thr);
failedAsyncPrepare = true;
// fall through to synchronous save
}
};
final PrioritisedExecutor.PrioritisedTask task;
if (unloading) {
this.chunkDataUnload.toRun().setRunnable(run);
task = this.chunkDataUnload.task();
} else {
task = this.scheduler.saveExecutor.createTask(run);
}
task.queue();
final CompoundTag save = ChunkSerializer.write(this.world, chunk);
PlatformHooks.get().chunkSyncSave(this.world, chunk, save);
MoonriseRegionFileIO.scheduleSave(
this.world, this.chunkX, this.chunkZ, completable, task, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, Priority.NORMAL
);
if (unloading) {
completing = true;
this.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, save);
if (failedAsyncPrepare) {
LOGGER.info("Successfully serialized chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "' synchronously");
}
} else {
MoonriseRegionFileIO.scheduleSave(this.world, this.chunkX, this.chunkZ, save, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA);
}
chunk.setUnsaved(false);
} catch (final Throwable thr) {
LOGGER.error("Failed to save chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "'", thr);
if (unloading && !completing) {
this.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, null);
}
}
return true;

View File

@@ -65,7 +65,7 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
final ServerLevel world = this.world;
final ProtoChunk protoChunk = (ProtoChunk)this.fromChunk;
chunk = new LevelChunk(this.world, protoChunk, (final LevelChunk unused) -> {
PlatformHooks.get().postLoadProtoChunk(world, protoChunk);
ChunkStatusTasks.postLoadProtoChunk(world, protoChunk.getEntities());
});
this.chunkHolder.replaceProtoChunk(new ImposterProtoChunk(chunk, false));
}
@@ -82,10 +82,16 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
// This brings entity addition back in line with older versions of the game
// Since we load the NBT in the empty status, this will never block for I/O
((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager.getOrCreateEntityChunk(this.chunkX, this.chunkZ, false);
chunk.setLoaded(true);
} finally {
platformHooks.setCurrentlyLoading(this.chunkHolder.vanillaChunkHolder, null);
}
// we don't need the entitiesInLevel, not sure why it's there
chunk.setLoaded(true);
try {
platformHooks.setCurrentlyLoading(this.chunkHolder.vanillaChunkHolder, chunk);
chunk.registerAllBlockEntitiesAfterLevelLoad();
chunk.registerTickContainerInLevel(this.world);
chunk.setUnsavedListener(this.world.getChunkSource().chunkMap.worldGenContext.unsavedListener());
platformHooks.chunkFullStatusComplete(chunk, (ProtoChunk)this.fromChunk);
} finally {
platformHooks.setCurrentlyLoading(this.chunkHolder.vanillaChunkHolder, null);

View File

@@ -5,8 +5,8 @@ import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
import ca.spottedleaf.concurrentutil.util.Priority;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemConverters;
import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemFeatures;
import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
@@ -19,7 +19,7 @@ import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
import net.minecraft.world.level.levelgen.blending.BlendingData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -273,12 +273,9 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
}
}
private static record ReadChunk(ProtoChunk protoChunk, SerializableChunkData chunkData) {}
private static final class ChunkDataLoadTask extends CallbackDataLoadTask<ReadChunk, ChunkAccess> {
private static final class ChunkDataLoadTask extends CallbackDataLoadTask<CompoundTag, ChunkAccess> {
private ChunkDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
final int chunkZ, final Priority priority) {
final int chunkZ, final Priority priority) {
super(scheduler, world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, priority);
}
@@ -303,32 +300,30 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
}
@Override
protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final ReadChunk data, final Throwable throwable) {
protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final CompoundTag data, final Throwable throwable) {
if (throwable != null) {
return new TaskResult<>(null, throwable);
}
if (data == null || data.protoChunk() == null) {
if (data == null) {
return new TaskResult<>(this.getEmptyChunk(), null);
}
if (!PlatformHooks.get().hasMainChunkLoadHook()) {
return new TaskResult<>(data.protoChunk(), null);
if (ChunkSystemFeatures.supportsAsyncChunkDeserialization()) {
return this.deserialize(data);
}
// need to invoke the callback for loading on the main thread
// need to deserialize on main thread
return null;
}
private ProtoChunk getEmptyChunk() {
return new ProtoChunk(
new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, this.world,
this.world.registryAccess().lookupOrThrow(Registries.BIOME), (BlendingData)null
this.world.registryAccess().registryOrThrow(Registries.BIOME), (BlendingData)null
);
}
@Override
protected TaskResult<ReadChunk, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
protected TaskResult<CompoundTag, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
if (throwable != null) {
LOGGER.error("Failed to load chunk data for task: " + this.toString() + ", chunk data will be lost", throwable);
return new TaskResult<>(null, null);
@@ -342,33 +337,32 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
// run converters
final CompoundTag converted = this.world.getChunkSource().chunkMap.upgradeChunkTag(data);
// unpack the data
final SerializableChunkData chunkData = SerializableChunkData.parse(
this.world, this.world.registryAccess(), converted
);
if (chunkData == null) {
LOGGER.error("Deserialized chunk for task: " + this.toString() + " produced null, chunk data will be lost?");
}
// read into ProtoChunk
final ProtoChunk chunk = chunkData == null ? null : chunkData.read(
this.world, this.world.getPoiManager(), this.world.getChunkSource().chunkMap.storageInfo(),
new ChunkPos(this.chunkX, this.chunkZ)
);
return new TaskResult<>(new ReadChunk(chunk, chunkData), null);
return new TaskResult<>(converted, null);
} catch (final Throwable thr2) {
LOGGER.error("Failed to parse chunk data for task: " + this.toString() + ", chunk data will be lost", thr2);
return new TaskResult<>(null, null);
}
}
@Override
protected TaskResult<ChunkAccess, Throwable> runOnMain(final ReadChunk data, final Throwable throwable) {
PlatformHooks.get().mainChunkLoad(data.protoChunk(), data.chunkData());
private TaskResult<ChunkAccess, Throwable> deserialize(final CompoundTag data) {
try {
final ChunkAccess deserialized = ChunkSerializer.read(
this.world, this.world.getPoiManager(), this.world.getChunkSource().chunkMap.storageInfo(), new ChunkPos(this.chunkX, this.chunkZ), data
);
return new TaskResult<>(deserialized, null);
} catch (final Throwable thr2) {
LOGGER.error("Failed to parse chunk data for task: " + this.toString() + ", chunk data will be lost", thr2);
return new TaskResult<>(this.getEmptyChunk(), null);
}
}
return new TaskResult<>(data.protoChunk(), null);
@Override
protected TaskResult<ChunkAccess, Throwable> runOnMain(final CompoundTag data, final Throwable throwable) {
// data != null && throwable == null
if (ChunkSystemFeatures.supportsAsyncChunkDeserialization()) {
throw new UnsupportedOperationException();
}
return this.deserialize(data);
}
}

View File

@@ -1231,7 +1231,7 @@ public final class CollisionUtil {
((CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)second).moonrise$offsetX(),
ft, tf
);
if (mergedX == null) {
if (mergedX == MergedVoxelCoordinateList.EMPTY) {
return Shapes.empty();
}
final MergedVoxelCoordinateList mergedY = MergedVoxelCoordinateList.merge(
@@ -1239,7 +1239,7 @@ public final class CollisionUtil {
((CollisionVoxelShape)second).moonrise$rootCoordinatesY(), ((CollisionVoxelShape)second).moonrise$offsetY(),
ft, tf
);
if (mergedY == null) {
if (mergedY == MergedVoxelCoordinateList.EMPTY) {
return Shapes.empty();
}
final MergedVoxelCoordinateList mergedZ = MergedVoxelCoordinateList.merge(
@@ -1247,7 +1247,7 @@ public final class CollisionUtil {
((CollisionVoxelShape)second).moonrise$rootCoordinatesZ(), ((CollisionVoxelShape)second).moonrise$offsetZ(),
ft, tf
);
if (mergedZ == null) {
if (mergedZ == MergedVoxelCoordinateList.EMPTY) {
return Shapes.empty();
}
@@ -1329,7 +1329,7 @@ public final class CollisionUtil {
((CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)second).moonrise$offsetX(),
ft, tf
);
if (mergedX == null) {
if (mergedX == MergedVoxelCoordinateList.EMPTY) {
return false;
}
final MergedVoxelCoordinateList mergedY = MergedVoxelCoordinateList.merge(
@@ -1337,7 +1337,7 @@ public final class CollisionUtil {
((CollisionVoxelShape)second).moonrise$rootCoordinatesY(), ((CollisionVoxelShape)second).moonrise$offsetY(),
ft, tf
);
if (mergedY == null) {
if (mergedY == MergedVoxelCoordinateList.EMPTY) {
return false;
}
final MergedVoxelCoordinateList mergedZ = MergedVoxelCoordinateList.merge(
@@ -1345,7 +1345,7 @@ public final class CollisionUtil {
((CollisionVoxelShape)second).moonrise$rootCoordinatesZ(), ((CollisionVoxelShape)second).moonrise$offsetZ(),
ft, tf
);
if (mergedZ == null) {
if (mergedZ == MergedVoxelCoordinateList.EMPTY) {
return false;
}
@@ -1368,6 +1368,10 @@ public final class CollisionUtil {
}
}
private static final MergedVoxelCoordinateList EMPTY = new MergedVoxelCoordinateList(
new double[] { 0.0 }, 0.0, new int[0], new int[0], 0
);
private static int[] getIndices(final int length) {
final int[] ret = new int[length];
@@ -1502,7 +1506,7 @@ public final class CollisionUtil {
}
}
return resultSize <= 1 ? null : new MergedVoxelCoordinateList(coordinates, 0.0, firstIndices, secondIndices, resultSize - 1);
return resultSize <= 1 ? EMPTY : new MergedVoxelCoordinateList(coordinates, 0.0, firstIndices, secondIndices, resultSize - 1);
}
}

View File

@@ -0,0 +1,246 @@
package ca.spottedleaf.moonrise.patches.collisions.util;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Optional;
import java.util.Spliterator;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Collector;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
public final class NoneMatchStream<T> implements Stream<T> {
private final boolean value;
public NoneMatchStream(final boolean value) {
this.value = value;
}
@Override
public boolean noneMatch(Predicate<? super T> predicate) {
return this.value;
}
@Override
public Stream<T> filter(Predicate<? super T> predicate) {
return null;
}
@Override
public <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
return null;
}
@Override
public IntStream mapToInt(ToIntFunction<? super T> mapper) {
return null;
}
@Override
public LongStream mapToLong(ToLongFunction<? super T> mapper) {
return null;
}
@Override
public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
return null;
}
@Override
public <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {
return null;
}
@Override
public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
return null;
}
@Override
public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
return null;
}
@Override
public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
return null;
}
@Override
public Stream<T> distinct() {
return null;
}
@Override
public Stream<T> sorted() {
return null;
}
@Override
public Stream<T> sorted(Comparator<? super T> comparator) {
return null;
}
@Override
public Stream<T> peek(Consumer<? super T> action) {
return null;
}
@Override
public Stream<T> limit(long maxSize) {
return null;
}
@Override
public Stream<T> skip(long n) {
return null;
}
@Override
public void forEach(Consumer<? super T> action) {
}
@Override
public void forEachOrdered(Consumer<? super T> action) {
}
@NotNull
@Override
public Object[] toArray() {
return new Object[0];
}
@NotNull
@Override
public <A> A[] toArray(IntFunction<A[]> generator) {
return null;
}
@Override
public T reduce(T identity, BinaryOperator<T> accumulator) {
return null;
}
@NotNull
@Override
public Optional<T> reduce(BinaryOperator<T> accumulator) {
return Optional.empty();
}
@Override
public <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) {
return null;
}
@Override
public <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) {
return null;
}
@Override
public <R, A> R collect(Collector<? super T, A, R> collector) {
return null;
}
@NotNull
@Override
public Optional<T> min(Comparator<? super T> comparator) {
return Optional.empty();
}
@NotNull
@Override
public Optional<T> max(Comparator<? super T> comparator) {
return Optional.empty();
}
@Override
public long count() {
return 0;
}
@Override
public boolean anyMatch(Predicate<? super T> predicate) {
return false;
}
@Override
public boolean allMatch(Predicate<? super T> predicate) {
return false;
}
@NotNull
@Override
public Optional<T> findFirst() {
return Optional.empty();
}
@NotNull
@Override
public Optional<T> findAny() {
return Optional.empty();
}
@NotNull
@Override
public Iterator<T> iterator() {
return null;
}
@NotNull
@Override
public Spliterator<T> spliterator() {
return null;
}
@Override
public boolean isParallel() {
return false;
}
@NotNull
@Override
public Stream<T> sequential() {
return null;
}
@NotNull
@Override
public Stream<T> parallel() {
return null;
}
@NotNull
@Override
public Stream<T> unordered() {
return null;
}
@NotNull
@Override
public Stream<T> onClose(Runnable closeHandler) {
return null;
}
@Override
public void close() {
}
}

View File

@@ -93,7 +93,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
final int currentLevel = this.getLightLevel(worldX, worldY, worldZ);
final BlockState blockState = this.getBlockState(worldX, worldY, worldZ);
final int emittedLevel = (PlatformHooks.get().getLightEmission(blockState, lightAccess.getLevel(), this.lightEmissionPos.set(worldX, worldY, worldZ))) & emittedMask;
final int emittedLevel = (PlatformHooks.get().getLightEmission(blockState, lightAccess.getLevel(), this.mutablePos1.set(worldX, worldY, worldZ))) & emittedMask;
this.setLightLevel(worldX, worldY, worldZ, emittedLevel);
// this accounts for change in emitted light that would cause an increase
@@ -121,6 +121,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
}
protected final BlockPos.MutableBlockPos recalcCenterPos = new BlockPos.MutableBlockPos();
protected final BlockPos.MutableBlockPos recalcNeighbourPos = new BlockPos.MutableBlockPos();
@Override
protected int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ,
@@ -135,7 +136,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
return level;
}
final int opacity = Math.max(1, centerState.getLightBlock());
final int opacity = Math.max(1, centerState.getLightBlock(world, this.recalcCenterPos));
if (opacity >= 15) {
return level;
}
@@ -166,8 +167,9 @@ public final class BlockStarLightEngine extends StarLightEngine {
// here the block can be conditionally opaque (i.e light cannot propagate from it), so we need to test that
// we don't read the blockstate because most of the time this is false, so using the faster
// known transparency lookup results in a net win
final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(direction.opposite.nms);
final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(direction.nms);
this.recalcNeighbourPos.set(offX, offY, offZ);
final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcNeighbourPos, direction.opposite.nms);
final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcCenterPos, direction.nms);
if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) {
// not allowed to propagate
continue;
@@ -218,7 +220,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
final PalettedContainer<BlockState> states = section.states;
final int offY = sectionY << 4;
final BlockPos.MutableBlockPos mutablePos = this.lightEmissionPos;
final BlockPos.MutableBlockPos mutablePos = this.mutablePos1;
for (int index = 0; index < (16 * 16 * 16); ++index) {
final BlockState state = states.get(index);
mutablePos.set(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15));

View File

@@ -290,6 +290,9 @@ public final class SkyStarLightEngine extends StarLightEngine {
);
}
protected final BlockPos.MutableBlockPos recalcCenterPos = new BlockPos.MutableBlockPos();
protected final BlockPos.MutableBlockPos recalcNeighbourPos = new BlockPos.MutableBlockPos();
@Override
protected int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ,
final int expect) {
@@ -301,7 +304,8 @@ public final class SkyStarLightEngine extends StarLightEngine {
final BlockState centerState = this.getBlockState(worldX, worldY, worldZ);
final BlockState conditionallyOpaqueState;
final int opacity = Math.max(1, centerState.getLightBlock());
this.recalcCenterPos.set(worldX, worldY, worldZ);
final int opacity = Math.max(1, centerState.getLightBlock(lightAccess.getLevel(), this.recalcCenterPos));
if (((StarlightAbstractBlockState)centerState).starlight$isConditionallyFullOpaque()) {
conditionallyOpaqueState = centerState;
} else {
@@ -330,8 +334,9 @@ public final class SkyStarLightEngine extends StarLightEngine {
// here the block can be conditionally opaque (i.e light cannot propagate from it), so we need to test that
// we don't read the blockstate because most of the time this is false, so using the faster
// known transparency lookup results in a net win
final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(direction.opposite.nms);
final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(direction.nms);
this.recalcNeighbourPos.set(offX, offY, offZ);
final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcNeighbourPos, direction.opposite.nms);
final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcCenterPos, direction.nms);
if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) {
// not allowed to propagate
continue;
@@ -599,6 +604,7 @@ public final class SkyStarLightEngine extends StarLightEngine {
// clobbering the light values will result in broken propagation)
protected final int tryPropagateSkylight(final BlockGetter world, final int worldX, int startY, final int worldZ,
final boolean extrudeInitialised, final boolean delayLightSet) {
final BlockPos.MutableBlockPos mutablePos = this.mutablePos3;
final int encodeOffset = this.coordinateOffset;
final long propagateDirection = AxisDirection.POSITIVE_Y.everythingButThisDirection; // just don't check upwards.
@@ -620,7 +626,8 @@ public final class SkyStarLightEngine extends StarLightEngine {
final VoxelShape fromShape;
if (((StarlightAbstractBlockState)above).starlight$isConditionallyFullOpaque()) {
fromShape = above.getFaceOcclusionShape(AxisDirection.NEGATIVE_Y.nms);
this.mutablePos2.set(worldX, startY + 1, worldZ);
fromShape = above.getFaceOcclusionShape(world, this.mutablePos2, AxisDirection.NEGATIVE_Y.nms);
if (Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
// above wont let us propagate
break;
@@ -630,9 +637,10 @@ public final class SkyStarLightEngine extends StarLightEngine {
}
// does light propagate from the top down?
mutablePos.set(worldX, startY, worldZ);
long flags = 0L;
if (((StarlightAbstractBlockState)current).starlight$isConditionallyFullOpaque()) {
final VoxelShape cullingFace = current.getFaceOcclusionShape(AxisDirection.POSITIVE_Y.nms);
final VoxelShape cullingFace = current.getFaceOcclusionShape(world, mutablePos, AxisDirection.POSITIVE_Y.nms);
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
// can't propagate here, we're done on this column.
@@ -641,7 +649,7 @@ public final class SkyStarLightEngine extends StarLightEngine {
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
}
final int opacity = current.getLightBlock();
final int opacity = current.getLightBlock(world, mutablePos);
if (opacity > 0) {
// let the queued value (if any) handle it from here.
break;

View File

@@ -44,9 +44,9 @@ public abstract class StarLightEngine {
protected static enum AxisDirection {
// Declaration order is important and relied upon. Do not change without modifying propagation code.
POSITIVE_X(1, 0, 0, Direction.EAST) , NEGATIVE_X(-1, 0, 0, Direction.WEST),
POSITIVE_Z(0, 0, 1, Direction.SOUTH), NEGATIVE_Z(0, 0, -1, Direction.NORTH),
POSITIVE_Y(0, 1, 0, Direction.UP) , NEGATIVE_Y(0, -1, 0, Direction.DOWN);
POSITIVE_X(1, 0, 0), NEGATIVE_X(-1, 0, 0),
POSITIVE_Z(0, 0, 1), NEGATIVE_Z(0, 0, -1),
POSITIVE_Y(0, 1, 0), NEGATIVE_Y(0, -1, 0);
static {
POSITIVE_X.opposite = NEGATIVE_X; NEGATIVE_X.opposite = POSITIVE_X;
@@ -63,11 +63,11 @@ public abstract class StarLightEngine {
public final long everythingButThisDirection;
public final long everythingButTheOppositeDirection;
AxisDirection(final int x, final int y, final int z, final Direction nms) {
AxisDirection(final int x, final int y, final int z) {
this.x = x;
this.y = y;
this.z = z;
this.nms = nms;
this.nms = Direction.fromDelta(x, y, z);
this.everythingButThisDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << this.ordinal()));
// positive is always even, negative is always odd. Flip the 1 bit to get the negative direction.
this.everythingButTheOppositeDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << (this.ordinal() ^ 1)));
@@ -107,7 +107,9 @@ public abstract class StarLightEngine {
// index = x + (z * 5)
protected final boolean[][] emptinessMapCache = new boolean[5 * 5][];
protected final BlockPos.MutableBlockPos lightEmissionPos = new BlockPos.MutableBlockPos();
protected final BlockPos.MutableBlockPos mutablePos1 = new BlockPos.MutableBlockPos();
protected final BlockPos.MutableBlockPos mutablePos2 = new BlockPos.MutableBlockPos();
protected final BlockPos.MutableBlockPos mutablePos3 = new BlockPos.MutableBlockPos();
protected int encodeOffsetX;
protected int encodeOffsetY;
@@ -1149,9 +1151,10 @@ public abstract class StarLightEngine {
if (blockState == null) {
continue;
}
this.mutablePos1.set(offX, offY, offZ);
long flags = 0;
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
continue;
@@ -1159,7 +1162,7 @@ public abstract class StarLightEngine {
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
}
final int opacity = blockState.getLightBlock();
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
if (targetLevel <= currentLevel) {
continue;
@@ -1183,12 +1186,13 @@ public abstract class StarLightEngine {
} else {
// we actually need to worry about our state here
final BlockState fromBlock = this.getBlockState(posX, posY, posZ);
this.mutablePos2.set(posX, posY, posZ);
for (final AxisDirection propagate : checkDirections) {
final int offX = posX + propagate.x;
final int offY = posY + propagate.y;
final int offZ = posZ + propagate.z;
final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(propagate.nms) : Shapes.empty();
final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(world, this.mutablePos2, propagate.nms) : Shapes.empty();
if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
continue;
@@ -1208,9 +1212,10 @@ public abstract class StarLightEngine {
if (blockState == null) {
continue;
}
this.mutablePos1.set(offX, offY, offZ);
long flags = 0;
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
continue;
@@ -1218,7 +1223,7 @@ public abstract class StarLightEngine {
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
}
final int opacity = blockState.getLightBlock();
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
if (targetLevel <= currentLevel) {
continue;
@@ -1291,10 +1296,10 @@ public abstract class StarLightEngine {
if (blockState == null) {
continue;
}
this.lightEmissionPos.set(offX, offY, offZ);
this.mutablePos1.set(offX, offY, offZ);
long flags = 0;
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
continue;
@@ -1302,7 +1307,7 @@ public abstract class StarLightEngine {
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
}
final int opacity = blockState.getLightBlock();
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity));
if (lightLevel > targetLevel) {
// it looks like another source propagated here, so re-propagate it
@@ -1316,7 +1321,7 @@ public abstract class StarLightEngine {
| (FLAG_RECHECK_LEVEL | flags);
continue;
}
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.lightEmissionPos)) & emittedMask;
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.mutablePos1)) & emittedMask;
if (emittedLight != 0) {
// re-propagate source
// note: do not set recheck level, or else the propagation will fail
@@ -1348,6 +1353,7 @@ public abstract class StarLightEngine {
} else {
// we actually need to worry about our state here
final BlockState fromBlock = this.getBlockState(posX, posY, posZ);
this.mutablePos2.set(posX, posY, posZ);
for (final AxisDirection propagate : checkDirections) {
final int offX = posX + propagate.x;
final int offY = posY + propagate.y;
@@ -1356,7 +1362,7 @@ public abstract class StarLightEngine {
final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset;
final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8);
final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(propagate.nms) : Shapes.empty();
final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(world, this.mutablePos2, propagate.nms) : Shapes.empty();
if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
continue;
@@ -1374,10 +1380,10 @@ public abstract class StarLightEngine {
if (blockState == null) {
continue;
}
this.lightEmissionPos.set(offX, offY, offZ);
this.mutablePos1.set(offX, offY, offZ);
long flags = 0;
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
continue;
@@ -1385,7 +1391,7 @@ public abstract class StarLightEngine {
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
}
final int opacity = blockState.getLightBlock();
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity));
if (lightLevel > targetLevel) {
// it looks like another source propagated here, so re-propagate it
@@ -1399,7 +1405,7 @@ public abstract class StarLightEngine {
| (FLAG_RECHECK_LEVEL | flags);
continue;
}
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.lightEmissionPos)) & emittedMask;
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.mutablePos1)) & emittedMask;
if (emittedLight != 0) {
// re-propagate source
// note: do not set recheck level, or else the propagation will fail

View File

@@ -1,13 +0,0 @@
package ca.spottedleaf.moonrise.patches.starlight.storage;
public interface StarlightSectionData {
public int starlight$getBlockLightState();
public void starlight$setBlockLightState(final int state);
public int starlight$getSkyLightState();
public void starlight$setSkyLightState(final int state);
}

View File

@@ -14,20 +14,19 @@ import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import org.slf4j.Logger;
// note: keep in-sync with SerializableChunkDataMixin
public final class SaveUtil {
private static final Logger LOGGER = LogUtils.getLogger();
public static final int STARLIGHT_LIGHT_VERSION = 9;
private static final int STARLIGHT_LIGHT_VERSION = 9;
public static int getLightVersion() {
return STARLIGHT_LIGHT_VERSION;
}
public static final String BLOCKLIGHT_STATE_TAG = "starlight.blocklight_state";
public static final String SKYLIGHT_STATE_TAG = "starlight.skylight_state";
public static final String STARLIGHT_VERSION_TAG = "starlight.light_version";
private static final String BLOCKLIGHT_STATE_TAG = "starlight.blocklight_state";
private static final String SKYLIGHT_STATE_TAG = "starlight.skylight_state";
private static final String STARLIGHT_VERSION_TAG = "starlight.light_version";
public static void saveLightHook(final Level world, final ChunkAccess chunk, final CompoundTag nbt) {
try {

View File

@@ -1,15 +1,10 @@
accessWidener v1 named
# BlockStateBase
accessible field net/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase lightBlock I
accessible field net/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase occlusionShape Lnet/minecraft/world/phys/shapes/VoxelShape;
accessible field net/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase occlusionShapesByFace [Lnet/minecraft/world/phys/shapes/VoxelShape;
# BlockStateBase.ShapeCache
accessible class net/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache
accessible field net/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache lightBlock I
accessible field net/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache collisionShape Lnet/minecraft/world/phys/shapes/VoxelShape;
accessible field net/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache occlusionShapes [Lnet/minecraft/world/phys/shapes/VoxelShape;
# LevelChunkSection
accessible field net/minecraft/world/level/chunk/LevelChunkSection states Lnet/minecraft/world/level/chunk/PalettedContainer;
@@ -22,6 +17,8 @@ accessible field net/minecraft/world/level/chunk/PalettedContainer data Lnet/min
# PalettedContainer.Data
accessible class net/minecraft/world/level/chunk/PalettedContainer$Data
accessible field net/minecraft/world/level/chunk/PalettedContainer$Data storage Lnet/minecraft/util/BitStorage;
accessible field net/minecraft/world/level/chunk/PalettedContainer$Data palette Lnet/minecraft/world/level/chunk/Palette;
# PaletteResize
@@ -39,17 +36,15 @@ mutable field net/minecraft/server/level/ChunkMap updatingChunkMap Lit/unimi/dsi
mutable field net/minecraft/server/level/ChunkMap pendingUnloads Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
mutable field net/minecraft/server/level/ChunkMap pendingGenerationTasks Ljava/util/List;
mutable field net/minecraft/server/level/ChunkMap unloadQueue Ljava/util/Queue;
mutable field net/minecraft/server/level/ChunkMap worldgenTaskDispatcher Lnet/minecraft/server/level/ChunkTaskDispatcher;
mutable field net/minecraft/server/level/ChunkMap lightTaskDispatcher Lnet/minecraft/server/level/ChunkTaskDispatcher;
mutable field net/minecraft/server/level/ChunkMap queueSorter Lnet/minecraft/server/level/ChunkTaskPriorityQueueSorter;
mutable field net/minecraft/server/level/ChunkMap worldgenMailbox Lnet/minecraft/util/thread/ProcessorHandle;
mutable field net/minecraft/server/level/ChunkMap mainThreadMailbox Lnet/minecraft/util/thread/ProcessorHandle;
accessible method net/minecraft/server/level/ChunkMap setServerViewDistance (I)V
accessible method net/minecraft/server/level/ChunkMap upgradeChunkTag (Lnet/minecraft/nbt/CompoundTag;)Lnet/minecraft/nbt/CompoundTag;
accessible field net/minecraft/server/level/ChunkMap worldGenContext Lnet/minecraft/world/level/chunk/status/WorldGenContext;
accessible field net/minecraft/server/level/ChunkMap tickingGenerated Ljava/util/concurrent/atomic/AtomicInteger;
accessible field net/minecraft/server/level/ChunkMap progressListener Lnet/minecraft/server/level/progress/ChunkProgressListener;
accessible method net/minecraft/server/level/ChunkMap playerIsCloseEnoughForSpawning (Lnet/minecraft/server/level/ServerPlayer;Lnet/minecraft/world/level/ChunkPos;)Z
mutable field net/minecraft/server/level/ChunkMap chunksToEagerlySave Lit/unimi/dsi/fastutil/longs/LongSet;
mutable field net/minecraft/server/level/ChunkMap activeChunkWrites Ljava/util/concurrent/atomic/AtomicInteger;
# ChunkLevel
accessible field net/minecraft/server/level/ChunkLevel FULL_CHUNK_LEVEL I
@@ -64,8 +59,8 @@ mutable field net/minecraft/world/level/lighting/LevelLightEngine skyEngine Lnet
# ThreadedLevelLightEngine
accessible class net/minecraft/server/level/ThreadedLevelLightEngine$TaskType
mutable field net/minecraft/server/level/ThreadedLevelLightEngine consecutiveExecutor Lnet/minecraft/util/thread/ConsecutiveExecutor;
mutable field net/minecraft/server/level/ThreadedLevelLightEngine taskDispatcher Lnet/minecraft/server/level/ChunkTaskDispatcher;
mutable field net/minecraft/server/level/ThreadedLevelLightEngine sorterMailbox Lnet/minecraft/util/thread/ProcessorHandle;
mutable field net/minecraft/server/level/ThreadedLevelLightEngine taskMailbox Lnet/minecraft/util/thread/ProcessorMailbox;
# SectionStorage
accessible field net/minecraft/world/level/chunk/storage/SectionStorage levelHeightAccessor Lnet/minecraft/world/level/LevelHeightAccessor;
@@ -192,7 +187,9 @@ mutable field net/minecraft/server/level/DistanceManager ticketTracker Lnet/mine
mutable field net/minecraft/server/level/DistanceManager tickingTicketsTracker Lnet/minecraft/server/level/TickingTracker;
mutable field net/minecraft/server/level/DistanceManager playerTicketManager Lnet/minecraft/server/level/DistanceManager$PlayerTicketTracker;
mutable field net/minecraft/server/level/DistanceManager chunksToUpdateFutures Ljava/util/Set;
mutable field net/minecraft/server/level/DistanceManager ticketDispatcher Lnet/minecraft/server/level/ThrottlingChunkTaskDispatcher;
mutable field net/minecraft/server/level/DistanceManager ticketThrottler Lnet/minecraft/server/level/ChunkTaskPriorityQueueSorter;
mutable field net/minecraft/server/level/DistanceManager ticketThrottlerInput Lnet/minecraft/util/thread/ProcessorHandle;
mutable field net/minecraft/server/level/DistanceManager ticketThrottlerReleaser Lnet/minecraft/util/thread/ProcessorHandle;
mutable field net/minecraft/server/level/DistanceManager ticketsToRelease Lit/unimi/dsi/fastutil/longs/LongSet;
mutable field net/minecraft/server/level/DistanceManager mainThreadExecutor Ljava/util/concurrent/Executor;
mutable field net/minecraft/server/level/DistanceManager naturalSpawnChunkCounter Lnet/minecraft/server/level/DistanceManager$FixedPlayerDistanceChunkTracker;
@@ -301,7 +298,3 @@ accessible class net/minecraft/world/level/levelgen/DensityFunctions$EndIslandDe
# LocalMobCapCalculator$MobCounts
accessible class net/minecraft/world/level/LocalMobCapCalculator$MobCounts
# SectionStorage$PackedChunk
accessible class net/minecraft/world/level/chunk/storage/SectionStorage$PackedChunk

View File

@@ -1,143 +1,143 @@
{
"required": true,
"minVersion": "0.8",
"package": "ca.spottedleaf.moonrise.mixin",
"compatibilityLevel": "JAVA_21",
"mixins": [
"bitstorage.SimpleBitStorageMixin",
"bitstorage.ZeroBitStorageMixin",
"block_counting.BitStorageMixin",
"block_counting.LevelChunkSectionMixin",
"block_counting.SimpleBitStorageMixin",
"block_counting.ZeroBitStorageMixin",
"block_entity_remove.LevelMixin",
"blockstate_propertyaccess.BooleanPropertyMixin",
"blockstate_propertyaccess.EnumPropertyMixin",
"blockstate_propertyaccess.IntegerPropertyMixin",
"blockstate_propertyaccess.PropertyMixin",
"blockstate_propertyaccess.StateHolderMixin",
"chunk_system.ChunkBufferMixin",
"chunk_system.ChunkGeneratorMixin",
"chunk_system.ChunkHolderMixin",
"chunk_system.ChunkMap$DistanceManagerMixin",
"chunk_system.ChunkMapMixin",
"chunk_system.ChunkPyramidMixin",
"chunk_system.ChunkStatusMixin",
"chunk_system.ChunkStepMixin",
"chunk_system.ChunkStorageMixin",
"chunk_system.DistanceManagerMixin",
"chunk_system.EntityGetterMixin",
"chunk_system.EntityMixin",
"chunk_system.EntityTickListMixin",
"chunk_system.GenerationChunkHolderMixin",
"chunk_system.LevelChunkMixin",
"chunk_system.LevelChunkTicksMixin",
"chunk_system.LevelMixin",
"chunk_system.LevelReaderMixin",
"chunk_system.MinecraftServerMixin",
"chunk_system.NoiseBasedChunkGeneratorMixin",
"chunk_system.PlayerListMixin",
"chunk_system.PoiManagerMixin",
"chunk_system.PoiSectionMixin",
"chunk_system.RegionFileMixin",
"chunk_system.RegionFileStorageMixin",
"chunk_system.SectionStorageMixin",
"chunk_system.SerializableChunkDataMixin",
"chunk_system.ServerChunkCache$MainThreadExecutorMixin",
"chunk_system.ServerChunkCacheMixin",
"chunk_system.ServerLevelMixin",
"chunk_system.ServerPlayerMixin",
"chunk_system.SortedArraySetMixin",
"chunk_system.StructureCheckMixin",
"chunk_system.StructureTemplate$PaletteMixin",
"chunk_system.TicketMixin",
"chunk_tick_iteration.ChunkMapMixin",
"chunk_tick_iteration.DistanceManagerMixin",
"chunk_tick_iteration.ServerChunkCacheMixin",
"chunk_tick_iteration.ServerLevelMixin",
"collisions.ArmorStandMixin",
"collisions.ArrayVoxelShapeMixin",
"collisions.BitSetDiscreteVoxelShapeMixin",
"collisions.BlockMixin",
"collisions.BlockPosMixin",
"collisions.BlockStateBaseMixin",
"collisions.CubeVoxelShapeMixin",
"collisions.DirectionMixin",
"collisions.DiscreteVoxelShapeMixin",
"collisions.EntityGetterMixin",
"collisions.EntityMixin",
"collisions.LevelMixin",
"collisions.ServerEntityMixin",
"collisions.ServerExplosionMixin",
"collisions.ShapesMixin",
"collisions.SliceShapeMixin",
"collisions.VoxelShapeMixin",
"command.CommandsMixin",
"config.MinecraftServerMixin",
"end_island.DensityFunctions$EndIslandDensityFunctionMixin",
"entity_tracker.ChunkMapMixin",
"entity_tracker.EntityMixin",
"entity_tracker.TrackedEntityMixin",
"fast_palette.CrudeIncrementalIntIdentityHashBiMapMixin",
"fast_palette.HashMapPaletteMixin",
"fast_palette.LinearPaletteMixin",
"fast_palette.PalettedContainer$DataMixin",
"fast_palette.PalettedContainerMixin",
"fast_palette.PaletteMixin",
"fast_palette.SingleValuePaletteMixin",
"fluid.FlowingFluidMixin",
"fluid.FluidStateMixin",
"fluid.MappedRegistryMixin",
"getblock.ChunkAccessMixin",
"getblock.LevelChunkMixin",
"getblock.LevelMixin",
"keep_alive_client.ServerGamePacketListenerImplMixin",
"mob_spawning.EntityTypeMixin",
"mob_spawning.LocalMobCapCalculator$MobCountsMixin",
"mob_spawning.MobSpawnSettingsMixin",
"mob_spawning.NaturalSpawnerMixin",
"poi_lookup.AcquirePoiMixin",
"poi_lookup.PoiManagerMixin",
"poi_lookup.PortalForcerMixin",
"random_ticking.BiomeManagerMixin",
"random_ticking.BiomeMixin",
"random_ticking.LevelMixin",
"random_ticking.ServerLevelMixin",
"serverlist.ConnectionMixin",
"starlight.blockstate.BlockStateBaseMixin",
"starlight.chunk.ChunkAccessMixin",
"starlight.chunk.EmptyLevelChunkMixin",
"starlight.chunk.ImposterProtoChunkMixin",
"starlight.chunk.LevelChunkMixin",
"starlight.chunk.ProtoChunkMixin",
"starlight.lightengine.LevelLightEngineMixin",
"starlight.lightengine.ThreadedLevelLightEngineMixin",
"starlight.world.SerializableChunkData$SectionData",
"starlight.world.SerializableChunkDataMixin",
"starlight.world.WorldGenRegionMixin",
"util_thread_counts.UtilMixin",
"util_threading_detector.PalettedContainerMixin",
"util_threading_detector.ThreadingDetectorMixin",
"util_time_source.UtilMixin"
],
"client": [
"chunk_system.ClientLevelMixin",
"chunk_system.OptionsMixin",
"collisions.LiquidBlockRendererMixin",
"collisions.ParticleMixin",
"config.MinecraftMixin",
"loading_screen.LevelLoadStatusManagerMixin",
"profiler.MinecraftMixin",
"render.SectionRenderDispatcherMixin",
"serverlist.ClientConnectionMixin",
"serverlist.ServerAddressResolverMixin",
"serverlist.ServerSelectionListMixin",
"starlight.multiplayer.ClientPacketListenerMixin"
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"conformVisibility": true
}
"required": true,
"minVersion": "0.8",
"package": "ca.spottedleaf.moonrise.mixin",
"compatibilityLevel": "JAVA_21",
"mixins": [
"bitstorage.SimpleBitStorageMixin",
"bitstorage.ZeroBitStorageMixin",
"block_counting.BitStorageMixin",
"block_counting.LevelChunkSectionMixin",
"block_counting.SimpleBitStorageMixin",
"block_counting.ZeroBitStorageMixin",
"block_entity_remove.LevelMixin",
"blockstate_propertyaccess.BooleanPropertyMixin",
"blockstate_propertyaccess.EnumPropertyMixin",
"blockstate_propertyaccess.IntegerPropertyMixin",
"blockstate_propertyaccess.PropertyMixin",
"blockstate_propertyaccess.StateHolderMixin",
"getblock.ChunkAccessMixin",
"getblock.LevelChunkMixin",
"getblock.LevelMixin",
"chunk_system.ChunkBufferMixin",
"chunk_system.ChunkGeneratorMixin",
"chunk_system.ChunkHolderMixin",
"chunk_system.ChunkMap$DistanceManagerMixin",
"chunk_system.ChunkMapMixin",
"chunk_system.ChunkPyramidMixin",
"chunk_system.ChunkSerializerMixin",
"chunk_system.ChunkStatusMixin",
"chunk_system.ChunkStepMixin",
"chunk_system.ChunkStorageMixin",
"chunk_system.DistanceManagerMixin",
"chunk_system.EntityGetterMixin",
"chunk_system.EntityMixin",
"chunk_system.EntityTickListMixin",
"chunk_system.GenerationChunkHolderMixin",
"chunk_system.LevelChunkMixin",
"chunk_system.LevelChunkTicksMixin",
"chunk_system.LevelMixin",
"chunk_system.LevelReaderMixin",
"chunk_system.MinecraftServerMixin",
"chunk_system.NoiseBasedChunkGeneratorMixin",
"chunk_system.PlayerListMixin",
"chunk_system.PoiManagerMixin",
"chunk_system.PoiSectionMixin",
"chunk_system.RegionFileMixin",
"chunk_system.RegionFileStorageMixin",
"chunk_system.SectionStorageMixin",
"chunk_system.ServerChunkCache$MainThreadExecutorMixin",
"chunk_system.ServerChunkCacheMixin",
"chunk_system.ServerLevelMixin",
"chunk_system.ServerPlayerMixin",
"chunk_system.SortedArraySetMixin",
"chunk_system.StructureCheckMixin",
"chunk_system.StructureTemplate$PaletteMixin",
"chunk_system.TicketMixin",
"chunk_tick_iteration.ChunkMapMixin",
"chunk_tick_iteration.DistanceManagerMixin",
"chunk_tick_iteration.ServerChunkCacheMixin",
"chunk_tick_iteration.ServerLevelMixin",
"collisions.ArmorStandMixin",
"collisions.ArrayVoxelShapeMixin",
"collisions.BitSetDiscreteVoxelShapeMixin",
"collisions.BlockMixin",
"collisions.BlockPosMixin",
"collisions.BlockStateBaseMixin",
"collisions.CubeVoxelShapeMixin",
"collisions.DirectionMixin",
"collisions.DiscreteVoxelShapeMixin",
"collisions.EntityGetterMixin",
"collisions.EntityMixin",
"collisions.ExplosionMixin",
"collisions.LevelMixin",
"collisions.LivingEntityMixin",
"collisions.ServerEntityMixin",
"collisions.ShapesMixin",
"collisions.SliceShapeMixin",
"collisions.VoxelShapeMixin",
"command.CommandsMixin",
"config.MinecraftServerMixin",
"end_island.DensityFunctions$EndIslandDensityFunctionMixin",
"entity_tracker.ChunkMapMixin",
"entity_tracker.EntityMixin",
"entity_tracker.TrackedEntityMixin",
"fast_palette.CrudeIncrementalIntIdentityHashBiMapMixin",
"fast_palette.HashMapPaletteMixin",
"fast_palette.LinearPaletteMixin",
"fast_palette.PalettedContainer$DataMixin",
"fast_palette.PalettedContainerMixin",
"fast_palette.PaletteMixin",
"fast_palette.SingleValuePaletteMixin",
"fluid.FlowingFluidMixin",
"fluid.FluidStateMixin",
"fluid.MappedRegistryMixin",
"keep_alive_client.ServerGamePacketListenerImplMixin",
"mob_spawning.EntityTypeMixin",
"mob_spawning.LocalMobCapCalculator$MobCountsMixin",
"mob_spawning.MobSpawnSettingsMixin",
"mob_spawning.NaturalSpawnerMixin",
"poi_lookup.AcquirePoiMixin",
"poi_lookup.PoiManagerMixin",
"poi_lookup.PortalForcerMixin",
"random_ticking.BiomeManagerMixin",
"random_ticking.BiomeMixin",
"random_ticking.LevelMixin",
"random_ticking.ServerLevelMixin",
"serverlist.ConnectionMixin",
"starlight.blockstate.BlockStateBaseMixin",
"starlight.chunk.ChunkAccessMixin",
"starlight.chunk.EmptyLevelChunkMixin",
"starlight.chunk.ImposterProtoChunkMixin",
"starlight.chunk.LevelChunkMixin",
"starlight.chunk.ProtoChunkMixin",
"starlight.lightengine.LevelLightEngineMixin",
"starlight.lightengine.ThreadedLevelLightEngineMixin",
"starlight.world.ChunkSerializerMixin",
"starlight.world.WorldGenRegionMixin",
"util_thread_counts.UtilMixin",
"util_threading_detector.PalettedContainerMixin",
"util_threading_detector.ThreadingDetectorMixin",
"util_time_source.UtilMixin"
],
"client": [
"chunk_system.ClientLevelMixin",
"chunk_system.OptionsMixin",
"collisions.LiquidBlockRendererMixin",
"collisions.ParticleMixin",
"config.MinecraftMixin",
"loading_screen.LevelLoadStatusManagerMixin",
"profiler.MinecraftMixin",
"render.SectionRenderDispatcherMixin",
"serverlist.ClientConnectionMixin",
"serverlist.ServerAddressResolverMixin",
"serverlist.ServerSelectionListMixin",
"starlight.multiplayer.ClientPacketListenerMixin"
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"conformVisibility": true
}
}