Compare commits

...

25 Commits

Author SHA1 Message Date
Jason Penilla
ceb4936d9d 0.2.0-beta.2 2024-10-26 10:58:58 -07:00
Jason Penilla
3cb888e894 Update Fabric API and call ServerChunkEvents.CHUNK_GENERATE 2024-10-26 09:51:34 -07:00
Jason Penilla
7bedc1a7de Back to 0.2.0-SNAPSHOT 2024-10-24 11:54:52 -07:00
Jason Penilla
718f6e1369 0.2.0-beta.1 2024-10-24 11:48:32 -07:00
Spottedleaf
da9ab708a6 Import diff
The ChunkTaskScheduler one is actually needed in Paper to compile.
2024-10-24 11:39:02 -07:00
Spottedleaf
f22335f0b6 Move logic in anyPlayerCloseEnoughForSpawning overwrite to correct place
The internal function is responsible for the actual player iteration.
2024-10-24 10:00:23 -07:00
Spottedleaf
a3f2328000 Redirect chunk holder retrieval in ChunkMap#forEachSpawnCandidateChunk
The old chunk holder field is not maintained so this would
NPE.
2024-10-24 09:50:35 -07:00
Spottedleaf
529b9a44bb Add missing overwrite for DistanceManager#getTickingChunks 2024-10-24 09:37:16 -07:00
Spottedleaf
1e9a6504a1 Add world parameter to configAutoSaveInterval/configMaxAutoSavePerTick
Paper needs the world parameter to access the config values,
but in Moonrise we do not.
2024-10-24 08:29:57 -07:00
Spottedleaf
9c46dcbb94 Remove unused read/write methods on ChunkSystemSectionStorage
As with the last commit, these were only used in the legacy
region file I/O code and as such there is no reason to maintain
them.
2024-10-24 07:10:48 -07:00
Spottedleaf
9a1e04389a Adjust SectionStorageMixin to destroy hooks
In the legacy implementation of the region file I/O (before
the chunk system), these hooks were actually used. Now, they are not
and so there is no point in maintaining them.
2024-10-24 07:01:46 -07:00
Spottedleaf
29084d8e3f Properly sync on dimension data during ServerChunkCache#close
Note that this data was still saved, but we did not block until
it was finished.
2024-10-23 22:57:27 -07:00
Spottedleaf
41790ecf1a Implement overwrite for DistanceManager#getSpawnCandidateChunks 2024-10-23 22:53:34 -07:00
Spottedleaf
8af7bccdfd Adjust PlatformHooks#convertNBT to take TypeReference
DataFixTypes has a limited number of types, which will limit its
usage on Paper.
2024-10-23 22:10:41 -07:00
Spottedleaf
5f9b3571f8 Fix compile
Did not implement the other half of changes required in the last
commit...
2024-10-23 21:36:36 -07:00
Spottedleaf
9adfb2514d Change PlatformHooks#onChunkHolderTicketChange to take ChunkHolder
This makes compatibility on Paper easier to implement
as NewChunkHolder is not always available.
2024-10-23 21:33:54 -07:00
Jason Penilla
bf2cd1c571 fabric: fix crash when fabric-lifecycle-events-v1 not present 2024-10-23 18:23:42 -07:00
Jason Penilla
dad9a5c2eb Update to 1.21.3 and update cloth config api 2024-10-23 10:34:20 -07:00
Jason Penilla
a3acd46ee1 Fix explosion mixin
The field is a ServerLevel not a Level
2024-10-22 13:45:05 -07:00
Spottedleaf
3e8cb80336 Update to 1.21.2 2024-10-22 10:16:03 -07:00
Spottedleaf
5c3e713be7 Misc changes reviewing 1.21.2 update for Paper 2024-10-22 09:30:56 -07:00
Spottedleaf
284631c321 Use ThreadLocal in FlowingFluidMixin#COLLISION_OCCLUSION_CACHE
While the cache is safe to access from multiple threads, doing
so may incur false/true cache line sharing and reduce the
performance of the cache.
2024-10-22 09:30:54 -07:00
Spottedleaf
666c4cb1a3 Update to 1.21.2-rc1 2024-10-22 09:30:12 -07:00
Spottedleaf
19b523eecd Add async chunk writing
The RegionFile IO scheduler is now capable of taking in
a Completable<CompoundTag> for the chunk data instead of
CompoundTag. This allows writes to be scheduled without
the write value immediately.
2024-10-22 09:28:49 -07:00
Spottedleaf
4a748778dc Update to 1.21.2-pre1
For collisions, need to check out the new applyEffectsFromBlocks
function and see if there are any improvements we can make.

For the chunk system, we need to implement async chunk saving.
The current async unload structure will not work, as it is designed
to only handle 1 pending save at any given time.
2024-10-22 09:28:49 -07:00
94 changed files with 1593 additions and 1883 deletions

View File

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

View File

@@ -22,9 +22,10 @@ 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:11.0.1"
modImplementation "com.terraformersmc:modmenu:${rootProject.modmenu_version}"
modImplementation fabricApiLibs.fabric.api
modImplementation fabricApiLibs.command.api.v2
modImplementation fabricApiLibs.lifecycle.events.v1
include fabricApiLibs.command.api.v2
include fabricApiLibs.base
}

View File

@@ -2,44 +2,37 @@ package ca.spottedleaf.moonrise.fabric;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import ca.spottedleaf.moonrise.common.util.ConfigHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic;
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;
public final class FabricHooks implements PlatformHooks {
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);
}
}
);
private static final boolean HAS_FABRIC_LIFECYCLE_EVENTS = FabricLoader.getInstance().isModLoaded("fabric-lifecycle-events-v1");
@Override
public String getBrand() {
@@ -58,16 +51,6 @@ 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;
@@ -85,7 +68,12 @@ public final class FabricHooks implements PlatformHooks {
@Override
public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original) {
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel) newChunk.getLevel(), newChunk);
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad((ServerLevel)newChunk.getLevel(), newChunk);
if (!(original instanceof ImposterProtoChunk)) {
ServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate((ServerLevel)newChunk.getLevel(), newChunk);
}
}
}
@Override
@@ -94,17 +82,19 @@ public final class FabricHooks implements PlatformHooks {
}
@Override
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
}
@Override
public void chunkUnloadFromWorld(final LevelChunk chunk) {
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel) chunk.getLevel(), chunk);
if (HAS_FABRIC_LIFECYCLE_EVENTS) {
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload((ServerLevel)chunk.getLevel(), chunk);
}
}
@Override
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
}
@@ -176,12 +166,12 @@ public final class FabricHooks implements PlatformHooks {
}
@Override
public long configAutoSaveInterval() {
public long configAutoSaveInterval(final ServerLevel world) {
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
}
@Override
public int configMaxAutoSavePerTick() {
public int configMaxAutoSavePerTick(final ServerLevel world) {
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
}
@@ -189,4 +179,47 @@ 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.21.1",
"minecraft": ">1.21.1 <1.21.4",
"fabric-command-api-v2": "*"
},
"custom": {

View File

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

View File

@@ -3,9 +3,13 @@ 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 ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.serialization.Dynamic;
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;
@@ -13,16 +17,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;
@@ -52,16 +56,6 @@ 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;
@@ -88,10 +82,12 @@ public final class NeoForgeHooks implements PlatformHooks {
}
@Override
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel) {
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel) {
final ChunkPos pos = holder.getPos();
EventHooks.fireChunkTicketLevelUpdated(
world, CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ),
oldLevel, newLevel, holder.vanillaChunkHolder
world, CoordinateUtils.getChunkKey(pos.x, pos.z),
oldLevel, newLevel, holder
);
}
@@ -101,7 +97,7 @@ public final class NeoForgeHooks implements PlatformHooks {
}
@Override
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data) {
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data) {
NeoForge.EVENT_BUS.post(new ChunkDataEvent.Save(chunk, world, data));
}
@@ -193,12 +189,12 @@ public final class NeoForgeHooks implements PlatformHooks {
}
@Override
public long configAutoSaveInterval() {
public long configAutoSaveInterval(final ServerLevel world) {
return ConfigHolder.getConfig().chunkSaving.autoSaveInterval.getTimeTicks();
}
@Override
public int configMaxAutoSavePerTick() {
public int configMaxAutoSavePerTick(final ServerLevel world) {
return ConfigHolder.getConfig().chunkSaving.maxAutoSaveChunksPerTick;
}
@@ -206,4 +202,47 @@ 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.21.2)"
versionRange = "(1.21.1,1.21.4)"
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.295" apply false
id("xyz.jpenilla.quiet-architectury-loom") version "1.7.297" 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:0.103.0+1.21.1")
from("net.fabricmc.fabric-api:fabric-api-catalog:${fabric_api_version}")
}
}
}

View File

@@ -1,23 +1,25 @@
package ca.spottedleaf.moonrise.common;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
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;
@@ -33,10 +35,6 @@ 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);
@@ -47,11 +45,11 @@ public interface PlatformHooks {
public boolean allowAsyncTicketUpdates();
public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel);
public void onChunkHolderTicketChange(final ServerLevel world, final ChunkHolder holder, final int oldLevel, final int newLevel);
public void chunkUnloadFromWorld(final LevelChunk chunk);
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final CompoundTag data);
public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data);
public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player);
@@ -82,12 +80,30 @@ public interface PlatformHooks {
public int configPlayerMaxConcurrentGens();
public long configAutoSaveInterval();
public long configAutoSaveInterval(final ServerLevel world);
public int configMaxAutoSavePerTick();
public int configMaxAutoSavePerTick(final ServerLevel world);
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,6 +2,7 @@ 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;
@@ -14,6 +15,10 @@ 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();
chunk.postProcessGeneration((ServerLevel)chunk.getLevel());
}
((ServerLevel)chunk.getLevel()).startTickingChunk(chunk);
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();

View File

@@ -1,5 +1,6 @@
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;
@@ -13,7 +14,7 @@ public final class ConfigHolder {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigHolder.class);
private static final File CONFIG_FILE = new File(System.getProperty("Moonrise.ConfigFile", "config/moonrise.yml"));
private static final File CONFIG_FILE = new File(System.getProperty(PlatformHooks.get().getBrand() + ".ConfigFile", "config/moonrise.yml"));
private static final TypeAdapterRegistry CONFIG_ADAPTERS = new TypeAdapterRegistry();
private static final YamlConfig<MoonriseConfig> CONFIG;
static {
@@ -25,7 +26,7 @@ public final class ConfigHolder {
throw new RuntimeException(ex);
}
}
private static final String CONFIG_HEADER = """
private static final String CONFIG_HEADER = String.format("""
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
@@ -33,10 +34,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.
-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.
""";
-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());
static {
reloadConfig();

View File

@@ -3,8 +3,12 @@ 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("Moonrise Common Worker #" + this.idGenerator.getAndIncrement());
thread.setName(PlatformHooks.get().getBrand() + " 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("Moonrise.WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
int workerThreads = configWorkerThreads;

View File

@@ -1,8 +1,10 @@
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("Moonrise.MaxViewDistance", 32);
public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
private MoonriseConstants() {}

View File

@@ -39,7 +39,6 @@ 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.getMaxSection() - 1; // getMaxSection() is exclusive
return world.getMaxSectionY();
}
public static int getMaxSection(final Level world) {
return world.getMaxSection() - 1; // getMaxSection() is exclusive
return world.getMaxSectionY();
}
public static int getMinSection(final LevelHeightAccessor world) {
return world.getMinSection();
return world.getMinSectionY();
}
public static int getMinSection(final Level world) {
return world.getMinSection();
return world.getMinSectionY();
}
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,6 +6,7 @@ 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;
@@ -17,6 +18,9 @@ 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;
@@ -33,23 +37,6 @@ abstract class BooleanPropertyMixin extends Property<Boolean> implements Propert
)
)
private void init(final CallbackInfo ci) {
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;
this.moonrise$setById(BY_ID);
}
}

View File

@@ -18,9 +18,6 @@ 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,7 +83,6 @@ 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;
@@ -126,8 +125,8 @@ abstract class StateHolderMixin<O, S> implements PropertyAccessStateHolder {
* @author Spottedleaf
*/
@Overwrite
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));
public <T extends Comparable<T>> T getNullableValue(Property<T> property) {
return property == null ? null : this.optimisedTable.get(this.tableIndex, property);
}
/**

View File

@@ -66,9 +66,6 @@ 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;
@@ -123,16 +120,6 @@ 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];
@@ -300,14 +287,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
// no players to sent to, so don't need to update anything
return null;
}
final LevelChunk ret = this.getChunkToSend();
if (ret != null) {
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
return ret;
}
return ret;
return this.getChunkToSend();
}
/**
@@ -326,14 +306,7 @@ abstract class ChunkHolderMixin extends GenerationChunkHolder implements ChunkSy
// no players to sent to, so don't need to update anything
return null;
}
final LevelChunk ret = this.getChunkToSend();
if (ret != null) {
((ChunkSystemServerLevel)this.getChunkMap().level).moonrise$addUnsyncedChunk((ChunkHolder)(Object)this);
return ret;
}
return ret;
return this.getChunkToSend();
}
/**

View File

@@ -11,14 +11,17 @@ 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.ChunkTaskPriorityQueueSorter;
import net.minecraft.server.level.ChunkTaskDispatcher;
import net.minecraft.server.level.ChunkTrackingView;
import net.minecraft.server.level.GeneratingChunkMap;
import net.minecraft.server.level.GenerationChunkHolder;
@@ -28,7 +31,6 @@ 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;
@@ -78,13 +80,10 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
private volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap;
@Shadow
private ChunkTaskPriorityQueueSorter queueSorter;
private ChunkTaskDispatcher worldgenTaskDispatcher;
@Shadow
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> worldgenMailbox;
@Shadow
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> mainThreadMailbox;
private ChunkTaskDispatcher lightTaskDispatcher;
@Shadow
private int serverViewDistance;
@@ -98,6 +97,12 @@ 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);
}
@@ -128,11 +133,12 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
this.updatingChunkMap = null;
this.visibleChunkMap = null;
this.pendingUnloads = null;
this.queueSorter = null;
this.worldgenMailbox = null;
this.mainThreadMailbox = null;
this.worldgenTaskDispatcher = null;
this.lightTaskDispatcher = 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(
@@ -152,7 +158,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, (tag, throwable) -> {
MoonriseRegionFileIO.loadDataAsync(ChunkMapMixin.this.level, chunkPos.x, chunkPos.z, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, (final CompoundTag tag, final Throwable throwable) -> {
if (throwable != null) {
future.completeExceptionally(throwable);
} else {
@@ -184,6 +190,15 @@ 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
@@ -261,6 +276,15 @@ 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
@@ -309,6 +333,15 @@ 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
@@ -403,7 +436,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
* @author Spottedleaf
*/
@Overwrite
public void onChunkReadyToSend(final LevelChunk chunk) {
public void onChunkReadyToSend(final ChunkHolder holder, final LevelChunk chunk) {
throw new UnsupportedOperationException();
}
@@ -422,7 +455,7 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
* @see NewChunkHolder#save(boolean)
*/
@Overwrite
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder) {
public boolean saveChunkIfNeeded(final ChunkHolder chunkHolder, final long time) {
throw new UnsupportedOperationException();
}
@@ -505,6 +538,21 @@ 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<>();
@@ -524,10 +572,11 @@ abstract class ChunkMapMixin extends ChunkStorage implements ChunkSystemChunkMap
}
@Override
public CompletableFuture<Void> write(final ChunkPos pos, final CompoundTag tag) {
public CompletableFuture<Void> write(final ChunkPos pos, final Supplier<CompoundTag> tag) {
MoonriseRegionFileIO.scheduleSave(
this.level, pos.x, pos.z, tag,
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA);
this.level, pos.x, pos.z, tag.get(),
MoonriseRegionFileIO.RegionFileType.CHUNK_DATA
);
return null;
}

View File

@@ -22,12 +22,13 @@ 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
private IOWorker worker;
public IOWorker worker;
@Unique
private static final Logger LOGGER = LogUtils.getLogger();
@@ -118,13 +119,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;Lnet/minecraft/nbt/CompoundTag;)Ljava/util/concurrent/CompletableFuture;"
target = "Lnet/minecraft/world/level/chunk/storage/IOWorker;store(Lnet/minecraft/world/level/ChunkPos;Ljava/util/function/Supplier;)Ljava/util/concurrent/CompletableFuture;"
)
)
private CompletableFuture<Void> redirectWrite(final IOWorker instance, final ChunkPos chunkPos,
final CompoundTag compoundTag) {
final Supplier<CompoundTag> compoundTag) {
try {
this.storage.write(chunkPos, compoundTag);
this.storage.write(chunkPos, compoundTag.get());
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(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);
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);
}
/**
@@ -55,9 +55,8 @@ 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, Supplier<ProfilerFiller> supplier,
LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci) {
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) {
this.entityStorage = null;
this.moonrise$setEntityLookup(new ClientEntityLookup(this, ((ClientLevel)(Object)this).new EntityCallbacks()));

View File

@@ -8,13 +8,12 @@ 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;
@@ -45,13 +44,7 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
Set<ChunkHolder> chunksToUpdateFutures;
@Shadow
ChunkTaskPriorityQueueSorter ticketThrottler;
@Shadow
ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> ticketThrottlerInput;
@Shadow
ProcessorHandle<ChunkTaskPriorityQueueSorter.Release> ticketThrottlerReleaser;
ThrottlingChunkTaskDispatcher ticketDispatcher;
@Shadow
LongSet ticketsToRelease;
@@ -59,12 +52,10 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
@Shadow
Executor mainThreadExecutor;
@Shadow
private DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter;
@Shadow
private int simulationDistance;
@Override
public ChunkMap moonrise$getChunkMap() {
throw new AbstractMethodError();
@@ -86,16 +77,14 @@ abstract class DistanceManagerMixin implements ChunkSystemDistanceManager {
this.tickingTicketsTracker = null;
this.playerTicketManager = null;
this.chunksToUpdateFutures = null;
this.ticketThrottler = null;
this.ticketThrottlerInput = null;
this.ticketThrottlerReleaser = null;
this.ticketDispatcher = null;
this.ticketsToRelease = null;
this.mainThreadExecutor = null;
this.simulationDistance = -1;
}
@Override
public ChunkHolderManager moonrise$getChunkHolderManager() {
public final ChunkHolderManager moonrise$getChunkHolderManager() {
return ((ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getChunkTaskScheduler().chunkHolderManager;
}
@@ -326,6 +315,15 @@ 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,15 +195,6 @@ 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,11 +93,15 @@ abstract class LevelChunkMixin extends ChunkAccess implements ChunkSystemLevelCh
}
@Override
public void setUnsaved(final boolean needsSaving) {
if (!needsSaving) {
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
public boolean tryMarkSaved() {
if (!this.isUnsaved()) {
return false;
}
super.setUnsaved(needsSaving);
((ChunkSystemLevelChunkTicks)this.blockTicks).moonrise$clearDirty();
((ChunkSystemLevelChunkTicks)this.fluidTicks).moonrise$clearDirty();
super.tryMarkSaved();
return true;
}
}

View File

@@ -10,6 +10,7 @@ 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;
@@ -37,9 +38,6 @@ 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);
@@ -59,7 +57,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
}
@Override
public void moonrise$setEntityLookup(final EntityLookup entityLookup) {
public final void moonrise$setEntityLookup(final EntityLookup entityLookup) {
if (this.entityLookup != null && !(this.entityLookup instanceof DefaultEntityLookup)) {
throw new IllegalStateException("Entity lookup already initialised");
}
@@ -87,7 +85,7 @@ abstract class LevelMixin implements ChunkSystemLevel, ChunkSystemEntityGetter,
@Overwrite
@Override
public List<Entity> getEntities(final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate) {
this.getProfiler().incrementCounter("getEntities");
Profiler.get().incrementCounter("getEntities");
final List<Entity> ret = new ArrayList<>();
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entity, boundingBox, ret, predicate);
@@ -105,7 +103,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) {
this.getProfiler().incrementCounter("getEntities");
Profiler.get().incrementCounter("getEntities");
if (entityTypeTest instanceof EntityType<T> byType) {
if (maxCount != Integer.MAX_VALUE) {
@@ -178,7 +176,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) {
this.getProfiler().incrementCounter("getEntities");
Profiler.get().incrementCounter("getEntities");
final List<T> ret = new ArrayList<>();
((ChunkSystemLevel)this).moonrise$getEntityLookup().getEntities(entityClass, null, boundingBox, ret, predicate);
@@ -196,7 +194,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) {
this.getProfiler().incrementCounter("getEntities");
Profiler.get().incrementCounter("getEntities");
final List<Entity> ret = new ArrayList<>();
((ChunkSystemLevel)this).moonrise$getEntityLookup().getHardCollidingEntities(entity, box, ret, predicate);

View File

@@ -240,6 +240,7 @@ 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,6 +36,7 @@ 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;
@@ -44,7 +45,14 @@ 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> implements ChunkSystemPoiManager {
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);
}
@Shadow
abstract boolean isVillageCenter(long l);
@@ -52,10 +60,6 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
@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;
@@ -151,8 +155,8 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
final ChunkHolderManager manager = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager;
final PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
final PoiChunk ret = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
.getPoiChunkIfLoaded(chunkX, chunkZ, true);
return ret == null ? Optional.empty() : (Optional)ret.getSectionForVanilla(chunkY);
}
@@ -206,9 +210,13 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
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 section = this.levelHeightAccessor.getMinSection(); section < this.levelHeightAccessor.getMaxSection(); ++section) {
final long sectionPos = SectionPos.asLong(chunkX, section, chunkZ);
for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
final long sectionPos = SectionPos.asLong(chunkX, sectionY, chunkZ);
this.updateDistanceTracking(sectionPos);
}
}
@@ -217,8 +225,12 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
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 = this.levelHeightAccessor.getMinSection(); sectionY < this.levelHeightAccessor.getMaxSection(); ++sectionY) {
for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
final PoiSection section = poiChunk.getSection(sectionY);
if (section != null && !((ChunkSystemPoiSection)section).moonrise$isEmpty()) {
this.onSectionLoad(SectionPos.asLong(chunkX, sectionY, chunkZ));
@@ -254,20 +266,4 @@ public abstract class PoiManagerMixin extends SectionStorage<Object> implements
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,8 +2,6 @@ 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;
@@ -23,15 +21,11 @@ import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Mixin(SectionStorage.class)
abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoCloseable {
abstract class SectionStorageMixin<R, P> implements ChunkSystemSectionStorage, AutoCloseable {
@Shadow
private SimpleRegionStorage simpleRegionStorage;
@Shadow
@Final
private static Logger LOGGER;
@Unique
private RegionFileStorage storage;
@@ -41,6 +35,9 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
return this.storage;
}
@Override
public void moonrise$close() throws IOException {}
/**
* @reason Retrieve storage from IOWorker, and then nuke it
* @author Spottedleaf
@@ -61,12 +58,8 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
* @author Spottedleaf
*/
@Overwrite
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);
}
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());
}
/**
@@ -74,29 +67,17 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
* @author Spottedleaf
*/
@Overwrite
public void readColumn(final ChunkPos pos, final RegistryOps<Tag> ops, final CompoundTag data) {
public void unpackChunk(final ChunkPos chunkPos, final SectionStorage.PackedChunk<P> packedChunk) {
throw new IllegalStateException("Only chunk system can load in state, offending class:" + this.getClass().getName());
}
/**
* @reason Route to new chunk system hook
* @reason Destroy old chunk system hook
* @author Spottedleaf
*/
@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;
@Overwrite
private void writeChunk(final ChunkPos chunkPos) {
throw new IllegalStateException("Only chunk system can write state, offending class:" + this.getClass().getName());
}
/**
@@ -113,4 +94,4 @@ abstract class SectionStorageMixin implements ChunkSystemSectionStorage, AutoClo
private void redirectClose(final SimpleRegionStorage instance) throws IOException {
this.moonrise$close();
}
}
}

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.ChunkSerializer;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ChunkSerializer.class)
abstract class ChunkSerializerMixin {
@Mixin(SerializableChunkData.class)
abstract class SerializableChunkDataMixin {
/**
* @reason Chunk system handles this during full transition
@@ -17,12 +17,12 @@ abstract class ChunkSerializerMixin {
* @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 static void skipConsistencyCheck(PoiManager instance, SectionPos sectionPos, LevelChunkSection levelChunkSection) {}
private void skipConsistencyCheck(final PoiManager instance, final SectionPos sectionPos, final LevelChunkSection levelChunkSection) {}
}

View File

@@ -24,6 +24,7 @@ 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;
@@ -51,6 +52,10 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
@Final
public ServerLevel level;
@Shadow
@Final
private DimensionDataStorage dataStorage;
@Unique
private final ConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new ConcurrentLong2ReferenceChainedHashTable<>();
@@ -261,6 +266,7 @@ 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);
}
@@ -314,7 +320,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
* @author Spottedleaf
*/
@Inject(
method = "tickChunks",
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
at = @At(
value = "INVOKE",
shift = At.Shift.AFTER,
@@ -335,60 +341,13 @@ abstract class ServerChunkCacheMixin extends ChunkSource implements ChunkSystemS
* @author Spottedleaf
*/
@Redirect(
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"
)
method = "tickChunks(Lnet/minecraft/util/profiling/ProfilerFiller;JLjava/util/List;)V",
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(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);
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);
}
@Unique
@@ -123,9 +123,6 @@ 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);
@@ -135,9 +132,6 @@ 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
@@ -354,45 +348,6 @@ 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

@@ -85,11 +85,20 @@ abstract class ChunkMapMixin {
}
/**
* @reason Use nearby players to avoid iterating over all online players
* @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) {
final ReferenceList<ServerPlayer> players = ((ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers().getPlayers(
pos, NearbyPlayers.NearbyMapType.SPAWN_RANGE
);

View File

@@ -4,6 +4,7 @@ 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;
@@ -106,4 +107,13 @@ 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,8 +7,6 @@ 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;
@@ -18,22 +16,15 @@ 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 {
@@ -42,110 +33,18 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
@Final
public ServerLevel level;
@Unique
private ServerChunkCache.ChunkAndHolder[] iterationCopy;
@Shadow
@Final
public ChunkMap chunkMap;
@Unique
private int iterationCopyLen;
@Unique
private final SimpleRandom shuffleRandom = new SimpleRandom(0L);
/**
* @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;
@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;
final NearbyPlayers.TrackedChunk nearbyPlayers = chunkData.nearbyPlayers;
if (nearbyPlayers == null) {
return false;
@@ -162,7 +61,7 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
Objects.checkFromIndexSize(0, len, raw.length);
for (int i = 0; i < len; ++i) {
if (instance.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
if (chunkMap.playerIsCloseEnoughForSpawning(raw[i], chunkPos)) {
return true;
}
}
@@ -171,19 +70,47 @@ abstract class ServerChunkCacheMixin extends ChunkSource {
}
/**
* @reason Clear the iteration array after the list is done being used.
* @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)
* @author Spottedleaf
*/
@Inject(
method = "tickChunks",
at = @At(
value = "INVOKE",
target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V",
ordinal = 0,
shift = At.Shift.AFTER
)
@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"
)
)
private void broadcastChanges(final CallbackInfo ci) {
Arrays.fill(this.iterationCopy, 0, this.iterationCopyLen, null);
private <T> void useBetterRandom(final List<T> list, final RandomSource randomSource) {
this.shuffleRandom.setSeed(randomSource.nextLong());
Util.shuffle(list, this.shuffleRandom);
}
}

View File

@@ -6,37 +6,32 @@ 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 org.spongepowered.asm.mixin.Final;
import net.minecraft.world.phys.AABB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
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 this method by making it use the class lookup
* @reason Optimise by making it use the class lookup
* @author Spottedleaf
*/
@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);
}
}
@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);
}
}

View File

@@ -34,6 +34,12 @@ 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);
}
@@ -74,7 +80,7 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
}
if (neighbours) {
for (final Direction direction : DIRECTIONS_CACHED) {
initCaches(Shapes.getFaceShape(shape, direction), false);
initCaches(((CollisionVoxelShape)shape).moonrise$getFaceShapeClamped(direction), false);
initCaches(shape.getFaceShape(direction), false);
}
}
@@ -107,17 +113,21 @@ 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,20 +1,16 @@
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;
@@ -27,11 +23,8 @@ 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 {
@@ -57,12 +50,6 @@ 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) {
@@ -296,163 +283,4 @@ 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

@@ -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.getNearest(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.getApproximateNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
}
@Unique

View File

@@ -4,9 +4,7 @@ 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;
@@ -23,12 +21,7 @@ abstract class LiquidBlockRendererMixin {
* @author Spottedleaf
*/
@Overwrite
private static boolean isFaceOccludedByState(final BlockGetter world, final Direction direction, final float height,
final BlockPos pos, final BlockState state) {
if (!state.canOcclude()) {
return false;
}
private static boolean isFaceOccludedByState(final Direction direction, final float height, final BlockState state) {
// check for created shape is empty
if (height < (float)CollisionUtil.COLLISION_EPSILON) {
return false;
@@ -50,25 +43,26 @@ 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 stateShape = ((CollisionVoxelShape)state.getOcclusionShape(world, pos)).moonrise$getFaceShapeClamped(direction.getOpposite());
final VoxelShape occlusionShape = ((CollisionVoxelShape)state.getFaceOcclusionShape(direction.getOpposite()))
.moonrise$getFaceShapeClamped(direction.getOpposite());
if (stateShape.isEmpty()) {
if (occlusionShape.isEmpty()) {
// cannot occlude
return false;
}
// fast check for box
if (heightShape == stateShape) {
if (heightShape == occlusionShape) {
return true;
}
return !Shapes.joinIsNotEmpty(heightShape, stateShape, BooleanOp.ONLY_FIRST);
return !Shapes.joinIsNotEmpty(heightShape, occlusionShape, BooleanOp.ONLY_FIRST);
}
}

View File

@@ -1,55 +0,0 @@
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,8 +79,7 @@ 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

@@ -1,6 +1,5 @@
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;
@@ -10,20 +9,15 @@ 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.Level;
import net.minecraft.world.level.ServerExplosion;
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;
@@ -33,33 +27,20 @@ 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(Explosion.class)
abstract class ExplosionMixin {
@Mixin(ServerExplosion.class)
abstract class ServerExplosionMixin {
@Shadow
@Final
private Level level;
@Shadow
@Final
private Entity source;
@Shadow
@Final
private double x;
@Shadow
@Final
private double y;
@Shadow
@Final
private double z;
private ServerLevel level;
@Shadow
@Final
@@ -69,21 +50,13 @@ abstract class ExplosionMixin {
@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 DamageSource damageSource;
private Vec3 center;
@Unique
@@ -142,6 +115,12 @@ abstract class ExplosionMixin {
@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) {
@@ -343,29 +322,45 @@ abstract class ExplosionMixin {
return (float)missedRays / (float)totalRays;
}
/**
* @reason Rewrite ray casting and seen fraction calculation for performance
* @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
* @author Spottedleaf
*/
@Overwrite
public void explode() {
this.level.gameEvent(this.source, GameEvent.EXPLODE, new Vec3(this.x, this.y, this.z));
public List<BlockPos> calculateExplodedPositions() {
final ObjectArrayList<BlockPos> ret = new ObjectArrayList<>();
this.blockCache = new Long2ObjectOpenHashMap<>();
final Vec3 center = this.center;
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];
final ExplosionBlockCache[] blockCache = this.directMappedBlockCache;
// use initial cache value that is most likely to be used: the source position
final ExplosionBlockCache initialCache;
{
final int blockX = Mth.floor(this.x);
final int blockY = Mth.floor(this.y);
final int blockZ = Mth.floor(this.z);
final int blockX = Mth.floor(center.x);
final int blockY = Mth.floor(center.y);
final int blockZ = Mth.floor(center.z);
final long key = BlockPos.asLong(blockX, blockY, blockZ);
@@ -381,9 +376,9 @@ abstract class ExplosionMixin {
for (int ray = 0, len = CACHED_RAYS.length; ray < len;) {
ExplosionBlockCache cachedBlock = initialCache;
double currX = this.x;
double currY = this.y;
double currZ = this.z;
double currX = center.x;
double currY = center.y;
double currZ = center.z;
final double incX = CACHED_RAYS[ray];
final double incY = CACHED_RAYS[ray + 1];
@@ -402,7 +397,7 @@ abstract class ExplosionMixin {
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];
@@ -424,7 +419,7 @@ abstract class ExplosionMixin {
cachedBlock.shouldExplode = shouldExplode ? Boolean.TRUE : Boolean.FALSE;
if (shouldExplode) {
if (this.fire || !cachedBlock.blockState.isAir()) {
this.toBlow.add(cachedBlock.immutablePos);
ret.add(cachedBlock.immutablePos);
}
}
}
@@ -436,83 +431,39 @@ abstract class ExplosionMixin {
} while (power > 0.0f);
}
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)),
return ret;
}
(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 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);
}
/**
* @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

@@ -278,15 +278,6 @@ 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,9 +1,7 @@
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;
@@ -22,7 +20,7 @@ abstract class SliceShapeMixin {
value = "RETURN"
)
)
private void initState(final VoxelShape parent, final Direction.Axis forAxis, final int forIndex, final CallbackInfo ci) {
private void initState(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.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
return new BlockHitResult(fromBehind, Direction.getApproximateNearest(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.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
return new BlockHitResult(fromBehind, Direction.getApproximateNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
}
return AABB.clip(((VoxelShape)(Object)this).toAabbs(), from, to, offset);

View File

@@ -1,5 +1,6 @@
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;
@@ -144,8 +145,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);
@@ -154,8 +155,9 @@ 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, passengers.get(i).getType().clientTrackingRange() << 4);
range = Math.max(range, PlatformHooks.get().modifyEntityTrackingRange(passenger, passenger.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 FluidOcclusionCacheKey[] COLLISION_OCCLUSION_CACHE = new FluidOcclusionCacheKey[COLLISION_OCCLUSION_CACHE_SIZE];
private static final ThreadLocal<FluidOcclusionCacheKey[]> COLLISION_OCCLUSION_CACHE = ThreadLocal.withInitial(() -> 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
private boolean canPassThroughWall(final Direction direction, final BlockGetter level,
final BlockPos fromPos, final BlockState fromState,
final BlockPos toPos, final BlockState toState) {
public static 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 : null;
COLLISION_OCCLUSION_CACHE.get() : 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 void moonrise$initCaches() {
public final 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 minBuildHeight;
private int maxY;
@Unique
private int maxBuildHeight;
private int minSectionY;
@Unique
private int minSection;
@Unique
private int maxSection;
private int maxSectionY;
@Unique
private int sectionsCount;
@@ -47,12 +47,17 @@ 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.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;
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;
}
@Override
@@ -61,52 +66,52 @@ abstract class LevelMixin implements LevelAccessor, AutoCloseable {
}
@Override
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;
public int getMaxY() {
return this.maxY;
}
@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,16 +20,17 @@ 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> {
abstract class PoiManagerMixin extends SectionStorage<PoiSection, PoiSection.Packed> {
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);
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);
}
/**

View File

@@ -49,7 +49,7 @@ abstract class MinecraftMixin extends ReentrantBlockableEventLoop<Runnable> impl
return;
}
cir.setReturnValue(ret == null || ret == InactiveProfiler.INSTANCE ? this.leafProfiler : ProfilerFiller.tee(this.leafProfiler, ret));
cir.setReturnValue(ret == null || ret == InactiveProfiler.INSTANCE ? this.leafProfiler : ProfilerFiller.combine(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);
protected abstract float getHeightAdjustedTemperature(BlockPos blockPos, int seaLevel);
/**
* @reason Cache appears ineffective
* @author Spottedleaf
*/
@Overwrite
public float getTemperature(final BlockPos pos) {
return this.getHeightAdjustedTemperature(pos);
public float getTemperature(final BlockPos pos, final int seaLevel) {
return this.getHeightAdjustedTemperature(pos, seaLevel);
}
}

View File

@@ -12,7 +12,6 @@ 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;
@@ -27,13 +26,12 @@ 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(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);
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);
}
@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,6 +19,10 @@ 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;
@@ -27,29 +31,26 @@ 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,11 +2,16 @@ 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.registries.Registries;
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.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;
@@ -18,8 +23,8 @@ abstract class ImposterProtoChunkMixin extends ProtoChunk implements StarlightCh
@Shadow
private LevelChunk wrapped;
public ImposterProtoChunkMixin(final LevelChunk levelChunk, final boolean bl) {
super(levelChunk.getPos(), UpgradeData.EMPTY, levelChunk, levelChunk.getLevel().registryAccess().registryOrThrow(Registries.BIOME), levelChunk.getBlendingData());
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);
}
@Override

View File

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

View File

@@ -10,13 +10,12 @@ 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.ChunkTaskPriorityQueueSorter;
import net.minecraft.server.level.ChunkTaskDispatcher;
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.ProcessorHandle;
import net.minecraft.util.thread.ProcessorMailbox;
import net.minecraft.util.thread.ConsecutiveExecutor;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess;
@@ -50,10 +49,10 @@ import java.util.function.Supplier;
abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements StarLightLightingProvider {
@Shadow
private ProcessorMailbox<Runnable> taskMailbox;
private ConsecutiveExecutor consecutiveExecutor;
@Shadow
private ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> sorterMailbox;
private ChunkTaskDispatcher taskDispatcher;
public ThreadedLevelLightEngineMixin(final LightChunkGetter chunkProvider, final boolean hasBlockLight, final boolean hasSkyLight) {
super(chunkProvider, hasBlockLight, hasSkyLight);
@@ -184,8 +183,8 @@ abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine implements
)
)
private void initHook(final CallbackInfo ci) {
this.taskMailbox = null;
this.sorterMailbox = null;
this.consecutiveExecutor = null;
this.taskDispatcher = null;
}
/**
@@ -194,7 +193,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,7 +40,8 @@ abstract class ClientPacketListenerMixin implements ClientGamePacketListener {
*/
@Shadow
protected abstract void applyLightData(final int chunkX, final int chunkZ, final ClientboundLightUpdatePacketData clientboundLightUpdatePacketData);
protected abstract void applyLightData(final int chunkX, final int chunkZ, final ClientboundLightUpdatePacketData clientboundLightUpdatePacketData,
final boolean markDirty);
@Shadow
protected abstract void enableChunkLight(final LevelChunk levelChunk, final int chunkX, final int chunkZ);
@@ -127,7 +128,7 @@ abstract class ClientPacketListenerMixin implements ClientGamePacketListener {
return;
}
// load in light data from packet immediately
this.applyLightData(chunkX, chunkZ, clientboundLevelChunkWithLightPacket.getLightData());
this.applyLightData(chunkX, chunkZ, clientboundLevelChunkWithLightPacket.getLightData(), false);
((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

@@ -1,45 +0,0 @@
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

@@ -0,0 +1,36 @@
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

@@ -0,0 +1,255 @@
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,10 +1,11 @@
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.DataFixTypes;
import net.minecraft.util.datafix.fixes.References;
public final class ChunkSystemConverters {
@@ -25,13 +26,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 DataFixTypes.POI_CHUNK.update(world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
return PlatformHooks.get().convertNBT(References.POI_CHUNK, 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 DataFixTypes.ENTITY_CHUNK.update(world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
return PlatformHooks.get().convertNBT(References.ENTITY_CHUNK, world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
}
private ChunkSystemConverters() {}

View File

@@ -1,36 +0,0 @@
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

@@ -1,11 +0,0 @@
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,6 +1,8 @@
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;
@@ -12,6 +14,7 @@ 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;
@@ -23,14 +26,12 @@ 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 {
@@ -215,30 +216,6 @@ 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.
@@ -444,27 +421,106 @@ 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 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
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
// create task
final ChunkIOTask newTask = new ChunkIOTask(
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead(), data
);
created[0] = true;
// create task
final ChunkIOTask newTask = new ChunkIOTask(
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead()
);
return newTask;
newTask.pushPendingWrite(write);
created[0] = true;
return newTask;
}
taskRunning.pushPendingWrite(write);
return taskRunning;
}
);
taskRunning.inProgressWrite = data;
return taskRunning;
});
write.schedule(task, scheduler);
if (created[0]) {
taskController.startTask(task);
@@ -711,7 +767,7 @@ public final class MoonriseRegionFileIO {
// set up task
final ChunkIOTask newTask = new ChunkIOTask(
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead(), ChunkIOTask.NOTHING_TO_WRITE
world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead()
);
newTask.inProgressRead.addToAsyncWaiters(onComplete);
@@ -719,22 +775,34 @@ public final class MoonriseRegionFileIO {
return newTask;
}
final CompoundTag pendingWrite = running.inProgressWrite;
final ChunkIOTask.InProgressWrite pendingWrite = running.inProgressWrite;
if (pendingWrite == ChunkIOTask.NOTHING_TO_WRITE) {
if (pendingWrite == null) {
// 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
callbackInfo.data = pendingWrite;
callbackInfo.throwable = null;
callbackInfo.completeNow = true;
if (!pendingWrite.addToAsyncWaiters(onComplete)) {
// data is ready now
callbackInfo.data = pendingWrite.value;
callbackInfo.throwable = pendingWrite.throwable;
callbackInfo.completeNow = true;
return running;
}
callbackInfo.write = pendingWrite;
return running;
};
@@ -755,7 +823,7 @@ public final class MoonriseRegionFileIO {
ret.raisePriority(priority);
}
return new CancellableRead(onComplete, ret);
return new CancellableRead(onComplete, callbackInfo.read, callbackInfo.write);
}
private static final class ImmediateCallbackCompletion {
@@ -764,6 +832,8 @@ public final class MoonriseRegionFileIO {
private Throwable throwable;
private boolean completeNow;
private boolean tasksNeedReadScheduling;
private ChunkIOTask.InProgressRead read;
private ChunkIOTask.InProgressWrite write;
}
@@ -802,26 +872,40 @@ public final class MoonriseRegionFileIO {
private static final class CancellableRead implements Cancellable {
private BiConsumer<CompoundTag, Throwable> callback;
private ChunkIOTask task;
private ChunkIOTask.InProgressRead read;
private ChunkIOTask.InProgressWrite write;
private CancellableRead(final BiConsumer<CompoundTag, Throwable> callback, final ChunkIOTask task) {
private CancellableRead(final BiConsumer<CompoundTag, Throwable> callback,
final ChunkIOTask.InProgressRead read,
final ChunkIOTask.InProgressWrite write) {
this.callback = callback;
this.task = task;
this.read = read;
this.write = write;
}
@Override
public boolean cancel() {
final BiConsumer<CompoundTag, Throwable> callback = this.callback;
final ChunkIOTask task = this.task;
final ChunkIOTask.InProgressRead read = this.read;
final ChunkIOTask.InProgressWrite write = this.write;
if (callback == null || task == null) {
if (callback == null || (read == null && write == null)) {
return false;
}
this.callback = null;
this.task = null;
return task.inProgressRead.cancel(callback);
this.read = null;
this.write = null;
if (read != null) {
return read.cancel(callback);
}
if (write != null) {
return write.cancel(callback);
}
// unreachable
throw new InternalError();
}
}
@@ -854,8 +938,6 @@ 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;
@@ -864,22 +946,21 @@ public final class MoonriseRegionFileIO {
private PrioritisedExecutor.PrioritisedTask currentTask;
private final InProgressRead inProgressRead;
private volatile CompoundTag inProgressWrite;
private volatile InProgressWrite inProgressWrite;
private final ReferenceOpenHashSet<InProgressWrite> allPendingWrites = new ReferenceOpenHashSet<>();
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 CompoundTag inProgressWrite) {
final int chunkX, final int chunkZ, final Priority priority, final InProgressRead inProgressRead) {
this.world = world;
this.regionDataController = regionDataController;
this.chunkX = chunkX;
this.chunkZ = chunkZ;
this.priority = priority;
this.inProgressRead = inProgressRead;
this.inProgressWrite = inProgressWrite;
}
public Priority getPriority() {
@@ -888,16 +969,26 @@ 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.priority = priority;
if (this.currentTask != null) {
this.currentTask.setPriority(priority);
}
this.updatePriority(priority);
return true;
}
@@ -909,10 +1000,7 @@ public final class MoonriseRegionFileIO {
return false;
}
this.priority = priority;
if (this.currentTask != null) {
this.currentTask.setPriority(priority);
}
this.updatePriority(priority);
return true;
}
@@ -924,15 +1012,28 @@ public final class MoonriseRegionFileIO {
return false;
}
this.priority = priority;
if (this.currentTask != null) {
this.currentTask.setPriority(priority);
}
this.updatePriority(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) {
@@ -964,7 +1065,7 @@ public final class MoonriseRegionFileIO {
canRead[0] = false;
}
if (valueInMap.inProgressWrite != NOTHING_TO_WRITE) {
if (valueInMap.inProgressWrite != null) {
return valueInMap;
}
@@ -1050,8 +1151,7 @@ public final class MoonriseRegionFileIO {
this.finishRead(compoundTag, throwable);
if (!this.tryAbortWrite()) {
// we are already on the compression executor, don't bother scheduling
this.performWriteCompress();
this.scheduleWriteCompress();
}
}
@@ -1060,17 +1160,24 @@ public final class MoonriseRegionFileIO {
}
public void scheduleWriteCompress() {
final InProgressWrite inProgressWrite = this.inProgressWrite;
final PrioritisedExecutor.PrioritisedTask task;
synchronized (this) {
task = this.regionDataController.compressionExecutor.createTask(this::performWriteCompress, this.priority);
task = this.regionDataController.compressionExecutor.createTask(() -> {
ChunkIOTask.this.performWriteCompress(inProgressWrite);
}, this.priority);
this.currentTask = task;
}
task.queue();
inProgressWrite.addToWaiters(this, (final CompoundTag data, final Throwable throwable) -> {
task.queue();
});
}
private boolean tryAbortWrite() {
final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
if (this.inProgressWrite == NOTHING_TO_WRITE) {
if (this.inProgressWrite == null) {
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!");
@@ -1079,7 +1186,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 != NOTHING_TO_WRITE) {
if (valueInMap.inProgressWrite != null) {
return valueInMap;
}
@@ -1095,61 +1202,62 @@ public final class MoonriseRegionFileIO {
return false;
}
private void performWriteCompress() {
for (;;) {
final CompoundTag write = this.inProgressWrite;
if (write == NOTHING_TO_WRITE) {
throw new IllegalStateException("Should be writable");
}
private void performWriteCompress(final InProgressWrite inProgressWrite) {
final CompoundTag write = inProgressWrite.value;
if (!inProgressWrite.isComplete()) {
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(write, failedWrite)) {
return;
} else {
// fetch new data and try again
continue;
}
if (this.tryCompleteWrite(inProgressWrite, failedWrite)) {
return;
} else {
// writeData != null && !failedWrite
// we can continue to I/O stage
this.writeData = writeData;
this.scheduleWriteIO();
// fetch new data and try again
this.scheduleWriteCompress();
return;
}
} else {
// writeData != null && !failedWrite
// we can continue to I/O stage
this.writeData = writeData;
this.scheduleWriteIO(inProgressWrite);
return;
}
}
private void scheduleWriteIO() {
private void scheduleWriteIO(final InProgressWrite inProgressWrite) {
final PrioritisedExecutor.PrioritisedTask task;
synchronized (this) {
task = this.regionDataController.ioScheduler.createTask(this.chunkX, this.chunkZ, this::runWriteIO, this.priority);
task = this.regionDataController.ioScheduler.createTask(this.chunkX, this.chunkZ, () -> {
ChunkIOTask.this.runWriteIO(inProgressWrite);
}, this.priority);
this.currentTask = task;
}
task.queue();
}
private void runWriteIO() {
private void runWriteIO(final InProgressWrite inProgressWrite) {
RegionDataController.WriteData writeData = this.writeData;
this.writeData = null;
@@ -1162,14 +1270,14 @@ public final class MoonriseRegionFileIO {
LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
}
if (!this.tryCompleteWrite(writeData.input(), failedWrite)) {
if (!this.tryCompleteWrite(inProgressWrite, failedWrite)) {
// fetch new data and try again
this.scheduleWriteCompress();
}
return;
}
private boolean tryCompleteWrite(final CompoundTag written, final boolean failedWrite) {
private boolean tryCompleteWrite(final InProgressWrite written, final boolean failedWrite) {
final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
final boolean[] done = new boolean[] { false };
@@ -1216,14 +1324,6 @@ 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);
}
@@ -1241,11 +1341,72 @@ 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 for task " + task.toString(), thr);
LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data (read) 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,12 +61,4 @@ 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,8 +23,4 @@ public interface ChunkSystemChunkHolder {
public LevelChunk moonrise$getFullChunk();
public boolean moonrise$isMarkedDirtyForPlayers();
public void moonrise$markDirtyForPlayers(final boolean value);
}

View File

@@ -1,5 +1,6 @@
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;
@@ -14,6 +15,7 @@ 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;
@@ -74,7 +76,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).collect(ImmutableList.toImmutableList());
return EntityType.loadEntitiesRecursive(compoundTag.getList("Entities", 10), world, EntitySpawnReason.LOAD).collect(ImmutableList.toImmutableList());
}
// Paper start - rewrite chunk system
@@ -102,7 +104,7 @@ public final class ChunkEntitySlices {
}
final ListTag entitiesTag = new ListTag();
for (final Entity entity : entities) {
for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x, chunkPos.z, entities)) {
CompoundTag compoundTag = new CompoundTag();
if (entity.save(compoundTag)) {
entitiesTag.add(compoundTag);
@@ -149,12 +151,12 @@ public final class ChunkEntitySlices {
continue;
}
if (entity.shouldBeSaved()) {
entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
PlatformHooks.get().unloadEntity(entity);
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()) {
passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
PlatformHooks.get().unloadEntity(passenger);
}
}
}
@@ -163,7 +165,7 @@ public final class ChunkEntitySlices {
return this.entities.size() != 0;
}
private List<Entity> getAllEntities() {
public 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);
ChunkEntitySlices ret;
final 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"); // Paper - rewrite chunk system
EntityLookup.this.checkThread(entity, "Cannot remove entity off-main");
final Visibility tickingState = EntityLookup.getEntityStatus(entity);
EntityLookup.this.removeEntity(entity);

View File

@@ -3,7 +3,6 @@ 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;
@@ -123,7 +122,6 @@ 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;
@@ -133,13 +131,8 @@ public final class PoiChunk {
continue;
}
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);
// I do not believe asynchronously converting to CompoundTag is worth the scheduling.
final DataResult<Tag> serializedResult = PoiSection.Packed.CODEC.encodeStart(registryOps, section.pack());
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);
@@ -183,19 +176,18 @@ public final class PoiChunk {
continue;
}
final long coordinateKey = 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(coordinateKey);
});
final CompoundTag section = sections.getCompound(key);
final DataResult<PoiSection> deserializeResult = codec.parse(registryOps, section);
final DataResult<PoiSection.Packed> deserializeResult = PoiSection.Packed.CODEC.parse(registryOps, section);
final int finalSectionY = sectionY;
final PoiSection deserialized = deserializeResult.resultOrPartial((final String description) -> {
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(() -> {
poiManager.setDirty(coordinateKey);
});
if (deserialized == null || ((ChunkSystemPoiSection)deserialized).moonrise$isEmpty()) {
// completely empty, no point in storing this
continue;

View File

@@ -1,19 +1,10 @@
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,7 +6,6 @@ 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;
@@ -16,13 +15,10 @@ 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;
@@ -42,8 +38,6 @@ 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;
@@ -433,11 +427,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
@@ -831,7 +825,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();
chunk.postProcessGeneration(this.world);
// check if there was any recursive action
if (this.removed || this.sendQueue.isEmpty() || this.sendQueue.firstLong() != pendingSend) {
return;
@@ -1083,5 +1077,9 @@ 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());
final int maxToSave = PlatformHooks.get().configMaxAutoSavePerTick();
final long maxSaveTime = currentTick - Math.max(1L, PlatformHooks.get().configAutoSaveInterval(this.world));
final int maxToSave = PlatformHooks.get().configMaxAutoSavePerTick(this.world);
for (int autoSaved = 0; autoSaved < maxToSave && !this.autoSaveQueue.isEmpty();) {
final NewChunkHolder holder = this.autoSaveQueue.first();

View File

@@ -6,7 +6,6 @@ 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;
@@ -121,6 +120,7 @@ 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,6 +288,7 @@ 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);
}
@@ -855,12 +856,14 @@ 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,8 +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.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;
@@ -47,7 +45,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.ChunkSerializer;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.VarHandle;
@@ -760,7 +758,6 @@ 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));
}
@@ -863,7 +860,7 @@ public final class NewChunkHolder {
if (chunk != null) {
final LazyRunnable toRun = new LazyRunnable();
this.chunkDataUnload = new UnloadTask(new CallbackCompletable<>(), this.scheduler.loadExecutor.createTask(toRun), toRun);
this.chunkDataUnload = new UnloadTask(new CallbackCompletable<>(), this.scheduler.saveExecutor.createTask(toRun), toRun);
}
if (poiChunk != null) {
this.poiDataUnload = new UnloadTask(new CallbackCompletable<>(), null, null);
@@ -898,7 +895,7 @@ public final class NewChunkHolder {
final ChunkEntitySlices entityChunk = state.entityChunk();
final PoiChunk poiChunk = state.poiChunk();
final boolean shouldLevelChunkNotSave = ChunkSystemFeatures.forceNoSave(chunk);
final boolean shouldLevelChunkNotSave = PlatformHooks.get().forceNoSave(chunk);
// unload chunk data
if (chunk != null) {
@@ -1081,7 +1078,7 @@ public final class NewChunkHolder {
}
// Don't really have a choice but to place this hook here
PlatformHooks.get().onChunkHolderTicketChange(this.world, this, oldLevel, newLevel);
PlatformHooks.get().onChunkHolderTicketChange(this.world, this.vanillaChunkHolder, oldLevel, newLevel);
}
static final int NEIGHBOUR_RADIUS = 2;
@@ -1713,7 +1710,7 @@ public final class NewChunkHolder {
}
}
final boolean forceNoSaveChunk = ChunkSystemFeatures.forceNoSave(chunk);
final boolean forceNoSaveChunk = PlatformHooks.get().forceNoSave(chunk);
// can only synchronously save worldgen chunks during shutdown
boolean canSaveChunk = !forceNoSaveChunk && (chunk != null && ((shutdown || chunk instanceof LevelChunk) && chunk.isUnsaved()));
@@ -1743,56 +1740,6 @@ 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) {
@@ -1800,45 +1747,39 @@ public final class NewChunkHolder {
}
return false;
}
boolean completing = false;
boolean failedAsyncPrepare = false;
try {
if (unloading && ChunkSystemFeatures.supportsAsyncChunkSave()) {
try {
final AsyncChunkSaveData asyncSaveData = ChunkSystemFeatures.getAsyncSaveData(this.world, chunk);
final SerializableChunkData chunkData = SerializableChunkData.copyOf(this.world, chunk);
PlatformHooks.get().chunkSyncSave(this.world, chunk, chunkData);
this.chunkDataUnload.toRun().setRunnable(new AsyncChunkSerializeTask(this.world, chunk, asyncSaveData, this));
chunk.tryMarkSaved();
chunk.setUnsaved(false);
final CallbackCompletable<CompoundTag> completable = new CallbackCompletable<>();
this.chunkDataUnload.task().queue();
final Runnable run = () -> {
final CompoundTag data = chunkData.write();
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
completable.complete(data);
if (unloading) {
NewChunkHolder.this.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, data);
}
}
final CompoundTag save = ChunkSerializer.write(this.world, chunk);
PlatformHooks.get().chunkSyncSave(this.world, chunk, save);
};
final PrioritisedExecutor.PrioritisedTask task;
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");
}
this.chunkDataUnload.toRun().setRunnable(run);
task = this.chunkDataUnload.task();
} else {
MoonriseRegionFileIO.scheduleSave(this.world, this.chunkX, this.chunkZ, save, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA);
task = this.scheduler.saveExecutor.createTask(run);
}
chunk.setUnsaved(false);
task.queue();
MoonriseRegionFileIO.scheduleSave(
this.world, this.chunkX, this.chunkZ, completable, task, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, Priority.NORMAL
);
} 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) -> {
ChunkStatusTasks.postLoadProtoChunk(world, protoChunk.getEntities());
PlatformHooks.get().postLoadProtoChunk(world, protoChunk);
});
this.chunkHolder.replaceProtoChunk(new ImposterProtoChunk(chunk, false));
}
@@ -82,16 +82,10 @@ 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);
} 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.setLoaded(true);
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.ChunkSerializer;
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
import net.minecraft.world.level.levelgen.blending.BlendingData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -273,9 +273,12 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
}
}
private static final class ChunkDataLoadTask extends CallbackDataLoadTask<CompoundTag, ChunkAccess> {
private static record ReadChunk(ProtoChunk protoChunk, SerializableChunkData chunkData) {}
private static final class ChunkDataLoadTask extends CallbackDataLoadTask<ReadChunk, 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);
}
@@ -300,30 +303,32 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
}
@Override
protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final CompoundTag data, final Throwable throwable) {
protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final ReadChunk data, final Throwable throwable) {
if (throwable != null) {
return new TaskResult<>(null, throwable);
}
if (data == null) {
if (data == null || data.protoChunk() == null) {
return new TaskResult<>(this.getEmptyChunk(), null);
}
if (ChunkSystemFeatures.supportsAsyncChunkDeserialization()) {
return this.deserialize(data);
if (!PlatformHooks.get().hasMainChunkLoadHook()) {
return new TaskResult<>(data.protoChunk(), null);
}
// need to deserialize on main thread
// need to invoke the callback for loading on the main thread
return null;
}
private ProtoChunk getEmptyChunk() {
return new ProtoChunk(
new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, this.world,
this.world.registryAccess().registryOrThrow(Registries.BIOME), (BlendingData)null
this.world.registryAccess().lookupOrThrow(Registries.BIOME), (BlendingData)null
);
}
@Override
protected TaskResult<CompoundTag, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
protected TaskResult<ReadChunk, 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);
@@ -337,32 +342,33 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
// run converters
final CompoundTag converted = this.world.getChunkSource().chunkMap.upgradeChunkTag(data);
return new TaskResult<>(converted, null);
// 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);
} 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);
}
}
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);
}
}
@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);
protected TaskResult<ChunkAccess, Throwable> runOnMain(final ReadChunk data, final Throwable throwable) {
PlatformHooks.get().mainChunkLoad(data.protoChunk(), data.chunkData());
return new TaskResult<>(data.protoChunk(), null);
}
}

View File

@@ -1231,7 +1231,7 @@ public final class CollisionUtil {
((CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)second).moonrise$offsetX(),
ft, tf
);
if (mergedX == MergedVoxelCoordinateList.EMPTY) {
if (mergedX == null) {
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 == MergedVoxelCoordinateList.EMPTY) {
if (mergedY == null) {
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 == MergedVoxelCoordinateList.EMPTY) {
if (mergedZ == null) {
return Shapes.empty();
}
@@ -1329,7 +1329,7 @@ public final class CollisionUtil {
((CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)second).moonrise$offsetX(),
ft, tf
);
if (mergedX == MergedVoxelCoordinateList.EMPTY) {
if (mergedX == null) {
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 == MergedVoxelCoordinateList.EMPTY) {
if (mergedY == null) {
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 == MergedVoxelCoordinateList.EMPTY) {
if (mergedZ == null) {
return false;
}
@@ -1368,10 +1368,6 @@ 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];
@@ -1506,7 +1502,7 @@ public final class CollisionUtil {
}
}
return resultSize <= 1 ? EMPTY : new MergedVoxelCoordinateList(coordinates, 0.0, firstIndices, secondIndices, resultSize - 1);
return resultSize <= 1 ? null : new MergedVoxelCoordinateList(coordinates, 0.0, firstIndices, secondIndices, resultSize - 1);
}
}

View File

@@ -1,246 +0,0 @@
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.mutablePos1.set(worldX, worldY, worldZ))) & emittedMask;
final int emittedLevel = (PlatformHooks.get().getLightEmission(blockState, lightAccess.getLevel(), this.lightEmissionPos.set(worldX, worldY, worldZ))) & emittedMask;
this.setLightLevel(worldX, worldY, worldZ, emittedLevel);
// this accounts for change in emitted light that would cause an increase
@@ -121,7 +121,6 @@ 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,
@@ -136,7 +135,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
return level;
}
final int opacity = Math.max(1, centerState.getLightBlock(world, this.recalcCenterPos));
final int opacity = Math.max(1, centerState.getLightBlock());
if (opacity >= 15) {
return level;
}
@@ -167,9 +166,8 @@ 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
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);
final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(direction.opposite.nms);
final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(direction.nms);
if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) {
// not allowed to propagate
continue;
@@ -220,7 +218,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
final PalettedContainer<BlockState> states = section.states;
final int offY = sectionY << 4;
final BlockPos.MutableBlockPos mutablePos = this.mutablePos1;
final BlockPos.MutableBlockPos mutablePos = this.lightEmissionPos;
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,9 +290,6 @@ 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) {
@@ -304,8 +301,7 @@ public final class SkyStarLightEngine extends StarLightEngine {
final BlockState centerState = this.getBlockState(worldX, worldY, worldZ);
final BlockState conditionallyOpaqueState;
this.recalcCenterPos.set(worldX, worldY, worldZ);
final int opacity = Math.max(1, centerState.getLightBlock(lightAccess.getLevel(), this.recalcCenterPos));
final int opacity = Math.max(1, centerState.getLightBlock());
if (((StarlightAbstractBlockState)centerState).starlight$isConditionallyFullOpaque()) {
conditionallyOpaqueState = centerState;
} else {
@@ -334,9 +330,8 @@ 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
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);
final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(direction.opposite.nms);
final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(direction.nms);
if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) {
// not allowed to propagate
continue;
@@ -604,7 +599,6 @@ 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.
@@ -626,8 +620,7 @@ public final class SkyStarLightEngine extends StarLightEngine {
final VoxelShape fromShape;
if (((StarlightAbstractBlockState)above).starlight$isConditionallyFullOpaque()) {
this.mutablePos2.set(worldX, startY + 1, worldZ);
fromShape = above.getFaceOcclusionShape(world, this.mutablePos2, AxisDirection.NEGATIVE_Y.nms);
fromShape = above.getFaceOcclusionShape(AxisDirection.NEGATIVE_Y.nms);
if (Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
// above wont let us propagate
break;
@@ -637,10 +630,9 @@ 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(world, mutablePos, AxisDirection.POSITIVE_Y.nms);
final VoxelShape cullingFace = current.getFaceOcclusionShape(AxisDirection.POSITIVE_Y.nms);
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
// can't propagate here, we're done on this column.
@@ -649,7 +641,7 @@ public final class SkyStarLightEngine extends StarLightEngine {
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
}
final int opacity = current.getLightBlock(world, mutablePos);
final int opacity = current.getLightBlock();
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), 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);
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);
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) {
AxisDirection(final int x, final int y, final int z, final Direction nms) {
this.x = x;
this.y = y;
this.z = z;
this.nms = Direction.fromDelta(x, y, z);
this.nms = nms;
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,9 +107,7 @@ public abstract class StarLightEngine {
// index = x + (z * 5)
protected final boolean[][] emptinessMapCache = new boolean[5 * 5][];
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 final BlockPos.MutableBlockPos lightEmissionPos = new BlockPos.MutableBlockPos();
protected int encodeOffsetX;
protected int encodeOffsetY;
@@ -1151,10 +1149,9 @@ 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(world, this.mutablePos1, propagate.getOpposite().nms);
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
continue;
@@ -1162,7 +1159,7 @@ public abstract class StarLightEngine {
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
}
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
final int opacity = blockState.getLightBlock();
final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
if (targetLevel <= currentLevel) {
continue;
@@ -1186,13 +1183,12 @@ 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(world, this.mutablePos2, propagate.nms) : Shapes.empty();
final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(propagate.nms) : Shapes.empty();
if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
continue;
@@ -1212,10 +1208,9 @@ 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(world, this.mutablePos1, propagate.getOpposite().nms);
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
continue;
@@ -1223,7 +1218,7 @@ public abstract class StarLightEngine {
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
}
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
final int opacity = blockState.getLightBlock();
final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
if (targetLevel <= currentLevel) {
continue;
@@ -1296,10 +1291,10 @@ public abstract class StarLightEngine {
if (blockState == null) {
continue;
}
this.mutablePos1.set(offX, offY, offZ);
this.lightEmissionPos.set(offX, offY, offZ);
long flags = 0;
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
continue;
@@ -1307,7 +1302,7 @@ public abstract class StarLightEngine {
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
}
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
final int opacity = blockState.getLightBlock();
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
@@ -1321,7 +1316,7 @@ public abstract class StarLightEngine {
| (FLAG_RECHECK_LEVEL | flags);
continue;
}
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.mutablePos1)) & emittedMask;
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.lightEmissionPos)) & emittedMask;
if (emittedLight != 0) {
// re-propagate source
// note: do not set recheck level, or else the propagation will fail
@@ -1353,7 +1348,6 @@ 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;
@@ -1362,7 +1356,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(world, this.mutablePos2, propagate.nms) : Shapes.empty();
final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(propagate.nms) : Shapes.empty();
if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
continue;
@@ -1380,10 +1374,10 @@ public abstract class StarLightEngine {
if (blockState == null) {
continue;
}
this.mutablePos1.set(offX, offY, offZ);
this.lightEmissionPos.set(offX, offY, offZ);
long flags = 0;
if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
continue;
@@ -1391,7 +1385,7 @@ public abstract class StarLightEngine {
flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
}
final int opacity = blockState.getLightBlock(world, this.mutablePos1);
final int opacity = blockState.getLightBlock();
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
@@ -1405,7 +1399,7 @@ public abstract class StarLightEngine {
| (FLAG_RECHECK_LEVEL | flags);
continue;
}
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.mutablePos1)) & emittedMask;
final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.lightEmissionPos)) & emittedMask;
if (emittedLight != 0) {
// re-propagate source
// note: do not set recheck level, or else the propagation will fail

View File

@@ -0,0 +1,13 @@
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,19 +14,20 @@ 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();
private static final int STARLIGHT_LIGHT_VERSION = 9;
public static final int STARLIGHT_LIGHT_VERSION = 9;
public static int getLightVersion() {
return 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 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";
public static void saveLightHook(final Level world, final ChunkAccess chunk, final CompoundTag nbt) {
try {

View File

@@ -1,10 +1,15 @@
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;
@@ -17,8 +22,6 @@ 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
@@ -36,15 +39,17 @@ 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 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;
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;
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
@@ -59,8 +64,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 sorterMailbox Lnet/minecraft/util/thread/ProcessorHandle;
mutable field net/minecraft/server/level/ThreadedLevelLightEngine taskMailbox Lnet/minecraft/util/thread/ProcessorMailbox;
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;
# SectionStorage
accessible field net/minecraft/world/level/chunk/storage/SectionStorage levelHeightAccessor Lnet/minecraft/world/level/LevelHeightAccessor;
@@ -187,9 +192,7 @@ 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 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 ticketDispatcher Lnet/minecraft/server/level/ThrottlingChunkTaskDispatcher;
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;
@@ -298,3 +301,7 @@ 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",
"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
}
"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
}
}