From 9fb975320228a881857c22b8e9edbc9e8f3e016e Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 7 Jul 2024 10:35:20 +0300 Subject: [PATCH 01/21] we ball --- gradle.properties | 2 +- .../ai/pathing/BlockStatePathingCache.java | 4 + .../common/ai/pathing/PathNodeCache.java | 134 +++++++++++ .../common/block/BlockListeningSection.java | 14 ++ .../nitori/common/block/BlockStateFlags.java | 123 ++++++++++ .../block/ListeningBlockStatePredicate.java | 10 + .../SleepUntilTimeBlockEntityTickInvoker.java | 37 +++ .../block/entity/SleepingBlockEntity.java | 80 +++++++ .../SectionedEntityMovementTracker.java | 4 + .../ChunkSectionChangeCallback.java | 104 +++++++++ .../SectionedBlockChangeTracker.java | 211 ++++++++++++++++++ .../util/deduplication/LithiumInterner.java | 16 ++ .../common/util/tuples/WorldSectionBox.java | 57 +++++ .../nitori/common/world/LithiumData.java | 55 +++++ .../nitori/mixin/MixinDirection.java | 46 ---- .../mixin/alloc/composter/ComposterMixin.java | 88 ++++---- .../block_entity_tickers/WorldChunkMixin.java | 38 ++++ .../mixin/collections/brain/BrainMixin.java | 41 ++++ .../TypeFilterableListMixin.java | 28 +++ .../TypeFilterableListMixin.java | 55 +++++ .../SectionedEntityCacheMixin.java | 96 ++++++++ .../mixin/math/fast_util/DirectionMixin.java | 71 +++--- .../mixin/{ => math/sine_lut}/MixinMth.java | 2 +- .../EntityShapeContextMixin.java | 110 +++++++++ .../block_tracking/ChunkSectionMixin.java | 173 ++++++++++++++ ...WrappedBlockEntityTickInvokerAccessor.java | 15 ++ .../campfire/CampfireBlockEntityMixin.java | 69 ++++++ .../lit/CampfireBlockEntityMixin.java | 40 ++++ .../unlit/CampfireBlockEntityMixin.java | 34 +++ .../AbstractFurnaceBlockEntityMixin.java | 88 ++++++++ src/main/resources/ignite.mod.json | 2 +- src/main/resources/mixins.core.json | 20 +- 32 files changed, 1734 insertions(+), 133 deletions(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/BlockStatePathingCache.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/BlockListeningSection.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/ListeningBlockStatePredicate.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/entity/SleepUntilTimeBlockEntityTickInvoker.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/entity/SleepingBlockEntity.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/entity/movement_tracker/SectionedEntityMovementTracker.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/ChunkSectionChangeCallback.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/SectionedBlockChangeTracker.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/util/deduplication/LithiumInterner.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/util/tuples/WorldSectionBox.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/world/LithiumData.java delete mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/MixinDirection.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/collections/block_entity_tickers/WorldChunkMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/collections/brain/BrainMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/collections/entity_by_type/TypeFilterableListMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/collections/entity_filtering/TypeFilterableListMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fast_retrieval/SectionedEntityCacheMixin.java rename src/main/java/net/gensokyoreimagined/nitori/mixin/{ => math/sine_lut}/MixinMth.java (98%) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/lazy_shape_context/EntityShapeContextMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/WrappedBlockEntityTickInvokerAccessor.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/CampfireBlockEntityMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/lit/CampfireBlockEntityMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/unlit/CampfireBlockEntityMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/furnace/AbstractFurnaceBlockEntityMixin.java diff --git a/gradle.properties b/gradle.properties index 272f9e8..6ef13b5 100755 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=net.gensokyoreimagined.nitori -version=1.2-SNAPSHOT +version=1.3-SNAPSHOT description=Converting patches into mixins, for the Ignite Framework org.gradle.parallel=true diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/BlockStatePathingCache.java b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/BlockStatePathingCache.java new file mode 100644 index 0000000..054a8b9 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/BlockStatePathingCache.java @@ -0,0 +1,4 @@ +package net.gensokyoreimagined.nitori.common.ai.pathing; + +public class BlockStatePathingCache { +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java new file mode 100644 index 0000000..c6884ac --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java @@ -0,0 +1,134 @@ +package net.gensokyoreimagined.nitori.common.ai.pathing; + +import net.gensokyoreimagined.nitori.common.block.BlockCountingSection; +import net.gensokyoreimagined.nitori.common.block.BlockStateFlags; +import net.gensokyoreimagined.nitori.common.util.Pos; +import net.gensokyoreimagined.nitori.common.world.ChunkView; +import net.gensokyoreimagined.nitori.common.world.WorldHelper; +import me.jellysquid.mods.lithium.mixin.ai.pathing.PathContextAccessor; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.pathfinder.WalkNodeEvaluator; +import net.minecraft.world.level.pathfinder.BinaryHeap; +import net.minecraft.entity.ai.pathing.PathNodeType; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockView; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkSection; + +public abstract class PathNodeCache { + private static boolean isChunkSectionDangerousNeighbor(ChunkSection section) { + return section.getBlockStateContainer() + .hasAny(state -> getNeighborPathNodeType(state) != PathNodeType.OPEN); + } + + public static PathNodeType getPathNodeType(BlockState state) { + return ((BlockStatePathingCache) state).lithium$getPathNodeType(); + } + + public static PathNodeType getNeighborPathNodeType(BlockBehaviour.AbstractBlockState state) { + return ((BlockStatePathingCache) state).lithium$getNeighborPathNodeType(); + } + + /** + * Returns whether a chunk section is free of dangers. This makes use of a caching layer to greatly + * accelerate neighbor danger checks when path-finding. + * + * @param section The chunk section to test for dangers + * @return True if this neighboring section is free of any dangers, otherwise false if it could + * potentially contain dangers + */ + public static boolean isSectionSafeAsNeighbor(ChunkSection section) { + // Empty sections can never contribute a danger + if (section.isEmpty()) { + return true; + } + + if (BlockStateFlags.ENABLED) { + return !((BlockCountingSection) section).lithium$mayContainAny(BlockStateFlags.PATH_NOT_OPEN); + } + return !isChunkSectionDangerousNeighbor(section); + } + + + public static PathNodeType getNodeTypeFromNeighbors(BinaryHeap context, int x, int y, int z, PathNodeType fallback) { + BlockView world = context.getWorld(); + + ChunkSection section = null; + + // Check that all the block's neighbors are within the same chunk column. If so, we can isolate all our block + // reads to just one chunk and avoid hits against the server chunk manager. + if (world instanceof ChunkView chunkView && WorldHelper.areNeighborsWithinSameChunkSection(x, y, z)) { + // If the y-coordinate is within bounds, we can cache the chunk section. Otherwise, the if statement to check + // if the cached chunk section was initialized will early-exit. + if (!world.isOutOfHeightLimit(y)) { + Chunk chunk = chunkView.lithium$getLoadedChunk(Pos.ChunkCoord.fromBlockCoord(x), Pos.ChunkCoord.fromBlockCoord(z)); + + // If the chunk is absent, the cached section above will remain null, as there is no chunk section anyway. + // An empty chunk or section will never pose any danger sources, which will be caught later. + if (chunk != null) { + section = chunk.getSectionArray()[Pos.SectionYIndex.fromBlockCoord(world, y)]; + } + } + + // If we can guarantee that blocks won't be modified while the cache is active, try to see if the chunk + // section is empty or contains any dangerous blocks within the palette. If not, we can assume any checks + // against this chunk section will always fail, allowing us to fast-exit. + if (section == null || PathNodeCache.isSectionSafeAsNeighbor(section)) { + return fallback; //TODO side effects of vanilla's path node caching + } + } + + int xStart = x - 1; + int yStart = y - 1; + int zStart = z - 1; + + int xEnd = x + 1; + int yEnd = y + 1; + int zEnd = z + 1; + + // Vanilla iteration order is XYZ + for (int adjX = xStart; adjX <= xEnd; adjX++) { + for (int adjY = yStart; adjY <= yEnd; adjY++) { + for (int adjZ = zStart; adjZ <= zEnd; adjZ++) { + // Skip the vertical column of the origin block + if (adjX == x && adjZ == z) { + continue; + } + + BlockState state; + + // If we're not accessing blocks outside a given section, we can greatly accelerate block state + // retrieval by calling upon the cached chunk directly. + if (section != null) { + state = section.getBlockState(adjX & 15, adjY & 15, adjZ & 15); + } else { + BlockPos.Mutable pos = ((PathContextAccessor) context).getLastNodePos().set(adjX, adjY, adjZ); + state = world.getBlockState(pos); + } + + if (state.isAir()) { + continue; + } + + PathNodeType neighborType = PathNodeCache.getNeighborPathNodeType(state); + + if (neighborType == null) { //Here null means that no path node type is cached (uninitialized or dynamic) + //Passing null as previous node type to the method signals to other lithium mixins that we only want the neighbor behavior of this block and not its neighbors + neighborType = WalkNodeEvaluator.getNodeTypeFromNeighbors(context, adjX + 1, adjY + 1, adjZ + 1, null); + //Here null means that the path node type is not changed by the block! + if (neighborType == null) { + neighborType = PathNodeType.OPEN; + } + } + if (neighborType != PathNodeType.OPEN) { + return neighborType; + } + } + } + } + + return fallback; + } + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockListeningSection.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockListeningSection.java new file mode 100644 index 0000000..41c22a7 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockListeningSection.java @@ -0,0 +1,14 @@ +package net.gensokyoreimagined.nitori.common.block; + +import net.gensokyoreimagined.nitori.common.entity.block_tracking.SectionedBlockChangeTracker; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.Level; + +public interface BlockListeningSection { + + void lithium$addToCallback(ListeningBlockStatePredicate blockGroup, SectionedBlockChangeTracker tracker, long sectionPos, Level world); + + void lithium$removeFromCallback(ListeningBlockStatePredicate blockGroup, SectionedBlockChangeTracker tracker); + + void lithium$invalidateListeningSection(SectionPos sectionPos); +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java new file mode 100644 index 0000000..92e4ae9 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java @@ -0,0 +1,123 @@ +package net.gensokyoreimagined.nitori.common.block; + +import it.unimi.dsi.fastutil.objects.Reference2BooleanArrayMap; +import net.gensokyoreimagined.nitori.common.ai.pathing.BlockStatePathingCache; +import net.gensokyoreimagined.nitori.common.ai.pathing.PathNodeCache; +import net.gensokyoreimagined.nitori.common.entity.FluidCachingEntity; +import net.gensokyoreimagined.nitori.common.reflection.ReflectionUtil; +import net.minecraft.block.AbstractBlock; +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ai.pathing.PathNodeType; +import net.minecraft.registry.tag.FluidTags; +import net.minecraft.world.chunk.ChunkSection; + +import java.util.ArrayList; + +public class BlockStateFlags { + public static final boolean ENABLED = BlockCountingSection.class.isAssignableFrom(ChunkSection.class); + + public static final int NUM_LISTENING_FLAGS; + public static final ListeningBlockStatePredicate[] LISTENING_FLAGS; + public static final int LISTENING_MASK_OR; + + //Listening Flag + public static final ListeningBlockStatePredicate ANY; + + public static final int NUM_TRACKED_FLAGS; + public static final TrackedBlockStatePredicate[] TRACKED_FLAGS; + + //Counting flags + public static final TrackedBlockStatePredicate OVERSIZED_SHAPE; + public static final TrackedBlockStatePredicate PATH_NOT_OPEN; + public static final TrackedBlockStatePredicate WATER; + public static final TrackedBlockStatePredicate LAVA; + + public static final TrackedBlockStatePredicate[] FLAGS; + + //Non counting flags + public static final TrackedBlockStatePredicate ENTITY_TOUCHABLE; + + static { + Reference2BooleanArrayMap listeningFlags = new Reference2BooleanArrayMap<>(); + + ANY = new ListeningBlockStatePredicate(listeningFlags.size()) { + @Override + public boolean test(BlockState operand) { + return true; + } + }; + //false -> we listen to changes of all blocks that pass the predicate test. + //true -> we only listen to changes of the predicate test result + listeningFlags.put(ANY, false); + + NUM_LISTENING_FLAGS = listeningFlags.size(); + int listenMaskOR = 0; + int iteration = 0; + for (var entry : listeningFlags.reference2BooleanEntrySet()) { + boolean listenOnlyXOR = entry.getBooleanValue(); + listenMaskOR |= listenOnlyXOR ? 0 : 1 << iteration; + } + LISTENING_MASK_OR = listenMaskOR; + LISTENING_FLAGS = listeningFlags.keySet().toArray(new ListeningBlockStatePredicate[NUM_LISTENING_FLAGS]); + + + ArrayList countingFlags = new ArrayList<>(listeningFlags.keySet()); + + OVERSIZED_SHAPE = new TrackedBlockStatePredicate(countingFlags.size()) { + @Override + public boolean test(BlockState operand) { + return operand.exceedsCube(); + } + }; + countingFlags.add(OVERSIZED_SHAPE); + + if (FluidCachingEntity.class.isAssignableFrom(Entity.class)) { + WATER = new TrackedBlockStatePredicate(countingFlags.size()) { + @Override + public boolean test(BlockState operand) { + return operand.getFluidState().getFluid().isIn(FluidTags.WATER); + } + }; + countingFlags.add(WATER); + + LAVA = new TrackedBlockStatePredicate(countingFlags.size()) { + @Override + public boolean test(BlockState operand) { + return operand.getFluidState().getFluid().isIn(FluidTags.LAVA); + } + }; + countingFlags.add(LAVA); + } else { + WATER = null; + LAVA = null; + } + + if (BlockStatePathingCache.class.isAssignableFrom(AbstractBlock.AbstractBlockState.class)) { + PATH_NOT_OPEN = new TrackedBlockStatePredicate(countingFlags.size()) { + @Override + public boolean test(BlockState operand) { + return PathNodeCache.getNeighborPathNodeType(operand) != PathNodeType.OPEN; + } + }; + countingFlags.add(PATH_NOT_OPEN); + } else { + PATH_NOT_OPEN = null; + } + + NUM_TRACKED_FLAGS = countingFlags.size(); + TRACKED_FLAGS = countingFlags.toArray(new TrackedBlockStatePredicate[NUM_TRACKED_FLAGS]); + + ArrayList flags = new ArrayList<>(countingFlags); + + ENTITY_TOUCHABLE = new TrackedBlockStatePredicate(countingFlags.size()) { + @Override + public boolean test(BlockState operand) { + return ReflectionUtil.isBlockStateEntityTouchable(operand); + } + }; + flags.add(ENTITY_TOUCHABLE); + + FLAGS = flags.toArray(new TrackedBlockStatePredicate[0]); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/ListeningBlockStatePredicate.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/ListeningBlockStatePredicate.java new file mode 100644 index 0000000..5484f71 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/ListeningBlockStatePredicate.java @@ -0,0 +1,10 @@ +package net.gensokyoreimagined.nitori.common.block; + +public abstract class ListeningBlockStatePredicate extends TrackedBlockStatePredicate { + public static int LISTENING_MASK; + + protected ListeningBlockStatePredicate(int index) { + super(index); + LISTENING_MASK |= (1 << this.getIndex()); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/SleepUntilTimeBlockEntityTickInvoker.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/SleepUntilTimeBlockEntityTickInvoker.java new file mode 100644 index 0000000..8384639 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/SleepUntilTimeBlockEntityTickInvoker.java @@ -0,0 +1,37 @@ +package net.gensokyoreimagined.nitori.common.block.entity; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.TickingBlockEntity; +import org.jetbrains.annotations.NotNull; + +public record SleepUntilTimeBlockEntityTickInvoker(BlockEntity sleepingBlockEntity, long sleepUntilTickExclusive, + TickingBlockEntity delegate) implements TickingBlockEntity { + + @Override + public void tick() { + //noinspection ConstantConditions + long tickTime = this.sleepingBlockEntity.getLevel().getGameTime(); + if (tickTime >= this.sleepUntilTickExclusive) { + ((SleepingBlockEntity) this.sleepingBlockEntity).setTicker(this.delegate); + this.delegate.tick(); + } + } + + @Override + public boolean isRemoved() { + return this.sleepingBlockEntity.isRemoved(); + } + + @Override + public @NotNull BlockPos getPos() { + return this.sleepingBlockEntity.getBlockPos(); + } + + @Override + public String getType() { + //noinspection ConstantConditions + return BlockEntityType.getKey(this.sleepingBlockEntity.getType()).toString(); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/SleepingBlockEntity.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/SleepingBlockEntity.java new file mode 100644 index 0000000..ec27378 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/SleepingBlockEntity.java @@ -0,0 +1,80 @@ +package net.gensokyoreimagined.nitori.common.block.entity; + +import net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.sleeping.WrappedBlockEntityTickInvokerAccessor; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.TickingBlockEntity; + +public interface SleepingBlockEntity { + TickingBlockEntity SLEEPING_BLOCK_ENTITY_TICKER = new TickingBlockEntity() { + public void tick() { + } + + public boolean isRemoved() { + return false; + } + + public BlockPos getPos() { + return null; + } + + public String getType() { + return ""; + } + }; + + WrappedBlockEntityTickInvokerAccessor lithium$getTickWrapper(); + + void lithium$setTickWrapper(WrappedBlockEntityTickInvokerAccessor tickWrapper); + + TickingBlockEntity lithium$getSleepingTicker(); + + void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker); + + default boolean nitori$startSleeping() { + if (this.isSleeping()) { + return false; + } + + WrappedBlockEntityTickInvokerAccessor tickWrapper = this.lithium$getTickWrapper(); + if (tickWrapper == null) { + return false; + } + this.lithium$setSleepingTicker(tickWrapper.getTicker()); + tickWrapper.callRebind(SleepingBlockEntity.SLEEPING_BLOCK_ENTITY_TICKER); + return true; + } + + default void sleepOnlyCurrentTick() { + TickingBlockEntity sleepingTicker = this.lithium$getSleepingTicker(); + WrappedBlockEntityTickInvokerAccessor tickWrapper = this.lithium$getTickWrapper(); + if (sleepingTicker == null) { + sleepingTicker = tickWrapper.getTicker(); + } + Level world = ((BlockEntity) this).getLevel(); + tickWrapper.callRebind(new SleepUntilTimeBlockEntityTickInvoker((BlockEntity) this, world.getGameTime() + 1, sleepingTicker)); + this.lithium$setSleepingTicker(null); + } + + default void wakeUpNow() { + TickingBlockEntity sleepingTicker = this.lithium$getSleepingTicker(); + if (sleepingTicker == null) { + return; + } + this.setTicker(sleepingTicker); + this.lithium$setSleepingTicker(null); + } + + default void setTicker(TickingBlockEntity delegate) { + WrappedBlockEntityTickInvokerAccessor tickWrapper = this.lithium$getTickWrapper(); + if (tickWrapper == null) { + return; + } + tickWrapper.callRebind(delegate); + } + + default boolean isSleeping() { + return this.lithium$getSleepingTicker() != null; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/movement_tracker/SectionedEntityMovementTracker.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/movement_tracker/SectionedEntityMovementTracker.java new file mode 100644 index 0000000..5a846a1 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/movement_tracker/SectionedEntityMovementTracker.java @@ -0,0 +1,4 @@ +package net.gensokyoreimagined.nitori.common.block.entity.movement_tracker; + +public class SectionedEntityMovementTracker { +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/ChunkSectionChangeCallback.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/ChunkSectionChangeCallback.java new file mode 100644 index 0000000..747622c --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/ChunkSectionChangeCallback.java @@ -0,0 +1,104 @@ +package net.gensokyoreimagined.nitori.common.entity.block_tracking; + +import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; +import net.gensokyoreimagined.nitori.common.block.BlockListeningSection; +import net.gensokyoreimagined.nitori.common.block.BlockStateFlags; +import net.gensokyoreimagined.nitori.common.block.ListeningBlockStatePredicate; +import net.gensokyoreimagined.nitori.common.util.Pos; +import net.gensokyoreimagined.nitori.common.world.LithiumData; +import net.gensokyoreimagined.nitori.common.world.chunk.ChunkStatusTracker; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.chunk.LevelChunkSection; + +import java.util.ArrayList; + +public final class ChunkSectionChangeCallback { + private final ArrayList[] trackers; + private short listeningMask; + + static { + if (BlockListeningSection.class.isAssignableFrom(LevelChunkSection.class)) { + ChunkStatusTracker.registerUnloadCallback((serverWorld, chunkPos) -> { + Long2ReferenceOpenHashMap changeCallbacks = ((LithiumData) serverWorld).lithium$getData().chunkSectionChangeCallbacks(); + int x = chunkPos.x; + int z = chunkPos.z; + for (int y = Pos.SectionYCoord.getMinYSection(serverWorld); y <= Pos.SectionYCoord.getMaxYSectionInclusive(serverWorld); y++) { + SectionPos chunkSectionPos = SectionPos.of(x, y, z); + ChunkSectionChangeCallback chunkSectionChangeCallback = changeCallbacks.remove(chunkSectionPos.asLong()); + if (chunkSectionChangeCallback != null) { + chunkSectionChangeCallback.onChunkSectionInvalidated(chunkSectionPos); + } + } + }); + } + } + + public ChunkSectionChangeCallback() { + //noinspection unchecked + this.trackers = new ArrayList[BlockStateFlags.NUM_LISTENING_FLAGS]; + this.listeningMask = 0; + } + + public static ChunkSectionChangeCallback create(long sectionPos, Level world) { + ChunkSectionChangeCallback chunkSectionChangeCallback = new ChunkSectionChangeCallback(); + Long2ReferenceOpenHashMap changeCallbacks = ((LithiumData) world).lithium$getData().chunkSectionChangeCallbacks(); + ChunkSectionChangeCallback previous = changeCallbacks.put(sectionPos, chunkSectionChangeCallback); + if (previous != null) { + previous.onChunkSectionInvalidated(SectionPos.from(sectionPos)); + } + return chunkSectionChangeCallback; + } + + public short onBlockChange(int blockGroupIndex, BlockListeningSection section) { + ArrayList sectionedBlockChangeTrackers = this.trackers[blockGroupIndex]; + this.trackers[blockGroupIndex] = null; + if (sectionedBlockChangeTrackers != null) { + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < sectionedBlockChangeTrackers.size(); i++) { + sectionedBlockChangeTrackers.get(i).setChanged(section); + } + } + this.listeningMask &= (short) ~(1 << blockGroupIndex); + + return this.listeningMask; + } + + public short addTracker(SectionedBlockChangeTracker tracker, ListeningBlockStatePredicate blockGroup) { + int blockGroupIndex = blockGroup.getIndex(); + ArrayList sectionedBlockChangeTrackers = this.trackers[blockGroupIndex]; + if (sectionedBlockChangeTrackers == null) { + this.trackers[blockGroupIndex] = (sectionedBlockChangeTrackers = new ArrayList<>()); + } + sectionedBlockChangeTrackers.add(tracker); + + this.listeningMask |= (short) (1 << blockGroupIndex); + return this.listeningMask; + } + + public short removeTracker(SectionedBlockChangeTracker tracker, ListeningBlockStatePredicate blockGroup) { + int blockGroupIndex = blockGroup.getIndex(); + ArrayList sectionedBlockChangeTrackers = this.trackers[blockGroupIndex]; + if (sectionedBlockChangeTrackers != null) { + sectionedBlockChangeTrackers.remove(tracker); + if (sectionedBlockChangeTrackers.isEmpty()) { + this.listeningMask &= (short) ~(1 << blockGroup.getIndex()); + } + } + return this.listeningMask; + } + + public void onChunkSectionInvalidated(SectionPos sectionPos) { + for (int flagIndex = 0; flagIndex < this.trackers.length; flagIndex++) { + ArrayList sectionedBlockChangeTrackers = this.trackers[flagIndex]; + this.trackers[flagIndex] = null; + if (sectionedBlockChangeTrackers != null) { + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < sectionedBlockChangeTrackers.size(); i++) { + sectionedBlockChangeTrackers.get(i).onChunkSectionInvalidated(sectionPos); + } + } + } + this.listeningMask = 0; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/SectionedBlockChangeTracker.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/SectionedBlockChangeTracker.java new file mode 100644 index 0000000..4d8ce4c --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/SectionedBlockChangeTracker.java @@ -0,0 +1,211 @@ +package net.gensokyoreimagined.nitori.common.entity.block_tracking; + +import net.gensokyoreimagined.nitori.common.block.BlockListeningSection; +import net.gensokyoreimagined.nitori.common.block.ListeningBlockStatePredicate; +import net.gensokyoreimagined.nitori.common.util.Pos; +import net.gensokyoreimagined.nitori.common.util.deduplication.LithiumInterner; +import net.gensokyoreimagined.nitori.common.util.tuples.WorldSectionBox; +import net.gensokyoreimagined.nitori.common.world.LithiumData; +import net.minecraft.world.phys.AABB; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.status.ChunkStatus; + +import java.util.ArrayList; +import java.util.Objects; + +public class SectionedBlockChangeTracker { + public final WorldSectionBox trackedWorldSections; + public final ListeningBlockStatePredicate blockGroup; + + private long maxChangeTime; + + private int timesRegistered; + //Some sections may not exist / be unloaded. We have to be aware of those. //TODO Invalidation when sections / chunks unload (but the entity does not (?), not sure whether this is possible) + boolean isListeningToAll = false; + private ArrayList sectionsNotListeningTo = null; + private ArrayList sectionsUnsubscribed = null; + + public SectionedBlockChangeTracker(WorldSectionBox trackedWorldSections, ListeningBlockStatePredicate blockGroup) { + this.trackedWorldSections = trackedWorldSections; + this.blockGroup = blockGroup; + + this.maxChangeTime = 0; + } + + public boolean matchesMovedBox(AABB box) { + return this.trackedWorldSections.matchesRelevantBlocksBox(box); + } + + public static SectionedBlockChangeTracker registerAt(Level world, AABB entityBoundingBox, ListeningBlockStatePredicate blockGroup) { + WorldSectionBox worldSectionBox = WorldSectionBox.relevantExpandedBlocksBox(world, entityBoundingBox); + SectionedBlockChangeTracker tracker = new SectionedBlockChangeTracker(worldSectionBox, blockGroup); + + LithiumInterner blockChangeTrackers = ((LithiumData) world).lithium$getData().blockChangeTrackers(); + tracker = blockChangeTrackers.getCanonical(tracker); + + tracker.register(); + return tracker; + } + + long getWorldTime() { + return this.trackedWorldSections.world().getGameTime(); + } + + public void register() { + if (this.timesRegistered == 0) { + WorldSectionBox trackedSections = this.trackedWorldSections; + for (int x = trackedSections.chunkX1(); x < trackedSections.chunkX2(); x++) { + for (int z = trackedSections.chunkZ1(); z < trackedSections.chunkZ2(); z++) { + Level world = trackedSections.world(); + ChunkAccess chunk = world.getChunk(x, z, ChunkStatus.FULL, false); + LevelChunkSection[] sectionArray = chunk == null ? null : chunk.getSections(); + for (int y = trackedSections.chunkY1(); y < trackedSections.chunkY2(); y++) { + if (Pos.SectionYCoord.getMinYSection(world) > y || Pos.SectionYCoord.getMaxYSectionExclusive(world) <= y) { + continue; + } + SectionPos sectionPos = SectionPos.of(x, y, z); + if (sectionArray == null) { + if (this.sectionsNotListeningTo == null) { + this.sectionsNotListeningTo = new ArrayList<>(); + } + this.sectionsNotListeningTo.add(sectionPos); + continue; + } + LevelChunkSection section = sectionArray[Pos.SectionYIndex.fromSectionCoord(world, y)]; + + BlockListeningSection blockListeningSection = (BlockListeningSection) section; + blockListeningSection.lithium$addToCallback(this.blockGroup, this, SectionPos.asLong(x, y, z), world); + } + } + } + this.isListeningToAll = (this.sectionsNotListeningTo == null || this.sectionsNotListeningTo.isEmpty()) + && (this.sectionsUnsubscribed == null || this.sectionsUnsubscribed.isEmpty()); + this.setChanged(this.getWorldTime()); + } + this.timesRegistered++; + } + + public void unregister() { + if (--this.timesRegistered > 0) { + return; + } + WorldSectionBox trackedSections = this.trackedWorldSections; + Level world = trackedSections.world(); + for (int x = trackedSections.chunkX1(); x < trackedSections.chunkX2(); x++) { + for (int z = trackedSections.chunkZ1(); z < trackedSections.chunkZ2(); z++) { + ChunkAccess chunk = world.getChunk(x, z, ChunkStatus.FULL, false); + LevelChunkSection[] sectionArray = chunk == null ? null : chunk.getSections(); + for (int y = trackedSections.chunkY1(); y < trackedSections.chunkY2(); y++) { + + if (sectionArray == null) { + continue; + } + if (Pos.SectionYCoord.getMinYSection(world) > y || Pos.SectionYCoord.getMaxYSectionExclusive(world) <= y) { + continue; + } + LevelChunkSection section = sectionArray[Pos.SectionYIndex.fromSectionCoord(world, y)]; + + BlockListeningSection blockListeningSection = (BlockListeningSection) section; + blockListeningSection.lithium$removeFromCallback(this.blockGroup, this); + } + } + } + this.sectionsNotListeningTo = null; + LithiumInterner blockChangeTrackers = ((LithiumData) world).lithium$getData().blockChangeTrackers(); + blockChangeTrackers.deleteCanonical(this); + } + + public void listenToAllSections() { + boolean changed = false; + ArrayList notListeningTo = this.sectionsNotListeningTo; + if (notListeningTo != null) { + for (int i = notListeningTo.size() - 1; i >= 0; i--) { + changed = true; + SectionPos chunkSectionPos = notListeningTo.get(i); + Level world = this.trackedWorldSections.world(); + ChunkAccess chunk = world.getChunk(chunkSectionPos.getX(), chunkSectionPos.getZ(), ChunkStatus.FULL, false); + if (chunk != null) { + notListeningTo.remove(i); + } else { + //Chunk not loaded, cannot listen to all sections. + return; + } + LevelChunkSection section = chunk.getSections()[Pos.SectionYIndex.fromSectionCoord(world, chunkSectionPos.getY())]; + BlockListeningSection blockListeningSection = (BlockListeningSection) section; + blockListeningSection.lithium$addToCallback(this.blockGroup, this, chunkSectionPos.asLong(), world); + } + } + if (this.sectionsUnsubscribed != null) { + ArrayList unsubscribed = this.sectionsUnsubscribed; + for (int i = unsubscribed.size() - 1; i >= 0; i--) { + changed = true; + BlockListeningSection blockListeningSection = unsubscribed.remove(i); + blockListeningSection.lithium$addToCallback(this.blockGroup, this, Long.MIN_VALUE, null); + } + } + this.isListeningToAll = true; + if (changed) { + this.setChanged(this.getWorldTime()); + } + } + + public void setChanged(BlockListeningSection section) { + if (this.sectionsUnsubscribed == null) { + this.sectionsUnsubscribed = new ArrayList<>(); + } + this.sectionsUnsubscribed.add(section); + this.setChanged(this.getWorldTime()); + this.isListeningToAll = false; + } + + public void setChanged(long atTime) { + if (atTime > this.maxChangeTime) { + this.maxChangeTime = atTime; + } + } + + /** + * Method to quickly check whether any relevant blocks changed inside the relevant chunk sections after + * the last test. + * + * @param lastCheckedTime time of the last interaction attempt + * @return whether any relevant entity moved in the tracked area + */ + public boolean isUnchangedSince(long lastCheckedTime) { + if (lastCheckedTime <= this.maxChangeTime) { + return false; + } + if (!this.isListeningToAll) { + this.listenToAllSections(); + return this.isListeningToAll && lastCheckedTime > this.maxChangeTime; + } + return true; + } + + //Do not modify, used for deduplication of instances + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (SectionedBlockChangeTracker) obj; + return Objects.equals(this.trackedWorldSections, that.trackedWorldSections) && + Objects.equals(this.blockGroup, that.blockGroup); + } + //Do not modify, used for deduplication of instances + @Override + public int hashCode() { + return this.getClass().hashCode() ^ this.trackedWorldSections.hashCode() ^ this.blockGroup.hashCode(); + } + + public void onChunkSectionInvalidated(SectionPos sectionPos) { + if (this.sectionsNotListeningTo == null) { + this.sectionsNotListeningTo = new ArrayList<>(); + } + this.sectionsNotListeningTo.add(sectionPos); + this.setChanged(this.getWorldTime()); + this.isListeningToAll = false; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/util/deduplication/LithiumInterner.java b/src/main/java/net/gensokyoreimagined/nitori/common/util/deduplication/LithiumInterner.java new file mode 100644 index 0000000..ef1d871 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/util/deduplication/LithiumInterner.java @@ -0,0 +1,16 @@ +package net.gensokyoreimagined.nitori.common.util.deduplication; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; + +public class LithiumInterner { + private final ObjectOpenHashSet canonicalStorage = new ObjectOpenHashSet<>(); + + public S getCanonical(S value) { + //noinspection unchecked + return (S) this.canonicalStorage.addOrGet(value); + } + + public void deleteCanonical(T value) { + this.canonicalStorage.remove(value); + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/util/tuples/WorldSectionBox.java b/src/main/java/net/gensokyoreimagined/nitori/common/util/tuples/WorldSectionBox.java new file mode 100644 index 0000000..59b84a5 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/util/tuples/WorldSectionBox.java @@ -0,0 +1,57 @@ +package net.gensokyoreimagined.nitori.common.util.tuples; + +import net.minecraft.world.phys.AABB; +import net.minecraft.core.SectionPos; +import net.minecraft.util.Mth; +import net.minecraft.world.level.Level; + +//Y values use coordinates, not indices (y=0 -> chunkY=0) +//upper bounds are EXCLUSIVE +public record WorldSectionBox(Level world, int chunkX1, int chunkY1, int chunkZ1, int chunkX2, int chunkY2, + int chunkZ2) { + public static WorldSectionBox entityAccessBox(Level world, AABB box) { + int minX = SectionPos.blockToSectionCoord(box.minX - 2.0D); + int minY = SectionPos.blockToSectionCoord(box.minY - 4.0D); + int minZ = SectionPos.blockToSectionCoord(box.minZ - 2.0D); + int maxX = SectionPos.blockToSectionCoord(box.maxX + 2.0D) + 1; + int maxY = SectionPos.blockToSectionCoord(box.maxY) + 1; + int maxZ = SectionPos.blockToSectionCoord(box.maxZ + 2.0D) + 1; + return new WorldSectionBox(world, minX, minY, minZ, maxX, maxY, maxZ); + } + + //Relevant block box: Entity hitbox expanded to all blocks it touches. Then expand the resulting box by 1 block in each direction. + //Include all chunk sections that contain blocks inside the expanded box. + public static WorldSectionBox relevantExpandedBlocksBox(Level world, AABB box) { + int minX = SectionPos.blockToSectionCoord(Mth.floor(box.minX) - 1); + int minY = SectionPos.blockToSectionCoord(Mth.floor(box.minY) - 1); + int minZ = SectionPos.blockToSectionCoord(Mth.floor(box.minZ) - 1); + int maxX = SectionPos.blockToSectionCoord(Mth.floor(box.maxX) + 1) + 1; + int maxY = SectionPos.blockToSectionCoord(Mth.floor(box.maxY) + 1) + 1; + int maxZ = SectionPos.blockToSectionCoord(Mth.floor(box.maxZ) + 1) + 1; + return new WorldSectionBox(world, minX, minY, minZ, maxX, maxY, maxZ); + } + //Like relevant blocks, but not expanded, because fluids never exceed the 1x1x1 volume of a block + public static WorldSectionBox relevantFluidBox(Level world, AABB box) { + int minX = SectionPos.blockToSectionCoord(Mth.floor(box.minX)); + int minY = SectionPos.blockToSectionCoord(Mth.floor(box.minY)); + int minZ = SectionPos.blockToSectionCoord(Mth.floor(box.minZ)); + int maxX = SectionPos.blockToSectionCoord(Mth.floor(box.maxX)) + 1; + int maxY = SectionPos.blockToSectionCoord(Mth.floor(box.maxY)) + 1; + int maxZ = SectionPos.blockToSectionCoord(Mth.floor(box.maxZ)) + 1; + return new WorldSectionBox(world, minX, minY, minZ, maxX, maxY, maxZ); + } + + public int numSections() { + return (this.chunkX2 - this.chunkX1) * (this.chunkY2 - this.chunkY1) * (this.chunkZ2 - this.chunkZ1); + } + + public boolean matchesRelevantBlocksBox(AABB box) { + return SectionPos.blockToSectionCoord(Mth.floor(box.minX) - 1) == this.chunkX1 && + SectionPos.blockToSectionCoord(Mth.floor(box.minY) - 1) == this.chunkY1 && + SectionPos.blockToSectionCoord(Mth.floor(box.minZ) - 1) == this.chunkZ1 && + SectionPos.blockToSectionCoord(Mth.ceil(box.maxX) + 1) + 1 == this.chunkX2 && + SectionPos.blockToSectionCoord(Mth.ceil(box.maxY) + 1) + 1 == this.chunkY2 && + SectionPos.blockToSectionCoord(Mth.ceil(box.maxZ) + 1) + 1 == this.chunkZ2; + } + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/LithiumData.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/LithiumData.java new file mode 100644 index 0000000..5181ce3 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/LithiumData.java @@ -0,0 +1,55 @@ +package net.gensokyoreimagined.nitori.common.world; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import net.gensokyoreimagined.nitori.common.entity.block_tracking.ChunkSectionChangeCallback; +import net.gensokyoreimagined.nitori.common.entity.block_tracking.SectionedBlockChangeTracker; +import net.gensokyoreimagined.nitori.common.block.entity.movement_tracker.SectionedEntityMovementTracker; +import net.gensokyoreimagined.nitori.common.util.deduplication.LithiumInterner; +import net.minecraft.world.entity.ai.navigation.PathNavigation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.entity.raid.Raid; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.gameevent.GameEventDispatcher; +import net.minecraft.world.level.gameevent.GameEventListenerRegistry; + +public interface LithiumData { + + record Data( + // Map of chunk position -> y section -> game event dispatcher + // This should be faster than the chunk lookup, since there are usually a lot more chunks than + // chunk with game event dispatchers (we only initialize them when non-empty set of listeners) + // All Int2ObjectMap objects are also stored in a field of the corresponding WorldChunk. + Long2ReferenceOpenHashMap> gameEventDispatchersByChunk, + + // Cached ominous banner, must not be mutated. + ItemStack ominousBanner, + + // Set of active mob navigations (active = have a path) + ReferenceOpenHashSet activeNavigations, + + // Block change tracker deduplication + LithiumInterner blockChangeTrackers, + + // Entity movement tracker deduplication + LithiumInterner> entityMovementTrackers, + + // Block ChunkSection listeners + Long2ReferenceOpenHashMap chunkSectionChangeCallbacks + ) { + public Data(Level world) { + this( + new Long2ReferenceOpenHashMap<>(), + world.registryAccess().getOptionalWrapper(Registries.BANNER_PATTERN).map(Raid::getOminousBanner).orElse(null), + new ReferenceOpenHashSet<>(), + new LithiumInterner<>(), + new LithiumInterner<>(), + new Long2ReferenceOpenHashMap<>() + ); + } + } + + LithiumData.Data lithium$getData(); +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinDirection.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinDirection.java deleted file mode 100644 index 6f58661..0000000 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinDirection.java +++ /dev/null @@ -1,46 +0,0 @@ -// Nitori Copyright (C) 2024 Gensokyo Reimagined -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -package net.gensokyoreimagined.nitori.mixin; - -import net.minecraft.core.Direction; -import net.minecraft.util.RandomSource; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; - -@Mixin(Direction.class) -public class MixinDirection { - @Shadow @Final private static Direction[] VALUES; - @Shadow @Final private int oppositeIndex; - - /** - * @author DoggySazHi - * @reason Implementation of 0005-lithium-fast-util.patch, requires a overwrite to avoid calling `from3DDataValue` - */ - @Overwrite - public Direction getOpposite() { - return VALUES[this.oppositeIndex]; - } - - /** - * @author DoggySazHi - * @reason Implementation of 0005-lithium-fast-util.patch, requires a overwrite to avoid calling `Util.getRandom` - */ - @Overwrite - public static Direction getRandom(RandomSource random) { - return VALUES[random.nextInt(VALUES.length)]; - } -} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/composter/ComposterMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/composter/ComposterMixin.java index ed91a02..5e334ea 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/composter/ComposterMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/composter/ComposterMixin.java @@ -1,48 +1,46 @@ package net.gensokyoreimagined.nitori.mixin.alloc.composter; -//import net.gensokyoreimagined.nitori.common.util.ArrayConstants; -//import net.minecraft.world.WorldlyContainer; -//import net.minecraft.core.Direction; -//import org.spongepowered.asm.mixin.Mixin; -//import org.spongepowered.asm.mixin.Overwrite; -// -//public class ComposterMixin { -// -// @Mixin(targets = "net.minecraft.block.ComposterBlock$ComposterInventory") -// static abstract class ComposterBlockComposterInventoryMixin implements WorldlyContainer { -// /** -// * @author 2No2Name -// * @reason avoid allocation -// */ -// @Overwrite -// public int[] getSlotsForFace(Direction side) { -// return side == Direction.UP ? ArrayConstants.ZERO : ArrayConstants.EMPTY; -// } -// } -// -// @Mixin(targets = "net.minecraft.block.ComposterBlock$DummyInventory") -// static abstract class ComposterBlockDummyInventoryMixin implements WorldlyContainer { -// /** -// * @author 2No2Name -// * @reason avoid allocation -// */ -// @Overwrite -// public int[] getSlotsForFace(Direction side) { -// return ArrayConstants.EMPTY; -// } -// } -// -// @Mixin(targets = "net.minecraft.block.ComposterBlock$FullComposterInventory") -// static abstract class ComposterBlockFullComposterInventoryMixin implements WorldlyContainer { -// /** -// * @author 2No2Name -// * @reason avoid allocation -// */ -// @Overwrite -// public int[] getSlotsForFace(Direction side) { -// return side == Direction.DOWN ? ArrayConstants.ZERO : ArrayConstants.EMPTY; -// } -// } -//} +import net.gensokyoreimagined.nitori.common.util.ArrayConstants; +import net.minecraft.world.WorldlyContainer; +import net.minecraft.core.Direction; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; -//TODO: Mixins are not getting dettected for some reason even if they are in the mixins.core??? \ No newline at end of file +public class ComposterMixin { + + @Mixin(targets = "net.minecraft.world.level.block.ComposterBlock$InputContainer") + static abstract class ComposterBlockComposterInventoryMixin implements WorldlyContainer { + /** + * @author 2No2Name + * @reason avoid allocation + */ + @Overwrite + public int[] getSlotsForFace(Direction side) { + return side == Direction.UP ? ArrayConstants.ZERO : ArrayConstants.EMPTY; + } + } + + @Mixin(targets = "net.minecraft.world.level.block.ComposterBlock$EmptyContainer") + static abstract class ComposterBlockDummyInventoryMixin implements WorldlyContainer { + /** + * @author 2No2Name + * @reason avoid allocation + */ + @Overwrite + public int[] getSlotsForFace(Direction side) { + return ArrayConstants.EMPTY; + } + } + + @Mixin(targets = "net.minecraft.world.level.block.ComposterBlock$OutputContainer") + static abstract class ComposterBlockFullComposterInventoryMixin implements WorldlyContainer { + /** + * @author 2No2Name + * @reason avoid allocation + */ + @Overwrite + public int[] getSlotsForFace(Direction side) { + return side == Direction.DOWN ? ArrayConstants.ZERO : ArrayConstants.EMPTY; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/block_entity_tickers/WorldChunkMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/block_entity_tickers/WorldChunkMixin.java new file mode 100644 index 0000000..2cfa23b --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/block_entity_tickers/WorldChunkMixin.java @@ -0,0 +1,38 @@ +package net.gensokyoreimagined.nitori.mixin.collections.block_entity_tickers; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.levelgen.blending.BlendingData; +import net.minecraft.world.ticks.LevelChunkTicks; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Map; + +@Mixin(LevelChunk.class) +public class WorldChunkMixin { + @Mutable + @Shadow + @Final + private Map tickersInLevel; + + @Inject( + method = "(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/world/level/chunk/UpgradeData;Lnet/minecraft/world/ticks/LevelChunkTicks;Lnet/minecraft/world/ticks/LevelChunkTicks;J[Lnet/minecraft/world/level/chunk/LevelChunkSection;Lnet/minecraft/world/level/chunk/LevelChunk$PostLoadProcessor;Lnet/minecraft/world/level/levelgen/blending/BlendingData;)V", + at = @At("TAIL") + ) + @Coerce + private void createFastUtilMap(Level world, ChunkPos pos, UpgradeData upgradeData, LevelChunkTicks blockTickScheduler, LevelChunkTicks fluidTickScheduler, long inhabitedTime, LevelChunkSection[] sectionArrayInitializer, LevelChunk.PostLoadProcessor entityLoader, BlendingData blendingData, CallbackInfo ci) { + this.tickersInLevel = new Object2ObjectOpenHashMap<>(); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/brain/BrainMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/brain/BrainMixin.java new file mode 100644 index 0000000..614d6ef --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/brain/BrainMixin.java @@ -0,0 +1,41 @@ +package net.gensokyoreimagined.nitori.mixin.collections.brain; + +import com.google.common.collect.ImmutableList; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import net.minecraft.world.entity.ai.Brain; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Collection; +import java.util.Map; +import java.util.function.Supplier; + +@Mixin(Brain.class) +public class BrainMixin { + + @Mutable + @Shadow + @Final + private Map memories; + + @Mutable + @Shadow + @Final + private Map sensors; + + @Inject( + method = "", + at = @At("RETURN") + ) + private void reinitializeBrainCollections(Collection memories, Collection sensors, ImmutableList memoryEntries, Supplier codecSupplier, CallbackInfo ci) { + this.memories = new Reference2ReferenceOpenHashMap<>(this.memories); + this.sensors = new Reference2ReferenceLinkedOpenHashMap<>(this.sensors); + } + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/entity_by_type/TypeFilterableListMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/entity_by_type/TypeFilterableListMixin.java new file mode 100644 index 0000000..2b55fe6 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/entity_by_type/TypeFilterableListMixin.java @@ -0,0 +1,28 @@ +package net.gensokyoreimagined.nitori.mixin.collections.entity_by_type; + +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import net.minecraft.util.ClassInstanceMultiMap; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; +import java.util.Map; + +@Mixin(ClassInstanceMultiMap.class) +public class TypeFilterableListMixin { + + @Mutable + @Shadow + @Final + private Map, List> byClass; + + @Inject(method = "", at = @At("RETURN")) + private void init(Class elementType, CallbackInfo ci) { + this.byClass = new Reference2ReferenceOpenHashMap<>(this.byClass); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/entity_filtering/TypeFilterableListMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/entity_filtering/TypeFilterableListMixin.java new file mode 100644 index 0000000..c92d560 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/entity_filtering/TypeFilterableListMixin.java @@ -0,0 +1,55 @@ +package net.gensokyoreimagined.nitori.mixin.collections.entity_filtering; + +import net.minecraft.util.ClassInstanceMultiMap; +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 java.util.*; + +/** + * Patches {@link ClassInstanceMultiMap} to improve performance when entities are being queried in the world. + */ +@Mixin(ClassInstanceMultiMap.class) +public class TypeFilterableListMixin { + + @Shadow + @Final + private Map, List> byClass; + + @Shadow + @Final + private List allInstances; + + /** + * @reason Only perform the slow Class#isAssignableFrom(Class) if a list doesn't exist for the type, otherwise + * we can assume it's already valid. The slow-path code is moved to a separate method to help the JVM inline this. + * @author JellySquid + */ + @SuppressWarnings("unchecked") + @Overwrite + public Collection find(Class type) { + Collection collection = this.byClass.get(type); + + if (collection == null) { + collection = this.createAllOfType(type); + } + + return (Collection) Collections.unmodifiableCollection(collection); + } + + private Collection createAllOfType(Class type) { + List list = new ArrayList<>(); + + for (T allElement : this.allInstances) { + if (type.isInstance(allElement)) { + list.add(allElement); + } + } + + this.byClass.put(type, list); + + return list; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fast_retrieval/SectionedEntityCacheMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fast_retrieval/SectionedEntityCacheMixin.java new file mode 100644 index 0000000..04a17db --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fast_retrieval/SectionedEntityCacheMixin.java @@ -0,0 +1,96 @@ +package net.gensokyoreimagined.nitori.mixin.entity.fast_retrieval; + +import net.minecraft.util.AbortableIterationConsumer; +import net.minecraft.world.phys.AABB; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.entity.EntityAccess; +import net.minecraft.world.level.entity.EntitySection; +import net.minecraft.world.level.entity.EntitySectionStorage; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(EntitySectionStorage.class) +public abstract class SectionedEntityCacheMixin { + @Shadow + @Nullable + public abstract EntitySection getSection(long sectionPos); + + /** + * @author 2No2Name + * @reason avoid iterating through LongAVLTreeSet, possibly iterating over hundreds of irrelevant longs to save up to 8 hash set gets + */ + @Inject( + method = "forEachAccessibleNonEmptySection", + at = @At( + value = "INVOKE_ASSIGN", + shift = At.Shift.AFTER, + target = "Lnet/minecraft/core/SectionPos;posToSectionCoord(D)I", + ordinal = 5 + ), + locals = LocalCapture.CAPTURE_FAILHARD, + cancellable = true + ) + public void forEachInBox(AABB box, AbortableIterationConsumer> action, CallbackInfo ci, int i, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { + if (maxX >= minX + 4 || maxZ >= minZ + 4) { + return; // Vanilla is likely more optimized when shooting entities with TNT cannons over huge distances. + // Choosing a cutoff of 4 chunk size, as it becomes more likely that these entity sections do not exist when + // they are far away from the shot entity (player despawn range, position maybe not on the ground, etc) + } + ci.cancel(); + + // Vanilla order of the AVL long set is sorting by ascending long value. The x, y, z positions are packed into + // a long with the x position's lowest 22 bits placed at the MSB. + // Therefore the long is negative iff the 22th bit of the x position is set, which happens iff the x position + // is negative. A positive x position will never have its 22th bit set, as these big coordinates are far outside + // the world. y and z positions are treated as unsigned when sorting by ascending long value, as their sign bits + // are placed somewhere inside the packed long + + for (int x = minX; x <= maxX; x++) { + for (int z = Math.max(minZ, 0); z <= maxZ; z++) { + if (this.forEachInColumn(x, minY, maxY, z, action).shouldAbort()) { + return; + } + } + + int bound = Math.min(-1, maxZ); + for (int z = minZ; z <= bound; z++) { + if (this.forEachInColumn(x, minY, maxY, z, action).shouldAbort()) { + return; + } + } + } + } + + private AbortableIterationConsumer.Continuation forEachInColumn(int x, int minY, int maxY, int z, AbortableIterationConsumer> action) { + AbortableIterationConsumer.Continuation ret = AbortableIterationConsumer.Continuation.CONTINUE; + //y from negative to positive, but y is treated as unsigned + for (int y = Math.max(minY, 0); y <= maxY; y++) { + if ((ret = this.consumeSection(SectionPos.asLong(x, y, z), action)).shouldAbort()) { + return ret; + } + } + int bound = Math.min(-1, maxY); + for (int y = minY; y <= bound; y++) { + if ((ret = this.consumeSection(SectionPos.asLong(x, y, z), action)).shouldAbort()) { + return ret; + } + } + return ret; + } + + private AbortableIterationConsumer.Continuation consumeSection(long pos, AbortableIterationConsumer> action) { + EntitySection section = this.getSection(pos); + //noinspection SizeReplaceableByIsEmpty + if (section != null && + 0 != section.size() /* util.entity_movement_tracking mixins modify isEmpty to include listener objects */ + && section.getStatus().isAccessible()) { + return action.accept(section); + } + return AbortableIterationConsumer.Continuation.CONTINUE; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/math/fast_util/DirectionMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/fast_util/DirectionMixin.java index d1d9486..943d543 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/math/fast_util/DirectionMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/fast_util/DirectionMixin.java @@ -1,38 +1,37 @@ package net.gensokyoreimagined.nitori.mixin.math.fast_util; -//import net.minecraft.core.Direction; -//import net.minecraft.core.Direction.Axis; -//import net.minecraft.util.RandomSource; -//import org.spongepowered.asm.mixin.Final; -//import org.spongepowered.asm.mixin.Mixin; -//import org.spongepowered.asm.mixin.Overwrite; -//import org.spongepowered.asm.mixin.Shadow; -// -//@Mixin(Direction.class) -//public class DirectionMixin { -// @Shadow -// @Final -// private static Direction[] ALL; -// -// @Shadow -// @Final -// private int idOpposite; -// -// /** -// * @reason Avoid the modulo/abs operations -// * @author JellySquid -// */ -// @Overwrite -// public Direction getOpposite() { -// return ALL[this.idOpposite]; -// } -// -// /** -// * @reason Do not allocate an excessive number of Direction arrays -// * @author JellySquid -// */ -// @Overwrite -// public static Direction getRandom(RandomSource rand) { -// return Direction.ALL[rand.nextInt(ALL.length)]; -// } -//} \ No newline at end of file +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(Direction.class) +public class DirectionMixin { + @Shadow + @Final + private static Direction[] VALUES; + + @Shadow + @Final + private int oppositeIndex; + + /** + * @reason Avoid the modulo/abs operations + * @author JellySquid + */ + @Overwrite + public Direction getOpposite() { + return VALUES[this.oppositeIndex]; + } + + /** + * @reason Do not allocate an excessive number of Direction arrays + * @author JellySquid + */ + @Overwrite + public static Direction getRandom(RandomSource rand) { + return VALUES[rand.nextInt(VALUES.length)]; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinMth.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/sine_lut/MixinMth.java similarity index 98% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/MixinMth.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/math/sine_lut/MixinMth.java index 78820bd..1bd55cd 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinMth.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/sine_lut/MixinMth.java @@ -12,7 +12,7 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package net.gensokyoreimagined.nitori.mixin; +package net.gensokyoreimagined.nitori.mixin.math.sine_lut; import net.minecraft.util.Mth; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/lazy_shape_context/EntityShapeContextMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/lazy_shape_context/EntityShapeContextMixin.java new file mode 100644 index 0000000..8312240 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/lazy_shape_context/EntityShapeContextMixin.java @@ -0,0 +1,110 @@ +package net.gensokyoreimagined.nitori.mixin.shapes.lazy_shape_context; + +import net.minecraft.world.phys.shapes.EntityCollisionContext; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyConstant; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.function.Predicate; + +@Mixin(EntityCollisionContext.class) +public class EntityShapeContextMixin { + @Mutable + @Shadow + @Final + private ItemStack heldItem; + + @Mutable + @Shadow + @Final + private Predicate canStandOnFluid; + + @Shadow + @Final + @Nullable + private Entity entity; + + /** + * Mixin the instanceof to always return false to avoid the expensive inventory access. + * No need to use Opcodes.INSTANCEOF or similar. + */ + @SuppressWarnings("InvalidInjectorMethodSignature") + @ModifyConstant( + method = "(Lnet/minecraft/world/entity/Entity;)V", + constant = @Constant(classValue = LivingEntity.class, ordinal = 0) + ) + private static boolean redirectInstanceOf(Object ignored, Class constant) { + return false; + } + + @SuppressWarnings("InvalidInjectorMethodSignature") + @ModifyConstant( + method = "(Lnet/minecraft/world/entity/Entity;)V", + constant = @Constant(classValue = LivingEntity.class, ordinal = 2) + ) + private static boolean redirectInstanceOf2(Object ignored, Class constant) { + return false; + } + + @Inject( + method = "(Lnet/minecraft/world/entity/Entity;)V", + at = @At("TAIL") + ) + private void initFields(Entity entity, CallbackInfo ci) { + this.heldItem = null; + this.canStandOnFluid = null; + } + + @Inject( + method = "isHoldingItem", + at = @At("HEAD") + ) + public void isHolding(Item item, CallbackInfoReturnable cir) { + this.nitori$initHeldItem(); + } + + @Intrinsic + public ItemStack getHeldItem() { + return this.heldItem; + } + + @SuppressWarnings({"UnresolvedMixinReference", "MixinAnnotationTarget"}) + @Inject( + method = "getHeldItem", + at = @At("HEAD") + ) + private void nitori$initHeldItem(CallbackInfoReturnable callbackInfoReturnable) { + this.nitori$initHeldItem(); + } + + @Unique + private void nitori$initHeldItem() { + if (this.heldItem == null) { + this.heldItem = this.entity instanceof LivingEntity ? ((LivingEntity) this.entity).getMainHandItem() : ItemStack.EMPTY; + } + } + + @Inject( + method = "canStandOnFluid", + at = @At("HEAD") + ) + public void canWalkOnFluid(FluidState state, FluidState fluidState, CallbackInfoReturnable cir) { + if (this.canStandOnFluid == null) { + if (this.entity instanceof LivingEntity livingEntity) { + this.canStandOnFluid = livingEntity::canStandOnFluid; + } else { + this.canStandOnFluid = (liquid) -> false; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java new file mode 100644 index 0000000..2b8f3d9 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java @@ -0,0 +1,173 @@ +package net.gensokyoreimagined.nitori.mixin.util.block_tracking; + +import me.jellysquid.mods.lithium.common.block.*; +import net.gensokyoreimagined.nitori.common.entity.block_tracking.ChunkSectionChangeCallback; +import net.gensokyoreimagined.nitori.common.entity.block_tracking.SectionedBlockChangeTracker; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.PalettedContainer; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +/** + * Keep track of how many blocks that meet certain criteria are in this chunk section. + * E.g. if no over-sized blocks are there, collision code can skip a few blocks. + * + * @author 2No2Name + */ +@Mixin(LevelChunkSection.class) +public abstract class ChunkSectionMixin implements BlockCountingSection, BlockListeningSection { + + @Shadow + @Final + private PalettedContainer blockStateContainer; + + @Unique + private short[] countsByFlag = null; + @Unique + private ChunkSectionChangeCallback changeListener; + @Unique + private short listeningMask; + + @Unique + private static void addToFlagCount(short[] countsByFlag, BlockState state, short change) { + int flags = ((BlockStateFlagHolder) state).lithium$getAllFlags(); + int i; + while ((i = Integer.numberOfTrailingZeros(flags)) < 32 && i < countsByFlag.length) { + //either count up by one (prevFlag not set) or down by one (prevFlag set) + countsByFlag[i] += change; + flags &= ~(1 << i); + } + } + + @Override + public boolean lithium$mayContainAny(TrackedBlockStatePredicate trackedBlockStatePredicate) { + if (this.countsByFlag == null) { + fastInitClientCounts(); + } + return this.countsByFlag[trackedBlockStatePredicate.getIndex()] != (short) 0; + } + + @Unique + private void fastInitClientCounts() { + this.countsByFlag = new short[BlockStateFlags.NUM_TRACKED_FLAGS]; + for (TrackedBlockStatePredicate trackedBlockStatePredicate : BlockStateFlags.TRACKED_FLAGS) { + if (this.blockStateContainer.hasAny(trackedBlockStatePredicate)) { + //We haven't counted, so we just set the count so high that it never incorrectly reaches 0. + //For most situations, this overestimation does not hurt client performance compared to correct counting, + this.countsByFlag[trackedBlockStatePredicate.getIndex()] = 16 * 16 * 16; + } + } + } + + @Redirect( + method = "calculateCounts()V", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/chunk/PalettedContainer;count(Lnet/minecraft/world/chunk/PalettedContainer$Counter;)V" + ) + ) + private void initFlagCounters(PalettedContainer palettedContainer, PalettedContainer.Counter consumer) { + palettedContainer.count((state, count) -> { + consumer.accept(state, count); + addToFlagCount(this.countsByFlag, state, (short) count); + }); + } + + @Inject(method = "calculateCounts()V", at = @At("HEAD")) + private void createFlagCounters(CallbackInfo ci) { + this.countsByFlag = new short[BlockStateFlags.NUM_TRACKED_FLAGS]; + } + + @Inject( + method = "readDataPacket", + at = @At(value = "HEAD") + ) + private void resetData(FriendlyByteBuf buf, CallbackInfo ci) { + this.countsByFlag = null; + } + + @Inject( + method = "setBlockState(IIILnet/minecraft/block/BlockState;Z)Lnet/minecraft/block/BlockState;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/block/BlockState;getFluidState()Lnet/minecraft/fluid/FluidState;", + ordinal = 0, + shift = At.Shift.BEFORE + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void updateFlagCounters(int x, int y, int z, BlockState newState, boolean lock, CallbackInfoReturnable cir, BlockState oldState) { + this.lithium$trackBlockStateChange(newState, oldState); + } + + @Override + public void lithium$trackBlockStateChange(BlockState newState, BlockState oldState) { + short[] countsByFlag = this.countsByFlag; + if (countsByFlag == null) { + return; + } + int prevFlags = ((BlockStateFlagHolder) oldState).lithium$getAllFlags(); + int flags = ((BlockStateFlagHolder) newState).lithium$getAllFlags(); + + int flagsXOR = prevFlags ^ flags; + //we need to iterate over indices that changed or are in the listeningMask + //Some Listening Flags are sensitive to both the previous and the new block. Others are only sensitive to + //blocks that are different according to the predicate (XOR). For XOR, the block counting needs to be updated + //as well. + int iterateFlags = (~BlockStateFlags.LISTENING_MASK_OR & flagsXOR) | + (BlockStateFlags.LISTENING_MASK_OR & this.listeningMask & (prevFlags | flags)); + int flagIndex; + + while ((flagIndex = Integer.numberOfTrailingZeros(iterateFlags)) < 32 && flagIndex < countsByFlag.length) { + int flagBit = 1 << flagIndex; + //either count up by one (prevFlag not set) or down by one (prevFlag set) + if ((flagsXOR & flagBit) != 0) { + countsByFlag[flagIndex] += (short) (1 - (((prevFlags >>> flagIndex) & 1) << 1)); + } + if ((this.listeningMask & flagBit) != 0) { + this.listeningMask = this.changeListener.onBlockChange(flagIndex, this); + } + iterateFlags &= ~flagBit; + } + } + + @Override + public void lithium$addToCallback(ListeningBlockStatePredicate blockGroup, SectionedBlockChangeTracker tracker, long sectionPos, Level world) { + if (this.changeListener == null) { + if (sectionPos == Long.MIN_VALUE || world == null) { + throw new IllegalArgumentException("Expected world and section pos during intialization!"); + } + this.changeListener = ChunkSectionChangeCallback.create(sectionPos, world); + } + + this.listeningMask = this.changeListener.addTracker(tracker, blockGroup); + } + + @Override + public void lithium$removeFromCallback(ListeningBlockStatePredicate blockGroup, SectionedBlockChangeTracker tracker) { + if (this.changeListener != null) { + this.listeningMask = this.changeListener.removeTracker(tracker, blockGroup); + } + } + + @Override + @Unique + public void lithium$invalidateListeningSection(ChunkSectionPos sectionPos) { + if (this.listeningMask != 0) { + this.changeListener.onChunkSectionInvalidated(sectionPos); + this.listeningMask = 0; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/WrappedBlockEntityTickInvokerAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/WrappedBlockEntityTickInvokerAccessor.java new file mode 100644 index 0000000..bb77675 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/WrappedBlockEntityTickInvokerAccessor.java @@ -0,0 +1,15 @@ +package net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.sleeping; + +import net.minecraft.world.level.block.entity.TickingBlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(targets = "net/minecraft/world/level/chunk/LevelChunk$RebindableTickingBlockEntityWrapper") +public interface WrappedBlockEntityTickInvokerAccessor { + @Invoker + void callRebind(TickingBlockEntity wrapped); + + @Accessor + TickingBlockEntity getTicker(); +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/CampfireBlockEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/CampfireBlockEntityMixin.java new file mode 100644 index 0000000..2fdfe32 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/CampfireBlockEntityMixin.java @@ -0,0 +1,69 @@ +package net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.sleeping.campfire; + +import net.gensokyoreimagined.nitori.common.block.entity.SleepingBlockEntity; +import net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.sleeping.WrappedBlockEntityTickInvokerAccessor; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.CampfireBlockEntity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.TickingBlockEntity; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(CampfireBlockEntity.class) +public class CampfireBlockEntityMixin extends BlockEntity implements SleepingBlockEntity { + + private WrappedBlockEntityTickInvokerAccessor tickWrapper = null; + private TickingBlockEntity sleepingTicker = null; + + public CampfireBlockEntityMixin(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Override + public WrappedBlockEntityTickInvokerAccessor lithium$getTickWrapper() { + return tickWrapper; + } + + @Override + public void lithium$setTickWrapper(WrappedBlockEntityTickInvokerAccessor tickWrapper) { + this.tickWrapper = tickWrapper; + this.lithium$setSleepingTicker(null); + } + + @Override + public TickingBlockEntity lithium$getSleepingTicker() { + return sleepingTicker; + } + + @Override + public void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker) { + this.sleepingTicker = sleepingTicker; + } + + + @Inject( + method = "placeFood", + at = @At(value = "INVOKE", target = "Lnet/minecraft/core/NonNullList;set(ILjava/lang/Object;)Ljava/lang/Object;") + ) + private void wakeUpOnAddItem(Entity user, ItemStack stack, int cookTime, CallbackInfoReturnable cir) { + this.wakeUpNow(); + } + + @Inject( + method = "loadAdditional", + at = @At(value = "RETURN") + ) + private void wakeUpOnReadNbt(CompoundTag nbt, HolderLookup.Provider registryLookup, CallbackInfo ci) { + this.wakeUpNow(); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/lit/CampfireBlockEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/lit/CampfireBlockEntityMixin.java new file mode 100644 index 0000000..53c3099 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/lit/CampfireBlockEntityMixin.java @@ -0,0 +1,40 @@ +package net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.sleeping.campfire.lit; + +import net.gensokyoreimagined.nitori.common.block.entity.SleepingBlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.CampfireBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.event.block.BlockCookEvent; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.Optional; + +@Mixin(CampfireBlockEntity.class) +public abstract class CampfireBlockEntityMixin extends BlockEntity implements SleepingBlockEntity { + + public CampfireBlockEntityMixin(BlockPos pos, BlockState state) { + super(BlockEntityType.CAMPFIRE, pos, state); + } + + @Inject( + method = "cookTick", + at = @At("RETURN"), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private static void trySleepLit(Level world, BlockPos pos, BlockState state, CampfireBlockEntity campfire, CallbackInfo ci, boolean flag) { + if (!flag) { + CampfireBlockEntityMixin self = (CampfireBlockEntityMixin) (Object) campfire; + self.nitori$startSleeping(); + } + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/unlit/CampfireBlockEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/unlit/CampfireBlockEntityMixin.java new file mode 100644 index 0000000..c2cda7b --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/campfire/unlit/CampfireBlockEntityMixin.java @@ -0,0 +1,34 @@ +package net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.sleeping.campfire.unlit; + +import net.gensokyoreimagined.nitori.common.block.entity.SleepingBlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.CampfireBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(CampfireBlockEntity.class) +public abstract class CampfireBlockEntityMixin extends BlockEntity implements SleepingBlockEntity { + + public CampfireBlockEntityMixin(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Inject( + method = "cooldownTick", + at = @At("RETURN"), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private static void trySleepLit(Level world, BlockPos pos, BlockState state, CampfireBlockEntity campfire, CallbackInfo ci, boolean flag) { + if (!flag) { + CampfireBlockEntityMixin self = (CampfireBlockEntityMixin) (Object) campfire; + self.nitori$startSleeping(); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/furnace/AbstractFurnaceBlockEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/furnace/AbstractFurnaceBlockEntityMixin.java new file mode 100644 index 0000000..3394c69 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/sleeping/furnace/AbstractFurnaceBlockEntityMixin.java @@ -0,0 +1,88 @@ +package net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.sleeping.furnace; + +import net.gensokyoreimagined.nitori.common.block.entity.SleepingBlockEntity; +import net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.sleeping.WrappedBlockEntityTickInvokerAccessor; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.entity.TickingBlockEntity; +import org.spongepowered.asm.mixin.Intrinsic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AbstractFurnaceBlockEntity.class) +public abstract class AbstractFurnaceBlockEntityMixin extends BlockEntity implements SleepingBlockEntity { + + @Shadow + protected abstract boolean isLit(); + + @Shadow + public int cookingProgress; + private WrappedBlockEntityTickInvokerAccessor tickWrapper = null; + private TickingBlockEntity sleepingTicker = null; + + public AbstractFurnaceBlockEntityMixin(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Override + public WrappedBlockEntityTickInvokerAccessor lithium$getTickWrapper() { + return tickWrapper; + } + + @Override + public void lithium$setTickWrapper(WrappedBlockEntityTickInvokerAccessor tickWrapper) { + this.tickWrapper = tickWrapper; + this.lithium$setSleepingTicker(null); + } + + @Override + public TickingBlockEntity lithium$getSleepingTicker() { + return sleepingTicker; + } + + @Override + public void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker) { + this.sleepingTicker = sleepingTicker; + } + + @Inject(method = "serverTick", at = @At("RETURN")) + private static void checkSleep(Level world, BlockPos pos, BlockState state, AbstractFurnaceBlockEntity blockEntity, CallbackInfo ci) { + ((AbstractFurnaceBlockEntityMixin) (Object) blockEntity).checkSleep(state); + } + + private void checkSleep(BlockState state) { + if (!this.isLit() && this.cookingProgress == 0 && (state.is(Blocks.FURNACE) || state.is(Blocks.BLAST_FURNACE) || state.is(Blocks.SMOKER)) && this.level != null) { + this.nitori$startSleeping(); + } + } + + @Inject(method = "loadAdditional", at = @At("RETURN" )) + private void wakeUpAfterFromTag(CallbackInfo ci) { + if (this.isSleeping() && this.level != null && !this.level.isClientSide) { + this.wakeUpNow(); + } + } + + @Override + @Intrinsic + public void setChanged() { + super.setChanged(); + } + + @SuppressWarnings({"MixinAnnotationTarget", "UnresolvedMixinReference"}) + @Inject(method = "setChanged()V", at = @At("RETURN")) + private void wakeOnMarkDirty(CallbackInfo ci) { + if (this.isSleeping() && this.level != null && !this.level.isClientSide) { + this.wakeUpNow(); + } + } + +} \ No newline at end of file diff --git a/src/main/resources/ignite.mod.json b/src/main/resources/ignite.mod.json index e5e9d5c..f3c5000 100755 --- a/src/main/resources/ignite.mod.json +++ b/src/main/resources/ignite.mod.json @@ -1,6 +1,6 @@ { "id": "Nitori", - "version": "1.2-SNAPSHOT", + "version": "1.3-SNAPSHOT", "mixins": [ "mixins.core.json" ] diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 00168bb..01cf8b2 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -13,7 +13,6 @@ "MixinBlockPos", "MixinChunkEntitySlices", "MixinCraftPlayer", - "MixinDirection", "MixinEntity", "MixinEntitySectionStorage", "MixinGameRules", @@ -21,7 +20,7 @@ "MixinLevel", "MixinLevelStorageAccess", "MixinMob", - "MixinMth", + "math.sine_lut.MixinMth", "MixinNoiseBasedChunkGenerator", "MixinPlayer", "MixinPlayerList", @@ -32,11 +31,26 @@ "MixinWorldGenRegion", "alloc.chunk_ticking.ServerChunkManagerMixin", "alloc.blockstate.StateMixin", + "alloc.composter.ComposterMixin$ComposterBlockFullComposterInventoryMixin", + "alloc.composter.ComposterMixin$ComposterBlockDummyInventoryMixin", + "alloc.composter.ComposterMixin$ComposterBlockComposterInventoryMixin", "util.MixinLevelBlockEntityRetrieval", "cached_hashcode.BlockNeighborGroupMixin", "shapes.blockstate_cache.BlockMixin", + "shapes.lazy_shape_context.EntityShapeContextMixin", + "entity.fast_retrieval.SectionedEntityCacheMixin", "math.fast_blockops.DirectionMixin", "math.fast_blockops.BlockPosMixin", - "ai.sensor.secondary_poi.SecondaryPointsOfInterestSensorMixin", + "math.fast_util.AxisCycleDirectionMixin$ForwardMixin", + "math.fast_util.AxisCycleDirectionMixin$BackwardMixin", + "math.fast_util.DirectionMixin", + "collections.entity_filtering.TypeFilterableListMixin", + "collections.entity_by_type.TypeFilterableListMixin", + "collections.block_entity_tickers.WorldChunkMixin", + "world.block_entity_ticking.sleeping.WrappedBlockEntityTickInvokerAccessor", + "world.block_entity_ticking.sleeping.campfire.CampfireBlockEntityMixin", + "world.block_entity_ticking.sleeping.campfire.unlit.CampfireBlockEntityMixin", + "world.block_entity_ticking.sleeping.campfire.lit.CampfireBlockEntityMixin", + "world.block_entity_ticking.sleeping.furnace.AbstractFurnaceBlockEntityMixin" ] } From 58988967049807f6a9a3b70be1b14a1c8d59617e Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 7 Jul 2024 11:31:30 +0300 Subject: [PATCH 02/21] send help --- .../common/ai/pathing/PathNodeCache.java | 22 +-- .../common/block/BlockCountingSection.java | 9 ++ .../nitori/common/block/BlockStateFlags.java | 16 +-- .../block/TrackedBlockStatePredicate.java | 30 +++++ .../common/entity/FluidCachingEntity.java | 4 + .../common/reflection/ReflectionUtil.java | 71 ++++++++++ .../ClientEntityManagerAccessor.java | 13 ++ .../nitori/common/world/ChunkView.java | 10 ++ .../nitori/common/world/WorldHelper.java | 126 ++++++++++++++++++ .../mixin/ai/pathing/PathContextAccessor.java | 14 ++ .../block_tracking/ChunkSectionMixin.java | 2 +- 11 files changed, 297 insertions(+), 20 deletions(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/BlockCountingSection.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/TrackedBlockStatePredicate.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/entity/FluidCachingEntity.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/reflection/ReflectionUtil.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/util/accessors/ClientEntityManagerAccessor.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/world/ChunkView.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/world/WorldHelper.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/ai/pathing/PathContextAccessor.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java index c6884ac..57d5f3e 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java @@ -11,13 +11,13 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.pathfinder.WalkNodeEvaluator; import net.minecraft.world.level.pathfinder.BinaryHeap; import net.minecraft.entity.ai.pathing.PathNodeType; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.BlockView; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.ChunkSection; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunkSection; public abstract class PathNodeCache { - private static boolean isChunkSectionDangerousNeighbor(ChunkSection section) { + private static boolean isChunkSectionDangerousNeighbor(LevelChunkSection section) { return section.getBlockStateContainer() .hasAny(state -> getNeighborPathNodeType(state) != PathNodeType.OPEN); } @@ -38,9 +38,9 @@ public abstract class PathNodeCache { * @return True if this neighboring section is free of any dangers, otherwise false if it could * potentially contain dangers */ - public static boolean isSectionSafeAsNeighbor(ChunkSection section) { + public static boolean isSectionSafeAsNeighbor(LevelChunkSection section) { // Empty sections can never contribute a danger - if (section.isEmpty()) { + if (section.hasOnlyAir()) { return true; } @@ -52,9 +52,9 @@ public abstract class PathNodeCache { public static PathNodeType getNodeTypeFromNeighbors(BinaryHeap context, int x, int y, int z, PathNodeType fallback) { - BlockView world = context.getWorld(); + BlockGetter world = context.getWorld(); - ChunkSection section = null; + LevelChunkSection section = null; // Check that all the block's neighbors are within the same chunk column. If so, we can isolate all our block // reads to just one chunk and avoid hits against the server chunk manager. @@ -62,12 +62,12 @@ public abstract class PathNodeCache { // If the y-coordinate is within bounds, we can cache the chunk section. Otherwise, the if statement to check // if the cached chunk section was initialized will early-exit. if (!world.isOutOfHeightLimit(y)) { - Chunk chunk = chunkView.lithium$getLoadedChunk(Pos.ChunkCoord.fromBlockCoord(x), Pos.ChunkCoord.fromBlockCoord(z)); + ChunkAccess chunk = chunkView.lithium$getLoadedChunk(Pos.ChunkCoord.fromBlockCoord(x), Pos.ChunkCoord.fromBlockCoord(z)); // If the chunk is absent, the cached section above will remain null, as there is no chunk section anyway. // An empty chunk or section will never pose any danger sources, which will be caught later. if (chunk != null) { - section = chunk.getSectionArray()[Pos.SectionYIndex.fromBlockCoord(world, y)]; + section = chunk.getSections()[Pos.SectionYIndex.fromBlockCoord(world, y)]; } } diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockCountingSection.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockCountingSection.java new file mode 100644 index 0000000..0e5ab74 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockCountingSection.java @@ -0,0 +1,9 @@ +package net.gensokyoreimagined.nitori.common.block; + +import net.minecraft.world.level.block.state.BlockState; + +public interface BlockCountingSection { + boolean lithium$mayContainAny(TrackedBlockStatePredicate trackedBlockStatePredicate); + + void lithium$trackBlockStateChange(BlockState newState, BlockState oldState); +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java index 92e4ae9..7c8018a 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java @@ -5,17 +5,17 @@ import net.gensokyoreimagined.nitori.common.ai.pathing.BlockStatePathingCache; import net.gensokyoreimagined.nitori.common.ai.pathing.PathNodeCache; import net.gensokyoreimagined.nitori.common.entity.FluidCachingEntity; import net.gensokyoreimagined.nitori.common.reflection.ReflectionUtil; -import net.minecraft.block.AbstractBlock; -import net.minecraft.block.BlockState; -import net.minecraft.entity.Entity; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.entity.Entity; import net.minecraft.entity.ai.pathing.PathNodeType; -import net.minecraft.registry.tag.FluidTags; -import net.minecraft.world.chunk.ChunkSection; +import net.minecraft.tags.FluidTags; +import net.minecraft.world.level.chunk.LevelChunkSection +import net.minecraft.world.level.block.state.BlockState; import java.util.ArrayList; public class BlockStateFlags { - public static final boolean ENABLED = BlockCountingSection.class.isAssignableFrom(ChunkSection.class); + public static final boolean ENABLED = BlockCountingSection.class.isAssignableFrom(LevelChunkSection.class); public static final int NUM_LISTENING_FLAGS; public static final ListeningBlockStatePredicate[] LISTENING_FLAGS; @@ -67,7 +67,7 @@ public class BlockStateFlags { OVERSIZED_SHAPE = new TrackedBlockStatePredicate(countingFlags.size()) { @Override public boolean test(BlockState operand) { - return operand.exceedsCube(); + return operand.shapeExceedsCube(); } }; countingFlags.add(OVERSIZED_SHAPE); @@ -93,7 +93,7 @@ public class BlockStateFlags { LAVA = null; } - if (BlockStatePathingCache.class.isAssignableFrom(AbstractBlock.AbstractBlockState.class)) { + if (BlockStatePathingCache.class.isAssignableFrom(BlockBehaviour.BlockBehaviourState.class)) { PATH_NOT_OPEN = new TrackedBlockStatePredicate(countingFlags.size()) { @Override public boolean test(BlockState operand) { diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/TrackedBlockStatePredicate.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/TrackedBlockStatePredicate.java new file mode 100644 index 0000000..35cd6f9 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/TrackedBlockStatePredicate.java @@ -0,0 +1,30 @@ +package net.gensokyoreimagined.nitori.common.block; + +import net.minecraft.world.level.block.state.BlockState; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; + +public abstract class TrackedBlockStatePredicate implements Predicate { + public static final AtomicBoolean FULLY_INITIALIZED; + + static { + FULLY_INITIALIZED = new AtomicBoolean(false); + if (!BlockStateFlags.ENABLED) { //classload the BlockStateFlags class which initializes the content of ALL_FLAGS + System.out.println("Lithium Cached BlockState Flags are disabled!"); + } + } + + private final int index; + + public TrackedBlockStatePredicate(int index) { + if (FULLY_INITIALIZED.get()) { + throw new IllegalStateException("Lithium Cached BlockState Flags: Cannot register more flags after assuming to be fully initialized."); + } + this.index = index; + } + + public int getIndex() { + return this.index; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/FluidCachingEntity.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/FluidCachingEntity.java new file mode 100644 index 0000000..3b29bd9 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/FluidCachingEntity.java @@ -0,0 +1,4 @@ +package net.gensokyoreimagined.nitori.common.entity; + +public interface FluidCachingEntity { +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/reflection/ReflectionUtil.java b/src/main/java/net/gensokyoreimagined/nitori/common/reflection/ReflectionUtil.java new file mode 100644 index 0000000..673aa6b --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/reflection/ReflectionUtil.java @@ -0,0 +1,71 @@ +package net.gensokyoreimagined.nitori.common.reflection; + +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.block.AbstractBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.util.crash.CrashException; +import net.minecraft.util.crash.CrashReport; +import net.minecraft.util.crash.CrashReportSection; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.WeakHashMap; + +public class ReflectionUtil { +// +// public static boolean hasMethodOverride(Class clazz, Class superclass, boolean fallbackResult, String methodName, Class... methodArgs) { +// while (clazz != null && clazz != superclass && superclass.isAssignableFrom(clazz)) { +// try { +// clazz.getDeclaredMethod(methodName, methodArgs); +// return true; +// } catch (NoSuchMethodException e) { +// clazz = clazz.getSuperclass(); +// } catch (NoClassDefFoundError error) { +// Logger logger = LogManager.getLogger("Lithium Class Analysis"); +// logger.warn("Lithium Class Analysis Error: Class " + clazz.getName() + " cannot be analysed, because" + +// " getting declared methods crashes with NoClassDefFoundError: " + error.getMessage() + +// ". This is usually caused by modded" + +// " entities declaring methods that have a return type or parameter type that is annotated" + +// " with @Environment(value=EnvType.CLIENT). Loading the type is not possible, because" + +// " it only exists in the CLIENT environment. The recommended fix is to annotate the method with" + +// " this argument or return type with the same annotation." + +// " Lithium handles this error by assuming the class cannot be included in some optimizations."); +// return fallbackResult; +// } catch (Throwable e) { +// final String crashedClass = clazz.getName(); +// CrashReport crashReport = CrashReport.create(e, "Lithium Class Analysis"); +// CrashReportSection crashReportSection = crashReport.addElement(e.getClass().toString() + " when getting declared methods."); +// crashReportSection.add("Analyzed class", crashedClass); +// crashReportSection.add("Analyzed method name", methodName); +// crashReportSection.add("Analyzed method args", methodArgs); +// +// throw new CrashException(crashReport); +// } +// } +// return false; +// } +// +// //How to find the remapped methods: +// //1) Run in the debugger: System.out.println(FabricLoader.getInstance().getMappingResolver().getNamespaceData("intermediary").methodNames) +// //2) Ctrl+F for the method name, in this case "onEntityCollision". Make sure to find the correct one. +// private static final String REMAPPED_ON_ENTITY_COLLISION = FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_4970", "method_9548", "(Lnet/minecraft/class_2680;Lnet/minecraft/class_1937;Lnet/minecraft/class_2338;Lnet/minecraft/class_1297;)V"); +// private static final WeakHashMap, Boolean> CACHED_IS_ENTITY_TOUCHABLE = new WeakHashMap<>(); +// public static boolean isBlockStateEntityTouchable(BlockState operand) { +// Class blockClazz = operand.getBlock().getClass(); +// //Caching results in hashmap as this calculation takes over a second for all blocks together +// Boolean result = CACHED_IS_ENTITY_TOUCHABLE.get(blockClazz); +// if (result != null) { +// return result; +// } +// boolean res = ReflectionUtil.hasMethodOverride(blockClazz, AbstractBlock.class, true, REMAPPED_ON_ENTITY_COLLISION, BlockState.class, World.class, BlockPos.class, Entity.class); +// CACHED_IS_ENTITY_TOUCHABLE.put(blockClazz, res); +// return res; + } +//} + + +//TODO: There's bunch of client sided options, we need to safely remove them if possible if not keep the behaviour since mixins work regardless of client or not \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/util/accessors/ClientEntityManagerAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/common/util/accessors/ClientEntityManagerAccessor.java new file mode 100644 index 0000000..c3ce794 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/util/accessors/ClientEntityManagerAccessor.java @@ -0,0 +1,13 @@ +package net.gensokyoreimagined.nitori.common.util.accessors; + +import net.minecraft.world.level.entity.TransientEntitySectionManager; +import net.minecraft.world.level.entity.EntityAccess; +import net.minecraft.world.level.entity.EntitySectionStorage; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(TransientEntitySectionManager.class) +public interface ClientEntityManagerAccessor { + @Accessor + EntitySectionStorage getCache(); +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/ChunkView.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/ChunkView.java new file mode 100644 index 0000000..18060d7 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/ChunkView.java @@ -0,0 +1,10 @@ +package net.gensokyoreimagined.nitori.common.world; + +import net.minecraft.world.level.chunk.ChunkAccess; +import org.jetbrains.annotations.Nullable; + +public interface ChunkView { + + @Nullable + ChunkAccess lithium$getLoadedChunk(int chunkX, int chunkZ); +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/WorldHelper.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/WorldHelper.java new file mode 100644 index 0000000..4ccbf9d --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/WorldHelper.java @@ -0,0 +1,126 @@ +package net.gensokyoreimagined.nitori.common.world; + +import net.gensokyoreimagined.nitori.common.client.ClientWorldAccessor; +import net.gensokyoreimagined.nitori.common.entity.EntityClassGroup; +import net.gensokyoreimagined.nitori.common.entity.pushable.EntityPushablePredicate; +import net.gensokyoreimagined.nitori.common.world.chunk.ClassGroupFilterableList; +import net.gensokyoreimagined.nitori.common.util.accessors.ClientEntityManagerAccessor; +import net.gensokyoreimagined.nitori.common.util.accessors.EntityTrackingSectionAccessor; +import net.gensokyoreimagined.nitori.common.util.accessors.ServerEntityManagerAccessor; +import net.gensokyoreimagined.nitori.common.util.accessors.ServerWorldAccessor; +import net.minecraft.core.BlockPos; +import net.minecraft.util.ClassInstanceMultiMap; +import net.minecraft.util.AbortableIterationConsumer; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.level.EntityGetter +import net.minecraft.world.level.Level; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.entity.EntitySectionStorage; +import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; + +public class WorldHelper { + public static final boolean CUSTOM_TYPE_FILTERABLE_LIST_DISABLED = !ClassGroupFilterableList.class.isAssignableFrom(ClassInstanceMultiMap.class); + + /** + * Partial [VanillaCopy] + * The returned entity iterator is only used for collision interactions. As most entities do not collide with other + * entities (cramming is different), getting them is not necessary. This is why we only get entities when they override + * {@link Entity#isCollidable()} if the reference entity does not override {@link Entity#collidesWith(Entity)}. + * Note that the returned iterator contains entities that override these methods. This does not mean that these methods + * always return true. + * + * @param entityView the world + * @param box the box the entities have to collide with + * @param collidingEntity the entity that is searching for the colliding entities + * @return iterator of entities with collision boxes + */ + public static List getEntitiesForCollision(EntityGetter entityView, AABB box, Entity collidingEntity) { + if (!CUSTOM_TYPE_FILTERABLE_LIST_DISABLED && entityView instanceof Level world && (collidingEntity == null || !EntityClassGroup.CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE.contains(collidingEntity.getClass()))) { + EntitySectionStorage cache = getEntityCacheOrNull(world); + if (cache != null) { + world.getProfiler().visit("getEntities"); + return getEntitiesOfClassGroup(cache, collidingEntity, EntityClassGroup.NoDragonClassGroup.BOAT_SHULKER_LIKE_COLLISION, box); + } + } + //use vanilla code in case the shortcut is not applicable + // due to the reference entity implementing special collision or the mixin being disabled in the config + return EntityGetter.getOtherEntities(collidingEntity, box); + } + + public static List getOtherEntitiesForCollision(EntityGetter entityView, AABB box, @Nullable Entity collidingEntity, Predicate entityPredicate) { + if (!CUSTOM_TYPE_FILTERABLE_LIST_DISABLED && entityView instanceof Level world) { + if (collidingEntity == null || !EntityClassGroup.CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE.contains(collidingEntity.getClass())) { + EntitySectionStorage cache = getEntityCacheOrNull(world); + if (cache != null) { + world.getProfiler().visit("getEntities"); + return getEntitiesOfClassGroup(cache, collidingEntity, EntityClassGroup.NoDragonClassGroup.BOAT_SHULKER_LIKE_COLLISION, box); + } + } + } + //use vanilla code in case the shortcut is not applicable + // due to the reference entity implementing special collision or the mixin being disabled in the config + return entityView.getOtherEntities(collidingEntity, box, entityPredicate); + } + + + //Requires util.accessors + public static EntitySectionStorage getEntityCacheOrNull(Level world) { + if (world instanceof ClientWorldAccessor) { + //noinspection unchecked + return ((ClientEntityManagerAccessor) ((ClientWorldAccessor) world).lithium$getEntityManager()).getCache(); + } else if (world instanceof ServerWorldAccessor) { + //noinspection unchecked + return ((ServerEntityManagerAccessor) ((ServerWorldAccessor) world).getEntityManager()).getCache(); + } + return null; + } + + public static List getEntitiesOfClassGroup(EntitySectionStorage cache, Entity collidingEntity, EntityClassGroup.NoDragonClassGroup entityClassGroup, AABB box) { + ArrayList entities = new ArrayList<>(); + cache.forEachInBox(box, section -> { + //noinspection unchecked + ClassInstanceMultiMap allEntities = ((EntityTrackingSectionAccessor) section).getCollection(); + //noinspection unchecked + Collection entitiesOfType = ((ClassGroupFilterableList) allEntities).lithium$getAllOfGroupType(entityClassGroup); + if (!entitiesOfType.isEmpty()) { + for (Entity entity : entitiesOfType) { + if (entity.getBoundingBox().intersects(box) && !entity.isSpectator() && entity != collidingEntity) { + //skip the dragon piece check without issues by only allowing EntityClassGroup.NoDragonClassGroup as type + entities.add(entity); + } + } + } + return AbortableIterationConsumer.Continuation.CONTINUE; + }); + return entities; + } + + public static List getPushableEntities(Level world, EntitySectionStorage cache, Entity except, AABB box, EntityPushablePredicate entityPushablePredicate) { + ArrayList entities = new ArrayList<>(); + cache.forEachInBox(box, section -> ((ClimbingMobCachingSection) section).lithium$collectPushableEntities(world, except, box, entityPushablePredicate, entities)); + return entities; + } + + public static boolean areNeighborsWithinSameChunk(BlockPos pos) { + int localX = pos.getX() & 15; + int localZ = pos.getZ() & 15; + + return localX > 0 && localZ > 0 && localX < 15 && localZ < 15; + } + + public static boolean areNeighborsWithinSameChunkSection(int x, int y, int z) { + int localX = x & 15; + int localY = y & 15; + int localZ = z & 15; + + return localX > 0 && localY > 0 && localZ > 0 && localX < 15 && localY < 15 && localZ < 15; + } + + public static boolean arePosWithinSameChunk(BlockPos pos1, BlockPos pos2) { + return pos1.getX() >> 4 == pos2.getX() >> 4 && pos1.getZ() >> 4 == pos2.getZ() >> 4; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/ai/pathing/PathContextAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/ai/pathing/PathContextAccessor.java new file mode 100644 index 0000000..3dde0a3 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/ai/pathing/PathContextAccessor.java @@ -0,0 +1,14 @@ +package net.gensokyoreimagined.nitori.mixin.ai.pathing; + +import net.minecraft.world.level.pathfinder.Path; +import net.minecraft.core.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Path.class) +public interface PathContextAccessor { + + @Accessor + BlockPos.MutableBlockPos getLastNodePos(); + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java index 2b8f3d9..86c14b1 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java @@ -1,6 +1,6 @@ package net.gensokyoreimagined.nitori.mixin.util.block_tracking; -import me.jellysquid.mods.lithium.common.block.*; +import net.gensokyoreimagined.nitori.common.block.*; import net.gensokyoreimagined.nitori.common.entity.block_tracking.ChunkSectionChangeCallback; import net.gensokyoreimagined.nitori.common.entity.block_tracking.SectionedBlockChangeTracker; import net.minecraft.world.level.block.state.BlockState; From c1e1e5d27cef8ffe7ad37caeaf20b1d8eab438f1 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sun, 7 Jul 2024 11:49:30 +0300 Subject: [PATCH 03/21] ok i eep --- .../common/ai/pathing/PathNodeCache.java | 2 +- .../common/block/BlockStateFlagHolder.java | 5 ++++ .../ChunkSectionChangeCallback.java | 2 +- .../world/chunk/ChunkStatusTracker.java | 26 +++++++++++++++++++ .../block_tracking/ChunkSectionMixin.java | 6 ++--- 5 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlagHolder.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/ChunkStatusTracker.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java index 57d5f3e..881b94f 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java @@ -18,7 +18,7 @@ import net.minecraft.world.level.chunk.LevelChunkSection; public abstract class PathNodeCache { private static boolean isChunkSectionDangerousNeighbor(LevelChunkSection section) { - return section.getBlockStateContainer() + return section.get() .hasAny(state -> getNeighborPathNodeType(state) != PathNodeType.OPEN); } diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlagHolder.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlagHolder.java new file mode 100644 index 0000000..c269100 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlagHolder.java @@ -0,0 +1,5 @@ +package net.gensokyoreimagined.nitori.common.block; + +public interface BlockStateFlagHolder { + int lithium$getAllFlags(); +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/ChunkSectionChangeCallback.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/ChunkSectionChangeCallback.java index 747622c..769c28e 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/ChunkSectionChangeCallback.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/block_tracking/ChunkSectionChangeCallback.java @@ -45,7 +45,7 @@ public final class ChunkSectionChangeCallback { Long2ReferenceOpenHashMap changeCallbacks = ((LithiumData) world).lithium$getData().chunkSectionChangeCallbacks(); ChunkSectionChangeCallback previous = changeCallbacks.put(sectionPos, chunkSectionChangeCallback); if (previous != null) { - previous.onChunkSectionInvalidated(SectionPos.from(sectionPos)); + previous.onChunkSectionInvalidated(SectionPos.of(sectionPos)); } return chunkSectionChangeCallback; } diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/ChunkStatusTracker.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/ChunkStatusTracker.java new file mode 100644 index 0000000..85e5d17 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/ChunkStatusTracker.java @@ -0,0 +1,26 @@ +package net.gensokyoreimagined.nitori.common.world.chunk; + +import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.ChunkPos; + +import java.util.ArrayList; +import java.util.function.BiConsumer; + +public class ChunkStatusTracker { + + //Add other callback types in the future when needed + private static final ArrayList> UNLOAD_CALLBACKS = new ArrayList<>(); + public static void onChunkStatusChange(ServerLevel serverWorld, ChunkPos pos, FullChunkStatus levelType) { + boolean loaded = levelType.isOrAfter(FullChunkStatus.FULL); + if (!loaded) { + for (int i = 0; i < UNLOAD_CALLBACKS.size(); i++) { + UNLOAD_CALLBACKS.get(i).accept(serverWorld, pos); + } + } + } + + public static void registerUnloadCallback(BiConsumer callback) { + UNLOAD_CALLBACKS.add(callback); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java index 86c14b1..419f28f 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java @@ -4,7 +4,7 @@ import net.gensokyoreimagined.nitori.common.block.*; import net.gensokyoreimagined.nitori.common.entity.block_tracking.ChunkSectionChangeCallback; import net.gensokyoreimagined.nitori.common.entity.block_tracking.SectionedBlockChangeTracker; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.network.FriendlyByteBuf +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.core.SectionPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.chunk.LevelChunkSection; @@ -91,7 +91,7 @@ public abstract class ChunkSectionMixin implements BlockCountingSection, BlockLi } @Inject( - method = "readDataPacket", + method = "read", at = @At(value = "HEAD") ) private void resetData(FriendlyByteBuf buf, CallbackInfo ci) { @@ -164,7 +164,7 @@ public abstract class ChunkSectionMixin implements BlockCountingSection, BlockLi @Override @Unique - public void lithium$invalidateListeningSection(ChunkSectionPos sectionPos) { + public void lithium$invalidateListeningSection(SectionPos sectionPos) { if (this.listeningMask != 0) { this.changeListener.onChunkSectionInvalidated(sectionPos); this.listeningMask = 0; From 5bb7e63d75dde7dcb6ef360cf0e434867e1be772 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Mon, 8 Jul 2024 16:28:30 +0300 Subject: [PATCH 04/21] perf testing begins --- .../api/inventory/LithiumInventory.java | 60 ++++++ .../ai/pathing/BlockStatePathingCache.java | 6 +- .../common/ai/pathing/PathNodeCache.java | 28 +-- .../common/block/BlockStateCounter.java | 28 +++ .../nitori/common/block/BlockStateFlags.java | 12 +- .../SectionedEntityMovementTracker.java | 4 - .../common/entity/EntityClassGroup.java | 111 ++++++++++ .../EntityMovementTrackerSection.java | 18 ++ .../MovementTrackerHelper.java | 54 +++++ .../SectionedEntityMovementListener.java | 5 + .../SectionedEntityMovementTracker.java | 197 ++++++++++++++++++ .../entity/pushable/BlockCachingEntity.java | 20 ++ .../pushable/EntityPushablePredicate.java | 14 ++ .../common/reflection/ReflectionUtil.java | 118 +++++------ .../world/ClimbingMobCachingSection.java | 17 ++ .../nitori/common/world/LithiumData.java | 4 +- .../nitori/common/world/WorldHelper.java | 39 ++-- .../world/chunk/ClassGroupFilterableList.java | 8 + .../mixin/ai/pathing/PathContextAccessor.java | 8 +- .../ClientEntityManagerAccessor.java | 6 +- .../EntityTrackingSectionAccessor.java | 12 ++ .../ServerEntityManagerAccessor.java | 13 ++ .../util/accessors/ServerWorldAccessor.java | 14 ++ .../AbstractBlockStateMixin.java | 44 ++++ .../block_tracking/ChunkSectionMixin.java | 63 ++++-- src/main/resources/mixins.core.json | 7 +- 26 files changed, 771 insertions(+), 139 deletions(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/api/inventory/LithiumInventory.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateCounter.java delete mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/block/entity/movement_tracker/SectionedEntityMovementTracker.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/entity/EntityClassGroup.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/EntityMovementTrackerSection.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/MovementTrackerHelper.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/SectionedEntityMovementListener.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/SectionedEntityMovementTracker.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/entity/pushable/BlockCachingEntity.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/entity/pushable/EntityPushablePredicate.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/world/ClimbingMobCachingSection.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/ClassGroupFilterableList.java rename src/main/java/net/gensokyoreimagined/nitori/{common => mixin}/util/accessors/ClientEntityManagerAccessor.java (78%) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/EntityTrackingSectionAccessor.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ServerEntityManagerAccessor.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ServerWorldAccessor.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/AbstractBlockStateMixin.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/api/inventory/LithiumInventory.java b/src/main/java/net/gensokyoreimagined/nitori/api/inventory/LithiumInventory.java new file mode 100644 index 0000000..6dd14af --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/api/inventory/LithiumInventory.java @@ -0,0 +1,60 @@ +package net.gensokyoreimagined.nitori.api.inventory; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.entity.vehicle.ContainerEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; + +/** + * Provides the ability for mods to allow Lithium's hopper optimizations to access their inventories' for item transfers. + * This exists because Lithium's optimized hopper logic will only interact with inventories more efficiently than + * vanilla if the stack list can be directly accessed and replaced with Lithium's custom stack list. + * It is not required to implement this interface, but doing so will allow the mod's inventories to benefit from + * Lithium's optimizations. + *

+ * This interface should be implemented by your {@link net.minecraft.inventory.Inventory} or + * {@link net.minecraft.inventory.SidedInventory} type to access the stack list. + *

+ * An inventory must not extend {@link net.minecraft.block.entity.BlockEntity} if it has a supporting block that + * implements {@link net.minecraft.block.InventoryProvider}. + *

+ * The hopper interaction behavior of a LithiumInventory should only change if the content of the inventory + * stack list also changes. For example, an inventory which only accepts an item if it already contains an item of the + * same type would work fine (changing the acceptance condition only happens when changing the inventory contents here). + * However, an inventory which accepts an item only if a certain block is near its position will need to signal this + * change to hoppers by calling {@link LithiumNonNullList#changedInteractionConditions()}. + * + * @author 2No2Name + */ +public interface LithiumInventory extends ContainerEntity { + + /** + * Getter for the inventory stack list of this inventory. + * + * @return inventory stack list + */ + NonNullList getInventoryLithium(); + + /** + * Setter for the inventory stack list of this inventory. + * Used to replace the stack list with Lithium's custom stack list. + * + * @param inventory inventory stack list + */ + void setInventoryLithium(NonNullList inventory); + + /** + * Generates the loot like a hopper access would do in vanilla. + *

+ * If a modded inventory has custom loot generation code, it will be required to override this + * loot generation method. Otherwise, its loot may be generated too late. + */ + default void generateLootLithium() { + if (this instanceof RandomizableContainerBlockEntity) { + ((RandomizableContainerBlockEntity) this).unpackLootTable(null); + } + if (this instanceof ContainerEntity) { + ((ContainerEntity) this).unpackChestVehicleLootTable(null); + } + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/BlockStatePathingCache.java b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/BlockStatePathingCache.java index 054a8b9..593b87b 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/BlockStatePathingCache.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/BlockStatePathingCache.java @@ -1,4 +1,8 @@ package net.gensokyoreimagined.nitori.common.ai.pathing; -public class BlockStatePathingCache { +import net.minecraft.world.level.pathfinder.PathType; + +public interface BlockStatePathingCache { + PathType lithium$getPathNodeType(); + PathType lithium$getNeighborPathNodeType(); } diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java index 881b94f..6ac7680 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/ai/pathing/PathNodeCache.java @@ -5,28 +5,28 @@ import net.gensokyoreimagined.nitori.common.block.BlockStateFlags; import net.gensokyoreimagined.nitori.common.util.Pos; import net.gensokyoreimagined.nitori.common.world.ChunkView; import net.gensokyoreimagined.nitori.common.world.WorldHelper; -import me.jellysquid.mods.lithium.mixin.ai.pathing.PathContextAccessor; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.pathfinder.PathfindingContext; import net.minecraft.world.level.pathfinder.WalkNodeEvaluator; import net.minecraft.world.level.pathfinder.BinaryHeap; -import net.minecraft.entity.ai.pathing.PathNodeType; import net.minecraft.core.BlockPos; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.pathfinder.PathType; +import net.gensokyoreimagined.nitori.mixin.ai.pathing.PathContextAccessor; public abstract class PathNodeCache { private static boolean isChunkSectionDangerousNeighbor(LevelChunkSection section) { - return section.get() - .hasAny(state -> getNeighborPathNodeType(state) != PathNodeType.OPEN); + return section.getStates().maybeHas(state -> getNeighborPathType(state) != PathType.OPEN); } - public static PathNodeType getPathNodeType(BlockState state) { + public static PathType getPathType(BlockState state) { return ((BlockStatePathingCache) state).lithium$getPathNodeType(); } - public static PathNodeType getNeighborPathNodeType(BlockBehaviour.AbstractBlockState state) { + public static PathType getNeighborPathType(BlockBehaviour.BlockStateBase state) { return ((BlockStatePathingCache) state).lithium$getNeighborPathNodeType(); } @@ -51,8 +51,8 @@ public abstract class PathNodeCache { } - public static PathNodeType getNodeTypeFromNeighbors(BinaryHeap context, int x, int y, int z, PathNodeType fallback) { - BlockGetter world = context.getWorld(); + public static PathType getNodeTypeFromNeighbors(PathfindingContext context, int x, int y, int z, PathType fallback) { + BlockGetter world = context.level(); LevelChunkSection section = null; @@ -61,7 +61,7 @@ public abstract class PathNodeCache { if (world instanceof ChunkView chunkView && WorldHelper.areNeighborsWithinSameChunkSection(x, y, z)) { // If the y-coordinate is within bounds, we can cache the chunk section. Otherwise, the if statement to check // if the cached chunk section was initialized will early-exit. - if (!world.isOutOfHeightLimit(y)) { + if (!world.isOutsideBuildHeight(y)) { ChunkAccess chunk = chunkView.lithium$getLoadedChunk(Pos.ChunkCoord.fromBlockCoord(x), Pos.ChunkCoord.fromBlockCoord(z)); // If the chunk is absent, the cached section above will remain null, as there is no chunk section anyway. @@ -103,7 +103,7 @@ public abstract class PathNodeCache { if (section != null) { state = section.getBlockState(adjX & 15, adjY & 15, adjZ & 15); } else { - BlockPos.Mutable pos = ((PathContextAccessor) context).getLastNodePos().set(adjX, adjY, adjZ); + BlockPos.MutableBlockPos pos = ((PathContextAccessor) context).getMutablePos().set(adjX, adjY, adjZ); state = world.getBlockState(pos); } @@ -111,17 +111,17 @@ public abstract class PathNodeCache { continue; } - PathNodeType neighborType = PathNodeCache.getNeighborPathNodeType(state); + PathType neighborType = PathNodeCache.getNeighborPathType(state); if (neighborType == null) { //Here null means that no path node type is cached (uninitialized or dynamic) //Passing null as previous node type to the method signals to other lithium mixins that we only want the neighbor behavior of this block and not its neighbors - neighborType = WalkNodeEvaluator.getNodeTypeFromNeighbors(context, adjX + 1, adjY + 1, adjZ + 1, null); + neighborType = WalkNodeEvaluator.checkNeighbourBlocks(context, adjX + 1, adjY + 1, adjZ + 1, null); //Here null means that the path node type is not changed by the block! if (neighborType == null) { - neighborType = PathNodeType.OPEN; + neighborType = PathType.OPEN; } } - if (neighborType != PathNodeType.OPEN) { + if (neighborType != PathType.OPEN) { return neighborType; } } diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateCounter.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateCounter.java new file mode 100644 index 0000000..3fb3179 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateCounter.java @@ -0,0 +1,28 @@ +package net.gensokyoreimagined.nitori.common.block; + +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.material.FluidState; + +public class BlockStateCounter implements PalettedContainer.CountConsumer { + public int nonEmptyBlockCount; + public int randomTickableBlockCount; + public int nonEmptyFluidCount; + + @Override + public void accept(BlockState arg, int i) { + FluidState lv = arg.getFluidState(); + if (!arg.isAir()) { + this.nonEmptyBlockCount += i; + if (arg.isRandomlyTicking()) { + this.randomTickableBlockCount += i; + } + } + if (!lv.isEmpty()) { + this.nonEmptyBlockCount += i; + if (lv.isRandomlyTicking()) { + this.nonEmptyFluidCount += i; + } + } + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java index 7c8018a..93a9b4a 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/block/BlockStateFlags.java @@ -7,10 +7,10 @@ import net.gensokyoreimagined.nitori.common.entity.FluidCachingEntity; import net.gensokyoreimagined.nitori.common.reflection.ReflectionUtil; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.entity.Entity; -import net.minecraft.entity.ai.pathing.PathNodeType; import net.minecraft.tags.FluidTags; -import net.minecraft.world.level.chunk.LevelChunkSection +import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.pathfinder.PathType; import java.util.ArrayList; @@ -76,7 +76,7 @@ public class BlockStateFlags { WATER = new TrackedBlockStatePredicate(countingFlags.size()) { @Override public boolean test(BlockState operand) { - return operand.getFluidState().getFluid().isIn(FluidTags.WATER); + return operand.getFluidState().getType().is(FluidTags.WATER); } }; countingFlags.add(WATER); @@ -84,7 +84,7 @@ public class BlockStateFlags { LAVA = new TrackedBlockStatePredicate(countingFlags.size()) { @Override public boolean test(BlockState operand) { - return operand.getFluidState().getFluid().isIn(FluidTags.LAVA); + return operand.getFluidState().getType().is(FluidTags.LAVA); } }; countingFlags.add(LAVA); @@ -93,11 +93,11 @@ public class BlockStateFlags { LAVA = null; } - if (BlockStatePathingCache.class.isAssignableFrom(BlockBehaviour.BlockBehaviourState.class)) { + if (BlockStatePathingCache.class.isAssignableFrom(BlockBehaviour.BlockStateBase.class)) { PATH_NOT_OPEN = new TrackedBlockStatePredicate(countingFlags.size()) { @Override public boolean test(BlockState operand) { - return PathNodeCache.getNeighborPathNodeType(operand) != PathNodeType.OPEN; + return PathNodeCache.getNeighborPathType(operand) != PathType.OPEN; } }; countingFlags.add(PATH_NOT_OPEN); diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/movement_tracker/SectionedEntityMovementTracker.java b/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/movement_tracker/SectionedEntityMovementTracker.java deleted file mode 100644 index 5a846a1..0000000 --- a/src/main/java/net/gensokyoreimagined/nitori/common/block/entity/movement_tracker/SectionedEntityMovementTracker.java +++ /dev/null @@ -1,4 +0,0 @@ -package net.gensokyoreimagined.nitori.common.block.entity.movement_tracker; - -public class SectionedEntityMovementTracker { -} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/EntityClassGroup.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/EntityClassGroup.java new file mode 100644 index 0000000..7814732 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/EntityClassGroup.java @@ -0,0 +1,111 @@ +package net.gensokyoreimagined.nitori.common.entity; + +import it.unimi.dsi.fastutil.objects.Reference2ByteOpenHashMap; +import net.gensokyoreimagined.nitori.common.reflection.ReflectionUtil; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.boss.enderdragon.EnderDragon; +import net.minecraft.world.entity.monster.Shulker; +import net.minecraft.world.entity.projectile.windcharge.WindCharge; +import net.minecraft.world.entity.vehicle.Minecart; + +import java.util.Objects; +import java.util.function.Predicate; +import java.util.logging.Logger; + +/** + * Class for grouping Entity classes by some property for use in TypeFilterableList + * It is intended that an EntityClassGroup acts as if it was immutable, however we cannot predict which subclasses of + * Entity might appear. Therefore we evaluate whether a class belongs to the class group when it is first seen. + * Once a class was evaluated the result of it is cached and cannot be changed. + * + * @author 2No2Name + */ +public class EntityClassGroup { + public static final EntityClassGroup CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE; //aka entities that will attempt to collide with all other entities when moving + + static { +// String remapped_collidesWith = FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_1297", "method_30949", "(Lnet/minecraft/class_1297;)Z"); + String remapped_collidesWith = "canCollideWith"; + CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE = new EntityClassGroup( + (Class entityClass) -> ReflectionUtil.hasMethodOverride(entityClass, Entity.class, true, remapped_collidesWith, Entity.class)); + + //sanity check: in case intermediary mappings changed, we fail + if ((!CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE.contains(Minecart.class))) { + throw new AssertionError(); + } + if ((!CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE.contains(WindCharge.class)) || (!CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE.contains(WindCharge.class))) { + throw new AssertionError(); + } + if ((CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE.contains(Shulker.class))) { + //should not throw an Error here, because another mod *could* add the method to ShulkerEntity. Warning when this sanity check fails. + Logger.getLogger("Lithium EntityClassGroup").warning("Either Lithium EntityClassGroup is broken or something else gave Shulkers the minecart-like collision behavior."); + } + CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE.clear(); + } + + private final Predicate> classFitEvaluator; + private volatile Reference2ByteOpenHashMap> class2GroupContains; + + public EntityClassGroup(Predicate> classFitEvaluator) { + this.class2GroupContains = new Reference2ByteOpenHashMap<>(); + Objects.requireNonNull(classFitEvaluator); + this.classFitEvaluator = classFitEvaluator; + } + + public void clear() { + this.class2GroupContains = new Reference2ByteOpenHashMap<>(); + } + + public boolean contains(Class entityClass) { + byte contains = this.class2GroupContains.getOrDefault(entityClass, (byte) 2); + if (contains != 2) { + return contains == 1; + } else { + return this.testAndAddClass(entityClass); + } + } + + boolean testAndAddClass(Class entityClass) { + byte contains; + //synchronizing here to avoid multiple threads replacing the map at the same time, and therefore possibly undoing progress + //it could also be fixed by using an AtomicReference's CAS, but we are writing very rarely (less than 150 times for the total game runtime in vanilla) + synchronized (this) { + //test the same condition again after synchronizing, as the collection might have been updated while this thread blocked + contains = this.class2GroupContains.getOrDefault(entityClass, (byte) 2); + if (contains != 2) { + return contains == 1; + } + //construct new map instead of updating the old map to avoid thread safety problems + //the map is not modified after publication + Reference2ByteOpenHashMap> newMap = this.class2GroupContains.clone(); + contains = this.classFitEvaluator.test(entityClass) ? (byte) 1 : (byte) 0; + newMap.put(entityClass, contains); + //publish the new map in a volatile field, so that all threads reading after this write can also see all changes to the map done before the write + this.class2GroupContains = newMap; + } + return contains == 1; + } + + public static class NoDragonClassGroup extends EntityClassGroup { + public static final NoDragonClassGroup BOAT_SHULKER_LIKE_COLLISION; //aka entities that other entities will do block-like collisions with when moving + + static { +// String remapped_isCollidable = FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_1297", "method_30948", "()Z"); + String remapped_isCollidable = "canBeCollidedWith"; + BOAT_SHULKER_LIKE_COLLISION = new NoDragonClassGroup( + (Class entityClass) -> ReflectionUtil.hasMethodOverride(entityClass, Entity.class, true, remapped_isCollidable)); + + if ((!BOAT_SHULKER_LIKE_COLLISION.contains(Shulker.class))) { + throw new AssertionError(); + } + BOAT_SHULKER_LIKE_COLLISION.clear(); + } + + public NoDragonClassGroup(Predicate> classFitEvaluator) { + super(classFitEvaluator); + if (classFitEvaluator.test(EnderDragon.class)) { + throw new IllegalArgumentException("EntityClassGroup.NoDragonClassGroup cannot be initialized: Must exclude EnderDragonEntity!"); + } + } + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/EntityMovementTrackerSection.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/EntityMovementTrackerSection.java new file mode 100644 index 0000000..0917f0c --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/EntityMovementTrackerSection.java @@ -0,0 +1,18 @@ +package net.gensokyoreimagined.nitori.common.entity.movement_tracker; + +import net.minecraft.world.level.entity.EntityAccess; +import net.minecraft.world.level.entity.EntitySectionStorage; + +public interface EntityMovementTrackerSection { + void lithium$addListener(SectionedEntityMovementTracker listener); + + void lithium$removeListener(EntitySectionStorage sectionedEntityCache, SectionedEntityMovementTracker listener); + + void lithium$trackEntityMovement(int notificationMask, long time); + + long lithium$getChangeTime(int trackedClass); + + void lithium$listenToMovementOnce(SectionedEntityMovementTracker listener, int trackedClass); + + void lithium$removeListenToMovementOnce(SectionedEntityMovementTracker listener, int trackedClass); +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/MovementTrackerHelper.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/MovementTrackerHelper.java new file mode 100644 index 0000000..287f7ff --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/MovementTrackerHelper.java @@ -0,0 +1,54 @@ +package net.gensokyoreimagined.nitori.common.entity.movement_tracker; + +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import net.gensokyoreimagined.nitori.api.inventory.LithiumInventory; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.level.block.entity.HopperBlockEntity; +import net.minecraft.world.level.entity.EntityAccess; + +import java.util.List; + +public abstract class MovementTrackerHelper { + public static final List> MOVEMENT_NOTIFYING_ENTITY_CLASSES; + public static volatile Reference2IntOpenHashMap> CLASS_2_NOTIFY_MASK; + public static final int NUM_MOVEMENT_NOTIFYING_CLASSES; + + static { + if (LithiumInventory.class.isAssignableFrom(HopperBlockEntity.class)) { + MOVEMENT_NOTIFYING_ENTITY_CLASSES = List.of(ItemEntity.class, Inventory.class); + } else { + MOVEMENT_NOTIFYING_ENTITY_CLASSES = List.of(); + } + + CLASS_2_NOTIFY_MASK = new Reference2IntOpenHashMap<>(); + CLASS_2_NOTIFY_MASK.defaultReturnValue(-1); + NUM_MOVEMENT_NOTIFYING_CLASSES = MOVEMENT_NOTIFYING_ENTITY_CLASSES.size(); + } + + public static int getNotificationMask(Class entityClass) { + int notificationMask = CLASS_2_NOTIFY_MASK.getInt(entityClass); + if (notificationMask == -1) { + notificationMask = calculateNotificationMask(entityClass); + } + return notificationMask; + } + private static int calculateNotificationMask(Class entityClass) { + int mask = 0; + for (int i = 0; i < MOVEMENT_NOTIFYING_ENTITY_CLASSES.size(); i++) { + Class superclass = MOVEMENT_NOTIFYING_ENTITY_CLASSES.get(i); + if (superclass.isAssignableFrom(entityClass)) { + mask |= 1 << i; + } + } + + //progress can be lost here, but it can only cost performance + //copy on write followed by publication in volatile field guarantees visibility of the final state + Reference2IntOpenHashMap> copy = CLASS_2_NOTIFY_MASK.clone(); + copy.put(entityClass, mask); + CLASS_2_NOTIFY_MASK = copy; + + return mask; + } + +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/SectionedEntityMovementListener.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/SectionedEntityMovementListener.java new file mode 100644 index 0000000..11e95da --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/SectionedEntityMovementListener.java @@ -0,0 +1,5 @@ +package net.gensokyoreimagined.nitori.common.entity.movement_tracker; + +public interface SectionedEntityMovementListener { + void lithium$handleEntityMovement(Class category); +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/SectionedEntityMovementTracker.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/SectionedEntityMovementTracker.java new file mode 100644 index 0000000..d163944 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/movement_tracker/SectionedEntityMovementTracker.java @@ -0,0 +1,197 @@ +package net.gensokyoreimagined.nitori.common.entity.movement_tracker; + +import it.unimi.dsi.fastutil.HashCommon; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import net.gensokyoreimagined.nitori.common.util.tuples.WorldSectionBox; +import net.gensokyoreimagined.nitori.common.world.LithiumData; +import net.gensokyoreimagined.nitori.mixin.util.accessors.ServerEntityManagerAccessor; +import net.gensokyoreimagined.nitori.mixin.util.accessors.ServerWorldAccessor; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.entity.EntityAccess; +import net.minecraft.world.level.entity.EntitySection; +import net.minecraft.world.level.entity.EntitySectionStorage; + +import java.util.ArrayList; + +public abstract class SectionedEntityMovementTracker { + final WorldSectionBox trackedWorldSections; + final Class clazz; + private final int trackedClass; + ArrayList> sortedSections; + boolean[] sectionVisible; + private int timesRegistered; + private final ArrayList sectionsNotListeningTo; + + private long maxChangeTime; + + private ReferenceOpenHashSet sectionedEntityMovementListeners; + + public SectionedEntityMovementTracker(WorldSectionBox interactionChunks, Class clazz) { + this.clazz = clazz; + this.trackedWorldSections = interactionChunks; + this.trackedClass = MovementTrackerHelper.MOVEMENT_NOTIFYING_ENTITY_CLASSES.indexOf(clazz); + assert this.trackedClass != -1; + this.sectionedEntityMovementListeners = null; + this.sectionsNotListeningTo = new ArrayList<>(); + } + + @Override + public int hashCode() { + return HashCommon.mix(this.trackedWorldSections.hashCode()) ^ HashCommon.mix(this.trackedClass) ^ this.getClass().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj.getClass() == this.getClass() && + this.clazz == ((SectionedEntityMovementTracker) obj).clazz && + this.trackedWorldSections.equals(((SectionedEntityMovementTracker) obj).trackedWorldSections); + } + + /** + * Method to quickly check whether any relevant entities moved inside the relevant entity sections after + * the last interaction attempt. + * + * @param lastCheckedTime time of the last interaction attempt + * @return whether any relevant entity moved in the tracked area + */ + public boolean isUnchangedSince(long lastCheckedTime) { + if (lastCheckedTime <= this.maxChangeTime) { + return false; + } + if (!this.sectionsNotListeningTo.isEmpty()) { + this.setChanged(this.listenToAllSectionsAndGetMaxChangeTime()); + return lastCheckedTime > this.maxChangeTime; + } + return true; + } + + private long listenToAllSectionsAndGetMaxChangeTime() { + long maxChangeTime = Long.MIN_VALUE; + ArrayList notListeningTo = this.sectionsNotListeningTo; + for (int i = notListeningTo.size() - 1; i >= 0; i--) { + EntityMovementTrackerSection entityMovementTrackerSection = notListeningTo.remove(i); + entityMovementTrackerSection.lithium$listenToMovementOnce(this, this.trackedClass); + maxChangeTime = Math.max(maxChangeTime, entityMovementTrackerSection.lithium$getChangeTime(this.trackedClass)); + } + return maxChangeTime; + } + + public void register(ServerLevel world) { + assert world == this.trackedWorldSections.world(); + + if (this.timesRegistered == 0) { + //noinspection unchecked + EntitySectionStorage cache = ((ServerEntityManagerAccessor) ((ServerWorldAccessor) world).getEntityManager()).getSectionStorage(); + + WorldSectionBox trackedSections = this.trackedWorldSections; + int size = trackedSections.numSections(); + assert size > 0; + this.sortedSections = new ArrayList<>(size); + this.sectionVisible = new boolean[size]; + + //vanilla iteration order in EntitySectionStorage is xzy + //WorldSectionBox upper coordinates are exclusive + for (int x = trackedSections.chunkX1(); x < trackedSections.chunkX2(); x++) { + for (int z = trackedSections.chunkZ1(); z < trackedSections.chunkZ2(); z++) { + for (int y = trackedSections.chunkY1(); y < trackedSections.chunkY2(); y++) { + EntitySection section = cache.getSection(SectionPos.asLong(x, y, z)); + EntityMovementTrackerSection sectionAccess = (EntityMovementTrackerSection) section; + this.sortedSections.add(section); + sectionAccess.lithium$addListener(this); + } + } + } + this.setChanged(world.getGameTime()); + } + + this.timesRegistered++; + } + + public void unRegister(Level world) { + assert world == this.trackedWorldSections.world(); + if (--this.timesRegistered > 0) { + return; + } + assert this.timesRegistered == 0; + //noinspection unchecked + EntitySectionStorage cache = ((ServerEntityManagerAccessor) ((ServerWorldAccessor) world).getEntityManager()).getSectionStorage(); + ((LithiumData) world).lithium$getData().entityMovementTrackers().deleteCanonical(this); + + ArrayList> sections = this.sortedSections; + for (int i = sections.size() - 1; i >= 0; i--) { + EntitySection section = sections.get(i); + EntityMovementTrackerSection sectionAccess = (EntityMovementTrackerSection) section; + sectionAccess.lithium$removeListener(cache, this); + if (!this.sectionsNotListeningTo.remove(section)) { + ((EntityMovementTrackerSection) section).lithium$removeListenToMovementOnce(this, this.trackedClass); + } + } + this.setChanged(world.getGameTime()); + } + + /** + * Register an entity section to this listener, so this listener can look for changes in the section. + */ + public void onSectionEnteredRange(EntityMovementTrackerSection section) { + this.setChanged(this.trackedWorldSections.world().getGameTime()); + //noinspection SuspiciousMethodCalls + int sectionIndex = this.sortedSections.lastIndexOf(section); + this.sectionVisible[sectionIndex] = true; + + this.sectionsNotListeningTo.add(section); + this.notifyAllListeners(); + } + + public void onSectionLeftRange(EntityMovementTrackerSection section) { + this.setChanged(this.trackedWorldSections.world().getGameTime()); + //noinspection SuspiciousMethodCalls + int sectionIndex = this.sortedSections.lastIndexOf(section); + + this.sectionVisible[sectionIndex] = false; + + if (!this.sectionsNotListeningTo.remove(section)) { + section.lithium$removeListenToMovementOnce(this, this.trackedClass); + this.notifyAllListeners(); + } + } + + /** + * Method that marks that new entities might have appeared or moved in the tracked chunk sections. + */ + private void setChanged(long atTime) { + if (atTime > this.maxChangeTime) { + this.maxChangeTime = atTime; + } + } + + public void listenToEntityMovementOnce(SectionedEntityMovementListener listener) { + if (this.sectionedEntityMovementListeners == null) { + this.sectionedEntityMovementListeners = new ReferenceOpenHashSet<>(); + } + this.sectionedEntityMovementListeners.add(listener); + + if (!this.sectionsNotListeningTo.isEmpty()) { + this.setChanged(this.listenToAllSectionsAndGetMaxChangeTime()); + } + + } + + public void emitEntityMovement(int classMask, EntityMovementTrackerSection section) { + if ((classMask & (1 << this.trackedClass)) != 0) { + this.notifyAllListeners(); + this.sectionsNotListeningTo.add(section); + } + } + + private void notifyAllListeners() { + ReferenceOpenHashSet listeners = this.sectionedEntityMovementListeners; + if (listeners != null && !listeners.isEmpty()) { + for (SectionedEntityMovementListener listener : listeners) { + listener.lithium$handleEntityMovement(this.clazz); + } + listeners.clear(); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/pushable/BlockCachingEntity.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/pushable/BlockCachingEntity.java new file mode 100644 index 0000000..0279b34 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/pushable/BlockCachingEntity.java @@ -0,0 +1,20 @@ +package net.gensokyoreimagined.nitori.common.entity.pushable; + +import net.minecraft.world.level.block.state.BlockState; + +public interface BlockCachingEntity { + + default void lithium$OnBlockCacheDeleted() { + + } + + default void lithium$OnBlockCacheSet(BlockState newState) { + + } + + default void lithium$SetClimbingMobCachingSectionUpdateBehavior(boolean listening) { + throw new UnsupportedOperationException(); + } + + BlockState lithium$getCachedFeetBlockState(); +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/entity/pushable/EntityPushablePredicate.java b/src/main/java/net/gensokyoreimagined/nitori/common/entity/pushable/EntityPushablePredicate.java new file mode 100644 index 0000000..c31b5b4 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/entity/pushable/EntityPushablePredicate.java @@ -0,0 +1,14 @@ +package net.gensokyoreimagined.nitori.common.entity.pushable; + +import java.util.function.Predicate; + +public abstract class EntityPushablePredicate implements Predicate { + public static Predicate and(Predicate first, Predicate second) { + return new EntityPushablePredicate() { + @Override + public boolean test(T t) { + return first.test(t) && second.test(t); + } + }; + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/reflection/ReflectionUtil.java b/src/main/java/net/gensokyoreimagined/nitori/common/reflection/ReflectionUtil.java index 673aa6b..028b362 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/reflection/ReflectionUtil.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/reflection/ReflectionUtil.java @@ -1,71 +1,71 @@ package net.gensokyoreimagined.nitori.common.reflection; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.block.AbstractBlock; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.entity.Entity; -import net.minecraft.util.crash.CrashException; -import net.minecraft.util.crash.CrashReport; -import net.minecraft.util.crash.CrashReportSection; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.entity.Entity; +import net.minecraft.ReportedException; +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.WeakHashMap; public class ReflectionUtil { -// -// public static boolean hasMethodOverride(Class clazz, Class superclass, boolean fallbackResult, String methodName, Class... methodArgs) { -// while (clazz != null && clazz != superclass && superclass.isAssignableFrom(clazz)) { -// try { -// clazz.getDeclaredMethod(methodName, methodArgs); -// return true; -// } catch (NoSuchMethodException e) { -// clazz = clazz.getSuperclass(); -// } catch (NoClassDefFoundError error) { -// Logger logger = LogManager.getLogger("Lithium Class Analysis"); -// logger.warn("Lithium Class Analysis Error: Class " + clazz.getName() + " cannot be analysed, because" + -// " getting declared methods crashes with NoClassDefFoundError: " + error.getMessage() + -// ". This is usually caused by modded" + -// " entities declaring methods that have a return type or parameter type that is annotated" + -// " with @Environment(value=EnvType.CLIENT). Loading the type is not possible, because" + -// " it only exists in the CLIENT environment. The recommended fix is to annotate the method with" + -// " this argument or return type with the same annotation." + -// " Lithium handles this error by assuming the class cannot be included in some optimizations."); -// return fallbackResult; -// } catch (Throwable e) { -// final String crashedClass = clazz.getName(); -// CrashReport crashReport = CrashReport.create(e, "Lithium Class Analysis"); -// CrashReportSection crashReportSection = crashReport.addElement(e.getClass().toString() + " when getting declared methods."); -// crashReportSection.add("Analyzed class", crashedClass); -// crashReportSection.add("Analyzed method name", methodName); -// crashReportSection.add("Analyzed method args", methodArgs); -// -// throw new CrashException(crashReport); -// } -// } -// return false; -// } -// -// //How to find the remapped methods: -// //1) Run in the debugger: System.out.println(FabricLoader.getInstance().getMappingResolver().getNamespaceData("intermediary").methodNames) -// //2) Ctrl+F for the method name, in this case "onEntityCollision". Make sure to find the correct one. -// private static final String REMAPPED_ON_ENTITY_COLLISION = FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_4970", "method_9548", "(Lnet/minecraft/class_2680;Lnet/minecraft/class_1937;Lnet/minecraft/class_2338;Lnet/minecraft/class_1297;)V"); -// private static final WeakHashMap, Boolean> CACHED_IS_ENTITY_TOUCHABLE = new WeakHashMap<>(); -// public static boolean isBlockStateEntityTouchable(BlockState operand) { -// Class blockClazz = operand.getBlock().getClass(); -// //Caching results in hashmap as this calculation takes over a second for all blocks together -// Boolean result = CACHED_IS_ENTITY_TOUCHABLE.get(blockClazz); -// if (result != null) { -// return result; -// } -// boolean res = ReflectionUtil.hasMethodOverride(blockClazz, AbstractBlock.class, true, REMAPPED_ON_ENTITY_COLLISION, BlockState.class, World.class, BlockPos.class, Entity.class); -// CACHED_IS_ENTITY_TOUCHABLE.put(blockClazz, res); -// return res; + + public static boolean hasMethodOverride(Class clazz, Class superclass, boolean fallbackResult, String methodName, Class... methodArgs) { + while (clazz != null && clazz != superclass && superclass.isAssignableFrom(clazz)) { + try { + clazz.getDeclaredMethod(methodName, methodArgs); + return true; + } catch (NoSuchMethodException e) { + clazz = clazz.getSuperclass(); + } catch (NoClassDefFoundError error) { + Logger logger = LogManager.getLogger("Lithium Class Analysis"); + logger.warn("Lithium Class Analysis Error: Class " + clazz.getName() + " cannot be analysed, because" + + " getting declared methods crashes with NoClassDefFoundError: " + error.getMessage() + + ". This is usually caused by modded" + + " entities declaring methods that have a return type or parameter type that is annotated" + + " with @Environment(value=EnvType.CLIENT). Loading the type is not possible, because" + + " it only exists in the CLIENT environment. The recommended fix is to annotate the method with" + + " this argument or return type with the same annotation." + + " Lithium handles this error by assuming the class cannot be included in some optimizations."); + return fallbackResult; + } catch (Throwable e) { + final String crashedClass = clazz.getName(); + CrashReport crashReport = CrashReport.forThrowable(e, "Lithium Class Analysis"); + CrashReportCategory crashReportSection = crashReport.addCategory(e.getClass().toString() + " when getting declared methods."); + crashReportSection.setDetail("Analyzed class", crashedClass); + crashReportSection.setDetail("Analyzed method name", methodName); + crashReportSection.setDetail("Analyzed method args", methodArgs); + + throw new ReportedException(crashReport); + } + } + return false; } -//} + + //How to find the remapped methods: + //1) Run in the debugger: System.out.println(FabricLoader.getInstance().getMappingResolver().getNamespaceData("intermediary").methodNames) + //2) Ctrl+F for the method name, in this case "onEntityCollision". Make sure to find the correct one. +// private static final String REMAPPED_ON_ENTITY_COLLISION = FabricLoader.getInstance().getMappingResolver().mapMethodName("intermediary", "net.minecraft.class_4970", "method_9548", "(Lnet/minecraft/class_2680;Lnet/minecraft/class_1937;Lnet/minecraft/class_2338;Lnet/minecraft/class_1297;)V"); + private static final String REMAPPED_ON_ENTITY_COLLISION = "entityInside"; + private static final WeakHashMap, Boolean> CACHED_IS_ENTITY_TOUCHABLE = new WeakHashMap<>(); + public static boolean isBlockStateEntityTouchable(BlockState operand) { + Class blockClazz = operand.getBlock().getClass(); + //Caching results in hashmap as this calculation takes over a second for all blocks together + Boolean result = CACHED_IS_ENTITY_TOUCHABLE.get(blockClazz); + if (result != null) { + return result; + } + boolean res = ReflectionUtil.hasMethodOverride(blockClazz, BlockBehaviour.class, true, REMAPPED_ON_ENTITY_COLLISION, BlockState.class, Level.class, BlockPos.class, Entity.class); + CACHED_IS_ENTITY_TOUCHABLE.put(blockClazz, res); + return res; + } +} //TODO: There's bunch of client sided options, we need to safely remove them if possible if not keep the behaviour since mixins work regardless of client or not \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/ClimbingMobCachingSection.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/ClimbingMobCachingSection.java new file mode 100644 index 0000000..1768b51 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/ClimbingMobCachingSection.java @@ -0,0 +1,17 @@ +package net.gensokyoreimagined.nitori.common.world; + +import net.gensokyoreimagined.nitori.common.entity.pushable.BlockCachingEntity; +import net.gensokyoreimagined.nitori.common.entity.pushable.EntityPushablePredicate; +import net.minecraft.util.AbortableIterationConsumer; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.AABB; + +import java.util.ArrayList; + +public interface ClimbingMobCachingSection { + AbortableIterationConsumer.Continuation lithium$collectPushableEntities(Level world, Entity except, AABB box, EntityPushablePredicate entityPushablePredicate, ArrayList entities); + + void lithium$onEntityModifiedCachedBlock(BlockCachingEntity entity, BlockState newBlockState); +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/LithiumData.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/LithiumData.java index 5181ce3..b0e0fd2 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/world/LithiumData.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/LithiumData.java @@ -5,7 +5,7 @@ import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import net.gensokyoreimagined.nitori.common.entity.block_tracking.ChunkSectionChangeCallback; import net.gensokyoreimagined.nitori.common.entity.block_tracking.SectionedBlockChangeTracker; -import net.gensokyoreimagined.nitori.common.block.entity.movement_tracker.SectionedEntityMovementTracker; +import net.gensokyoreimagined.nitori.common.entity.movement_tracker.SectionedEntityMovementTracker; import net.gensokyoreimagined.nitori.common.util.deduplication.LithiumInterner; import net.minecraft.world.entity.ai.navigation.PathNavigation; import net.minecraft.world.item.ItemStack; @@ -42,7 +42,7 @@ public interface LithiumData { public Data(Level world) { this( new Long2ReferenceOpenHashMap<>(), - world.registryAccess().getOptionalWrapper(Registries.BANNER_PATTERN).map(Raid::getOminousBanner).orElse(null), + world.registryAccess().lookup(Registries.BANNER_PATTERN).map(Raid::getLeaderBannerInstance).orElse(null), new ReferenceOpenHashSet<>(), new LithiumInterner<>(), new LithiumInterner<>(), diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/WorldHelper.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/WorldHelper.java index 4ccbf9d..8cf71f5 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/world/WorldHelper.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/WorldHelper.java @@ -1,18 +1,17 @@ package net.gensokyoreimagined.nitori.common.world; -import net.gensokyoreimagined.nitori.common.client.ClientWorldAccessor; import net.gensokyoreimagined.nitori.common.entity.EntityClassGroup; import net.gensokyoreimagined.nitori.common.entity.pushable.EntityPushablePredicate; +import net.gensokyoreimagined.nitori.mixin.util.accessors.ClientEntityManagerAccessor; +import net.gensokyoreimagined.nitori.mixin.util.accessors.EntityTrackingSectionAccessor; +import net.gensokyoreimagined.nitori.mixin.util.accessors.ServerEntityManagerAccessor; import net.gensokyoreimagined.nitori.common.world.chunk.ClassGroupFilterableList; -import net.gensokyoreimagined.nitori.common.util.accessors.ClientEntityManagerAccessor; -import net.gensokyoreimagined.nitori.common.util.accessors.EntityTrackingSectionAccessor; -import net.gensokyoreimagined.nitori.common.util.accessors.ServerEntityManagerAccessor; -import net.gensokyoreimagined.nitori.common.util.accessors.ServerWorldAccessor; +import net.gensokyoreimagined.nitori.mixin.util.accessors.ServerWorldAccessor; import net.minecraft.core.BlockPos; import net.minecraft.util.ClassInstanceMultiMap; import net.minecraft.util.AbortableIterationConsumer; import net.minecraft.world.phys.AABB; -import net.minecraft.world.level.EntityGetter +import net.minecraft.world.level.EntityGetter; import net.minecraft.world.level.Level; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.entity.EntitySectionStorage; @@ -29,7 +28,7 @@ public class WorldHelper { * Partial [VanillaCopy] * The returned entity iterator is only used for collision interactions. As most entities do not collide with other * entities (cramming is different), getting them is not necessary. This is why we only get entities when they override - * {@link Entity#isCollidable()} if the reference entity does not override {@link Entity#collidesWith(Entity)}. + * {@link Entity#canBeCollidedWith()} if the reference entity does not override {@link Entity#canCollideWith(Entity)}. * Note that the returned iterator contains entities that override these methods. This does not mean that these methods * always return true. * @@ -42,13 +41,13 @@ public class WorldHelper { if (!CUSTOM_TYPE_FILTERABLE_LIST_DISABLED && entityView instanceof Level world && (collidingEntity == null || !EntityClassGroup.CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE.contains(collidingEntity.getClass()))) { EntitySectionStorage cache = getEntityCacheOrNull(world); if (cache != null) { - world.getProfiler().visit("getEntities"); + world.getProfiler().incrementCounter("getEntities"); return getEntitiesOfClassGroup(cache, collidingEntity, EntityClassGroup.NoDragonClassGroup.BOAT_SHULKER_LIKE_COLLISION, box); } } //use vanilla code in case the shortcut is not applicable // due to the reference entity implementing special collision or the mixin being disabled in the config - return EntityGetter.getOtherEntities(collidingEntity, box); + return entityView.getEntities(collidingEntity, box); } public static List getOtherEntitiesForCollision(EntityGetter entityView, AABB box, @Nullable Entity collidingEntity, Predicate entityPredicate) { @@ -56,34 +55,32 @@ public class WorldHelper { if (collidingEntity == null || !EntityClassGroup.CUSTOM_COLLIDE_LIKE_MINECART_BOAT_WINDCHARGE.contains(collidingEntity.getClass())) { EntitySectionStorage cache = getEntityCacheOrNull(world); if (cache != null) { - world.getProfiler().visit("getEntities"); + world.getProfiler().incrementCounter("getEntities"); return getEntitiesOfClassGroup(cache, collidingEntity, EntityClassGroup.NoDragonClassGroup.BOAT_SHULKER_LIKE_COLLISION, box); } } } //use vanilla code in case the shortcut is not applicable // due to the reference entity implementing special collision or the mixin being disabled in the config - return entityView.getOtherEntities(collidingEntity, box, entityPredicate); + return entityView.getEntities(collidingEntity, box, entityPredicate); } //Requires util.accessors public static EntitySectionStorage getEntityCacheOrNull(Level world) { - if (world instanceof ClientWorldAccessor) { - //noinspection unchecked - return ((ClientEntityManagerAccessor) ((ClientWorldAccessor) world).lithium$getEntityManager()).getCache(); - } else if (world instanceof ServerWorldAccessor) { - //noinspection unchecked - return ((ServerEntityManagerAccessor) ((ServerWorldAccessor) world).getEntityManager()).getCache(); - } + // Does not work on Paper... +// if (world instanceof ServerWorldAccessor) { +// //noinspection unchecked +// return ((ServerEntityManagerAccessor) ((ServerWorldAccessor) world).getEntityManager()).getSectionStorage(); +// } return null; } public static List getEntitiesOfClassGroup(EntitySectionStorage cache, Entity collidingEntity, EntityClassGroup.NoDragonClassGroup entityClassGroup, AABB box) { ArrayList entities = new ArrayList<>(); - cache.forEachInBox(box, section -> { + cache.forEachAccessibleNonEmptySection(box, section -> { //noinspection unchecked - ClassInstanceMultiMap allEntities = ((EntityTrackingSectionAccessor) section).getCollection(); + ClassInstanceMultiMap allEntities = ((EntityTrackingSectionAccessor) section).getStorage(); //noinspection unchecked Collection entitiesOfType = ((ClassGroupFilterableList) allEntities).lithium$getAllOfGroupType(entityClassGroup); if (!entitiesOfType.isEmpty()) { @@ -101,7 +98,7 @@ public class WorldHelper { public static List getPushableEntities(Level world, EntitySectionStorage cache, Entity except, AABB box, EntityPushablePredicate entityPushablePredicate) { ArrayList entities = new ArrayList<>(); - cache.forEachInBox(box, section -> ((ClimbingMobCachingSection) section).lithium$collectPushableEntities(world, except, box, entityPushablePredicate, entities)); + cache.forEachAccessibleNonEmptySection(box, section -> ((ClimbingMobCachingSection) section).lithium$collectPushableEntities(world, except, box, entityPushablePredicate, entities)); return entities; } diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/ClassGroupFilterableList.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/ClassGroupFilterableList.java new file mode 100644 index 0000000..260c676 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/ClassGroupFilterableList.java @@ -0,0 +1,8 @@ +package net.gensokyoreimagined.nitori.common.world.chunk; + +import net.gensokyoreimagined.nitori.common.entity.EntityClassGroup; +import java.util.Collection; + +public interface ClassGroupFilterableList { + Collection lithium$getAllOfGroupType(EntityClassGroup type); +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/ai/pathing/PathContextAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/ai/pathing/PathContextAccessor.java index 3dde0a3..92d5bce 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/ai/pathing/PathContextAccessor.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/ai/pathing/PathContextAccessor.java @@ -1,14 +1,12 @@ package net.gensokyoreimagined.nitori.mixin.ai.pathing; -import net.minecraft.world.level.pathfinder.Path; import net.minecraft.core.BlockPos; +import net.minecraft.world.level.pathfinder.PathfindingContext; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(Path.class) +@Mixin(PathfindingContext.class) public interface PathContextAccessor { - @Accessor - BlockPos.MutableBlockPos getLastNodePos(); - + BlockPos.MutableBlockPos getMutablePos(); } \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/util/accessors/ClientEntityManagerAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ClientEntityManagerAccessor.java similarity index 78% rename from src/main/java/net/gensokyoreimagined/nitori/common/util/accessors/ClientEntityManagerAccessor.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ClientEntityManagerAccessor.java index c3ce794..cec3829 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/util/accessors/ClientEntityManagerAccessor.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ClientEntityManagerAccessor.java @@ -1,13 +1,13 @@ -package net.gensokyoreimagined.nitori.common.util.accessors; +package net.gensokyoreimagined.nitori.mixin.util.accessors; -import net.minecraft.world.level.entity.TransientEntitySectionManager; import net.minecraft.world.level.entity.EntityAccess; import net.minecraft.world.level.entity.EntitySectionStorage; +import net.minecraft.world.level.entity.TransientEntitySectionManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(TransientEntitySectionManager.class) public interface ClientEntityManagerAccessor { @Accessor - EntitySectionStorage getCache(); + EntitySectionStorage getSectionStorage(); } \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/EntityTrackingSectionAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/EntityTrackingSectionAccessor.java new file mode 100644 index 0000000..3fcf2dc --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/EntityTrackingSectionAccessor.java @@ -0,0 +1,12 @@ +package net.gensokyoreimagined.nitori.mixin.util.accessors; + +import net.minecraft.util.ClassInstanceMultiMap; +import net.minecraft.world.level.entity.EntitySection; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(EntitySection.class) +public interface EntityTrackingSectionAccessor { + @Accessor + ClassInstanceMultiMap getStorage(); +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ServerEntityManagerAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ServerEntityManagerAccessor.java new file mode 100644 index 0000000..2c05b11 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ServerEntityManagerAccessor.java @@ -0,0 +1,13 @@ +package net.gensokyoreimagined.nitori.mixin.util.accessors; + +import net.minecraft.world.level.entity.EntityAccess; +import net.minecraft.world.level.entity.EntitySectionStorage; +import net.minecraft.world.level.entity.PersistentEntitySectionManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(PersistentEntitySectionManager.class) +public interface ServerEntityManagerAccessor { + @Accessor + EntitySectionStorage getSectionStorage(); +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ServerWorldAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ServerWorldAccessor.java new file mode 100644 index 0000000..e36e3d5 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/accessors/ServerWorldAccessor.java @@ -0,0 +1,14 @@ +package net.gensokyoreimagined.nitori.mixin.util.accessors; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.entity.PersistentEntitySectionManager; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.Mixin; + +// Does not work on Paper... +@Mixin(ServerLevel.class) +public interface ServerWorldAccessor { + @Accessor + PersistentEntitySectionManager getEntityManager(); +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/AbstractBlockStateMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/AbstractBlockStateMixin.java new file mode 100644 index 0000000..7d0ea64 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/AbstractBlockStateMixin.java @@ -0,0 +1,44 @@ +package net.gensokyoreimagined.nitori.mixin.util.block_tracking; + +import net.gensokyoreimagined.nitori.common.block.BlockStateFlagHolder; +import net.gensokyoreimagined.nitori.common.block.BlockStateFlags; +import net.gensokyoreimagined.nitori.common.block.TrackedBlockStatePredicate; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.Inject; + +@Mixin(BlockBehaviour.BlockStateBase.class) +public class AbstractBlockStateMixin implements BlockStateFlagHolder { + @Unique + private int flags; + + @Inject(method = "initCache", at = @At("RETURN")) + private void init(CallbackInfo ci) { + this.initFlags(); + } + + @Unique + private void initFlags() { + TrackedBlockStatePredicate.FULLY_INITIALIZED.set(true); + + int flags = 0; + + for (int i = 0; i < BlockStateFlags.FLAGS.length; i++) { + //noinspection ConstantConditions + if (BlockStateFlags.FLAGS[i].test((BlockState) (Object) this)) { + flags |= 1 << i; + } + } + + this.flags = flags; + } + + @Override + public int lithium$getAllFlags() { + return this.flags; + } +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java index 419f28f..22d90ef 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java @@ -9,13 +9,10 @@ import net.minecraft.core.SectionPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.PalettedContainer; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; +import net.minecraft.world.level.material.FluidState; +import org.spongepowered.asm.mixin.*; 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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.LocalCapture; @@ -31,7 +28,7 @@ public abstract class ChunkSectionMixin implements BlockCountingSection, BlockLi @Shadow @Final - private PalettedContainer blockStateContainer; + public PalettedContainer states; @Unique private short[] countsByFlag = null; @@ -63,7 +60,7 @@ public abstract class ChunkSectionMixin implements BlockCountingSection, BlockLi private void fastInitClientCounts() { this.countsByFlag = new short[BlockStateFlags.NUM_TRACKED_FLAGS]; for (TrackedBlockStatePredicate trackedBlockStatePredicate : BlockStateFlags.TRACKED_FLAGS) { - if (this.blockStateContainer.hasAny(trackedBlockStatePredicate)) { + if (this.states.maybeHas(trackedBlockStatePredicate)) { //We haven't counted, so we just set the count so high that it never incorrectly reaches 0. //For most situations, this overestimation does not hurt client performance compared to correct counting, this.countsByFlag[trackedBlockStatePredicate.getIndex()] = 16 * 16 * 16; @@ -71,23 +68,43 @@ public abstract class ChunkSectionMixin implements BlockCountingSection, BlockLi } } - @Redirect( - method = "calculateCounts()V", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/chunk/PalettedContainer;count(Lnet/minecraft/world/chunk/PalettedContainer$Counter;)V" - ) - ) - private void initFlagCounters(PalettedContainer palettedContainer, PalettedContainer.Counter consumer) { - palettedContainer.count((state, count) -> { - consumer.accept(state, count); +// @Redirect( +// method = "recalcBlockCounts()V", +// at = @At( +// value = "INVOKE", +// target = "Lnet/minecraft/world/chunk/PalettedContainer;count(Lnet/minecraft/world/chunk/PalettedContainer$Counter;)V" +// ) +// ) +// private void initFlagCounters(PalettedContainer palettedContainer, PalettedContainer.Counter consumer) { +// palettedContainer.count((state, count) -> { +// consumer.accept(state, count); +// addToFlagCount(this.countsByFlag, state, (short) count); +// }); +// } + @Shadow + short nonEmptyBlockCount; + @Shadow + private short tickingBlockCount; + @Shadow + private short tickingFluidCount; + + /** + * @author DoggySazHi + * @reason Replace the Paper implementation with Mojang + Lithium implementation + */ + @Overwrite + public void recalcBlockCounts() { + this.countsByFlag = new short[BlockStateFlags.NUM_TRACKED_FLAGS]; + + BlockStateCounter lv = new BlockStateCounter(); + this.states.count((state, count) -> { + lv.accept(state, count); addToFlagCount(this.countsByFlag, state, (short) count); }); - } - @Inject(method = "calculateCounts()V", at = @At("HEAD")) - private void createFlagCounters(CallbackInfo ci) { - this.countsByFlag = new short[BlockStateFlags.NUM_TRACKED_FLAGS]; + this.nonEmptyBlockCount = (short)lv.nonEmptyBlockCount; + this.tickingBlockCount = (short)lv.randomTickableBlockCount; + this.tickingFluidCount = (short)lv.nonEmptyFluidCount; } @Inject( @@ -99,10 +116,10 @@ public abstract class ChunkSectionMixin implements BlockCountingSection, BlockLi } @Inject( - method = "setBlockState(IIILnet/minecraft/block/BlockState;Z)Lnet/minecraft/block/BlockState;", + method = "setBlockState(IIILnet/minecraft/world/level/block/state/BlockState;Z)Lnet/minecraft/world/level/block/state/BlockState;", at = @At( value = "INVOKE", - target = "Lnet/minecraft/block/BlockState;getFluidState()Lnet/minecraft/fluid/FluidState;", + target = "Lnet/minecraft/world/level/block/state/BlockState;getFluidState()Lnet/minecraft/world/level/material/FluidState;", ordinal = 0, shift = At.Shift.BEFORE ), diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 01cf8b2..b4d2e04 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -51,6 +51,11 @@ "world.block_entity_ticking.sleeping.campfire.CampfireBlockEntityMixin", "world.block_entity_ticking.sleeping.campfire.unlit.CampfireBlockEntityMixin", "world.block_entity_ticking.sleeping.campfire.lit.CampfireBlockEntityMixin", - "world.block_entity_ticking.sleeping.furnace.AbstractFurnaceBlockEntityMixin" + "world.block_entity_ticking.sleeping.furnace.AbstractFurnaceBlockEntityMixin", + "util.accessors.ClientEntityManagerAccessor", + "util.accessors.EntityTrackingSectionAccessor", + "util.accessors.ServerEntityManagerAccessor", + "util.block_tracking.AbstractBlockStateMixin", + "util.block_tracking.ChunkSectionMixin" ] } From a824c25224186d4694162c3e90b94e9f1e7ac096 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Mon, 8 Jul 2024 20:24:31 +0300 Subject: [PATCH 05/21] disable problematic mixin --- src/main/resources/mixins.core.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index b4d2e04..d50e732 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -56,6 +56,5 @@ "util.accessors.EntityTrackingSectionAccessor", "util.accessors.ServerEntityManagerAccessor", "util.block_tracking.AbstractBlockStateMixin", - "util.block_tracking.ChunkSectionMixin" ] } From ed6e029363f2e6b1a5042601d28bed62469d0285 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Mon, 8 Jul 2024 21:47:46 +0300 Subject: [PATCH 06/21] SortedArraySetMixin --- .../chunk_tickets/SortedArraySetMixin.java | 52 +++++++++++++++++++ src/main/resources/mixins.core.json | 3 +- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/collections/chunk_tickets/SortedArraySetMixin.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/chunk_tickets/SortedArraySetMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/chunk_tickets/SortedArraySetMixin.java new file mode 100644 index 0000000..9844238 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/chunk_tickets/SortedArraySetMixin.java @@ -0,0 +1,52 @@ +package net.gensokyoreimagined.nitori.mixin.collections.chunk_tickets; + +import net.minecraft.util.SortedArraySet; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Collection; +import java.util.function.Predicate; + +@Mixin(SortedArraySet.class) +public abstract class SortedArraySetMixin implements Collection { + @Shadow + int size; + + @Shadow + T[] contents ; + + /** + * Add an optimized implementation of {@link Collection#removeIf(Predicate)} which doesn't attempt to shift + * the values in the array multiple times with each removal. This also eliminates a number of object allocations + * and works on the direct backing array, speeding things up a fair chunk. + */ + @Override + public boolean removeIf(Predicate filter) { + T[] arr = this.contents ; + + int writeLim = this.size; + int writeIdx = 0; + + for (int readIdx = 0; readIdx < writeLim; readIdx++) { + T obj = arr[readIdx]; + + // If the filter does not pass the object, simply skip over it. The write pointer will + // not be advanced and the next element to pass will instead take this one's place. + if (filter.test(obj)) { + continue; + } + + // If the read and write pointers are the same, then no removals have occurred so far. This + // allows us to skip copying unchanged values back into the array. + if (writeIdx != readIdx) { + arr[writeIdx] = obj; + } + + writeIdx++; + } + + this.size = writeIdx; + + return writeLim != writeIdx; + } +} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index d50e732..05960a9 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -46,6 +46,7 @@ "math.fast_util.DirectionMixin", "collections.entity_filtering.TypeFilterableListMixin", "collections.entity_by_type.TypeFilterableListMixin", + "collections.chunk_tickets.SortedArraySetMixin", "collections.block_entity_tickers.WorldChunkMixin", "world.block_entity_ticking.sleeping.WrappedBlockEntityTickInvokerAccessor", "world.block_entity_ticking.sleeping.campfire.CampfireBlockEntityMixin", @@ -55,6 +56,6 @@ "util.accessors.ClientEntityManagerAccessor", "util.accessors.EntityTrackingSectionAccessor", "util.accessors.ServerEntityManagerAccessor", - "util.block_tracking.AbstractBlockStateMixin", + "util.block_tracking.AbstractBlockStateMixin" ] } From 123ffb09883550e8691d6a66ea61c6ed2ccf8b40 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Tue, 9 Jul 2024 00:05:25 +0300 Subject: [PATCH 07/21] collections.GoalSelectorMixin --- .../collections/goals/GoalSelectorMixin.java | 33 +++++++++++++++++++ src/main/resources/mixins.core.json | 1 + 2 files changed, 34 insertions(+) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/collections/goals/GoalSelectorMixin.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/goals/GoalSelectorMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/goals/GoalSelectorMixin.java new file mode 100644 index 0000000..b5a5ed7 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/goals/GoalSelectorMixin.java @@ -0,0 +1,33 @@ +package net.gensokyoreimagined.nitori.mixin.collections.goals; + +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; +import net.minecraft.world.entity.ai.goal.GoalSelector; +import net.minecraft.world.entity.ai.goal.WrappedGoal; +import net.minecraft.util.profiling.ProfilerFiller; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Set; +import java.util.function.Supplier; + +@Mixin(GoalSelector.class) +public abstract class GoalSelectorMixin { + + @Mutable + @Shadow + @Final + private Set availableGoals; + + /** + * Replace the goal set with an optimized collection type which performs better for iteration. + */ + @Inject(method = "", at = @At("RETURN")) + private void reinit(Supplier supplier, CallbackInfo ci) { + this.availableGoals = new ObjectLinkedOpenHashSet<>(this.availableGoals); + } +} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 05960a9..49db3df 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -48,6 +48,7 @@ "collections.entity_by_type.TypeFilterableListMixin", "collections.chunk_tickets.SortedArraySetMixin", "collections.block_entity_tickers.WorldChunkMixin", + "collections.goals.GoalSelectorMixin", "world.block_entity_ticking.sleeping.WrappedBlockEntityTickInvokerAccessor", "world.block_entity_ticking.sleeping.campfire.CampfireBlockEntityMixin", "world.block_entity_ticking.sleeping.campfire.unlit.CampfireBlockEntityMixin", From e9fd0a0363bafd0221e04f8d3cc5cc3fe53a433d Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Tue, 9 Jul 2024 00:09:04 +0300 Subject: [PATCH 08/21] vec.FastMathVec3DMixin --- .../mixin/math/vec/FastMathVec3DMixin.java | 37 +++++++++++++++++++ src/main/resources/mixins.core.json | 1 + 2 files changed, 38 insertions(+) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/math/vec/FastMathVec3DMixin.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/math/vec/FastMathVec3DMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/vec/FastMathVec3DMixin.java new file mode 100644 index 0000000..2d93ff7 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/vec/FastMathVec3DMixin.java @@ -0,0 +1,37 @@ +package net.gensokyoreimagined.nitori.mixin.math.vec; + +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.*; + +@Mixin(Vec3.class) +public class FastMathVec3DMixin { + @Mutable + @Shadow + @Final + public double x; + + @Mutable + @Shadow @Final public double y; + + @Mutable + @Shadow @Final public double z; + private Vec3 cachedNormalized; + + /** + * @author QPCrummer + * @reason Cache normalized Vec + */ + @Overwrite + public Vec3 normalize() { + if (cachedNormalized == null) { + double squaredLength = x * x + y * y + z * z; + if (squaredLength < 1.0E-8) { + cachedNormalized = Vec3.ZERO; + } else { + double invLength = 1.0 / Math.sqrt(squaredLength); + cachedNormalized = new Vec3(x * invLength, y * invLength, z * invLength); + } + } + return cachedNormalized; + } +} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 49db3df..a5f0c81 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -44,6 +44,7 @@ "math.fast_util.AxisCycleDirectionMixin$ForwardMixin", "math.fast_util.AxisCycleDirectionMixin$BackwardMixin", "math.fast_util.DirectionMixin", + "math.vec.FastMathVec3DMixin", "collections.entity_filtering.TypeFilterableListMixin", "collections.entity_by_type.TypeFilterableListMixin", "collections.chunk_tickets.SortedArraySetMixin", From f155fec797de07faeab2992b9d77a347aa35d8b6 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Tue, 9 Jul 2024 00:45:05 +0300 Subject: [PATCH 09/21] math.MathHelperIntrinsicMixin, math.GenericFastMath --- .../math/general/GenericFastMathMixin.java | 69 +++++++++++++++++++ .../intrinsic/MathHelperIntrinsicMixin.java | 54 +++++++++++++++ src/main/resources/mixins.core.json | 2 + 3 files changed, 125 insertions(+) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/math/general/GenericFastMathMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/math/intrinsic/MathHelperIntrinsicMixin.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/math/general/GenericFastMathMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/general/GenericFastMathMixin.java new file mode 100644 index 0000000..876f020 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/general/GenericFastMathMixin.java @@ -0,0 +1,69 @@ +package net.gensokyoreimagined.nitori.mixin.math.general; + +import net.minecraft.world.phys.AABB; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.stream.IntStream; + +@Mixin(Mth.class) +public class GenericFastMathMixin { + + /** + * @author QPCrummer + * @reason Slightly more optimized + */ + @Overwrite + public static boolean rayIntersectsAABB(Vec3 origin, Vec3 direction, AABB box) { + + double d = (box.minX + box.maxX) * 0.5; + double e = (box.maxX - box.minX) * 0.5; + double f = origin.x - d; + if (Math.abs(f) > e && f * direction.x > 0.0) { + return false; + } + + double g = (box.minY + box.maxY) * 0.5; + double h = (box.maxY - box.minY) * 0.5; + double i = origin.y - g; + if (Math.abs(i) > h && i * direction.y >= 0.0) { + return false; + } + + double j = (box.minZ + box.maxZ) * 0.5; + double k = (box.maxZ - box.minZ) * 0.5; + double l = origin.z - j; + if (Math.abs(l) > k && l * direction.z >= 0.0) { + return false; + } + + double m = Math.abs(direction.x); + double n = Math.abs(direction.y); + double o = Math.abs(direction.z); + double p = direction.y * l - direction.z * i; + if (Math.abs(p) > h * o + k * n || Math.abs(direction.z * f - direction.x * l) > e * o + k * m) { + return false; + } + + return Math.abs(direction.x * i - direction.y * f) < e * n + h * m; + } + + /** + * @author QPCrummer + * @reason Slightly more optimized + */ + @Overwrite + public static IntStream outFromOrigin(int seed, int lowerBound, int upperBound, int steps) { + + if (steps < 1 || seed < lowerBound || seed > upperBound) { + return IntStream.empty(); + } + + return IntStream.iterate(seed, i -> { + int nextValue = i + (i <= seed ? steps : -steps); + return nextValue >= lowerBound && nextValue <= upperBound ? nextValue : i; + }); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/math/intrinsic/MathHelperIntrinsicMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/intrinsic/MathHelperIntrinsicMixin.java new file mode 100644 index 0000000..eaff0f7 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/intrinsic/MathHelperIntrinsicMixin.java @@ -0,0 +1,54 @@ +package net.gensokyoreimagined.nitori.mixin.math.intrinsic; + +import net.minecraft.util.Mth; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +// Credit to Gale patch #0018 +@Mixin(Mth.class) +public class MathHelperIntrinsicMixin { + /** + * @author QPCrummer + * @reason Use Intrinsic instead + */ + @Overwrite + public static int floor(float value) { + return (int) Math.floor(value); + } + + /** + * @author QPCrummer + * @reason Use Intrinsic instead + */ + @Overwrite + public static int floor(double value) { + return (int) Math.floor(value); + } + + /** + * @author QPCrummer + * @reason Use Intrinsic instead + */ + @Overwrite + public static int ceil(float value) { + return (int) Math.ceil(value); + } + + /** + * @author QPCrummer + * @reason Use Intrinsic instead + */ + @Overwrite + public static int ceil(double value) { + return (int) Math.ceil(value); + } + + /** + * @author QPCrummer + * @reason Use Intrinsic instead + */ + @Overwrite + public static double absMax(double a, double b) { + return Math.max(Math.abs(a), Math.abs(b)); + } +} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index a5f0c81..da85d52 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -45,6 +45,8 @@ "math.fast_util.AxisCycleDirectionMixin$BackwardMixin", "math.fast_util.DirectionMixin", "math.vec.FastMathVec3DMixin", + "math.general.GenericFastMathMixin", + "math.intrinsic.MathHelperIntrinsicMixin", "collections.entity_filtering.TypeFilterableListMixin", "collections.entity_by_type.TypeFilterableListMixin", "collections.chunk_tickets.SortedArraySetMixin", From b5f1450bb0601fad18eef4759366a023fa1829df Mon Sep 17 00:00:00 2001 From: DoggySazHi Date: Mon, 8 Jul 2024 15:37:40 -0700 Subject: [PATCH 10/21] MixinEntityTickList --- .../nitori/mixin/MixinEntityTickList.java | 70 +++++++++++++++++++ src/main/resources/mixins.core.json | 3 +- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/MixinEntityTickList.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinEntityTickList.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinEntityTickList.java new file mode 100644 index 0000000..805a458 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinEntityTickList.java @@ -0,0 +1,70 @@ +package net.gensokyoreimagined.nitori.mixin; + +import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.entity.EntityTickList; +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.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.function.Consumer; + +@Mixin(EntityTickList.class) +public class MixinEntityTickList { + @Unique + private Int2ObjectLinkedOpenHashMap nitori$entities = new Int2ObjectLinkedOpenHashMap<>(); + @Unique + private Int2ObjectLinkedOpenHashMap nitori$iteratorPointer = null; + + // I've decided I hate Paper's design. + // All of these are effectively Overwrites. + + @Unique + private void nitori$ensureActiveIsNotIterated() { + if (nitori$iteratorPointer == nitori$entities) { + // Avoid a ConcurrentModificationException by cloning the map before modifying it. + // Side effect is that it allocates more memory to avoid blocking the main thread, but it's all pointers anyway. + nitori$entities = nitori$entities.clone(); + } + } + + @Inject(method = "add", at = @At("HEAD"), cancellable = true) + public void add(Entity entity, CallbackInfo ci) { + nitori$ensureActiveIsNotIterated(); + nitori$entities.put(entity.getId(), entity); + ci.cancel(); + } + + @Inject(method = "remove", at = @At("HEAD"), cancellable = true) + public void remove(Entity entity, CallbackInfo ci) { + nitori$ensureActiveIsNotIterated(); + nitori$entities.remove(entity.getId()); + ci.cancel(); + } + + @Inject(method = "contains", at = @At("HEAD"), cancellable = true) + public void contains(Entity entity, CallbackInfoReturnable ci) { + ci.setReturnValue(nitori$entities.containsKey(entity.getId())); + ci.cancel(); + } + + @Inject(method = "forEach", at = @At("HEAD"), cancellable = true) + public void forEach(Consumer action, CallbackInfo ci) { + if (nitori$iteratorPointer == nitori$entities) { + nitori$entities = nitori$entities.clone(); // Avoid a ConcurrentModificationException by cloning the map before iterating over it. + } + + nitori$iteratorPointer = nitori$entities; // Mark the map as being iterated. + + try { + nitori$iteratorPointer.values().forEach(action); // Iterate over the map. + } finally { + nitori$iteratorPointer = null; // Mark the map as no longer being iterated. + } + + ci.cancel(); + } +} diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index a5f0c81..6d6ded0 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -58,6 +58,7 @@ "util.accessors.ClientEntityManagerAccessor", "util.accessors.EntityTrackingSectionAccessor", "util.accessors.ServerEntityManagerAccessor", - "util.block_tracking.AbstractBlockStateMixin" + "util.block_tracking.AbstractBlockStateMixin", + "MixinEntityTickList" ] } From 4aa707b8b5be22fc1b1f0a2c7edadbe60fa8f4e0 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Tue, 9 Jul 2024 04:51:11 +0300 Subject: [PATCH 11/21] we are at the end game --- .../chunkwatching/PlayerClientVDTracking.java | 9 ++++ .../fast_hand_swing/LivingEntityMixin.java | 28 +++++++++++ .../playerwatching/MixinChunkFilter.java | 22 +++++++++ .../MixinServerPlayerEntity.java | 46 +++++++++++++++++++ .../MixinMobEntity.java | 35 ++++++++++++++ src/main/resources/mixins.core.json | 5 +- 6 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/chunkwatching/PlayerClientVDTracking.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fast_hand_swing/LivingEntityMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinChunkFilter.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinServerPlayerEntity.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/chunkwatching/PlayerClientVDTracking.java b/src/main/java/net/gensokyoreimagined/nitori/common/chunkwatching/PlayerClientVDTracking.java new file mode 100644 index 0000000..251fba9 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/chunkwatching/PlayerClientVDTracking.java @@ -0,0 +1,9 @@ +package net.gensokyoreimagined.nitori.common.chunkwatching; + +public interface PlayerClientVDTracking { + + boolean isClientViewDistanceChanged(); + + int getClientViewDistance(); + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fast_hand_swing/LivingEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fast_hand_swing/LivingEntityMixin.java new file mode 100644 index 0000000..ccf7cdf --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fast_hand_swing/LivingEntityMixin.java @@ -0,0 +1,28 @@ +package net.gensokyoreimagined.nitori.mixin.entity.fast_hand_swing; + +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(LivingEntity.class) +public abstract class LivingEntityMixin { + @Shadow + public boolean swinging; + + @Shadow + public int swingTime; + + @Inject( + method = "updateSwingTime", + at = @At("HEAD"), + cancellable = true + ) + private void skipGetDuration(CallbackInfo ci) { + if (!this.swinging && this.swingTime == 0) { + ci.cancel(); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinChunkFilter.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinChunkFilter.java new file mode 100644 index 0000000..0c04e76 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinChunkFilter.java @@ -0,0 +1,22 @@ +package net.gensokyoreimagined.nitori.mixin.playerwatching; + +import net.minecraft.server.level.ChunkTrackingView; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(ChunkTrackingView.class) +public interface MixinChunkFilter { + + /** + * @author ishland + * @reason use chebyshev distance + */ + @Overwrite + static boolean isWithinDistance(int centerX, int centerZ, int viewDistance, int x, int z, boolean includeEdge) { + int actualViewDistance = viewDistance + (includeEdge ? 1 : 0); + int xDistance = Math.abs(centerX - x); + int zDistance = Math.abs(centerZ - z); + return xDistance <= actualViewDistance && zDistance <= actualViewDistance; + } + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinServerPlayerEntity.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinServerPlayerEntity.java new file mode 100644 index 0000000..5357e9b --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinServerPlayerEntity.java @@ -0,0 +1,46 @@ +package net.gensokyoreimagined.nitori.mixin.playerwatching; + +import net.gensokyoreimagined.nitori.common.chunkwatching.PlayerClientVDTracking; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerPlayer; +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.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerPlayer.class) +public class MixinServerPlayerEntity implements PlayerClientVDTracking { + + @Unique + private boolean vdChanged = false; + + @Unique + private int clientVD = 2; + + @Inject(method = "updateOptions", at = @At("HEAD")) + private void onClientSettingsChanged(ClientInformation packet, CallbackInfo ci) { + final int currentVD = packet.viewDistance(); + if (currentVD != this.clientVD) this.vdChanged = true; + this.clientVD = Math.max(2, currentVD); + } + + @Inject(method = "restoreFrom", at = @At("RETURN")) + private void onPlayerCopy(ServerPlayer oldPlayer, boolean alive, CallbackInfo ci) { + this.clientVD = ((PlayerClientVDTracking) oldPlayer).getClientViewDistance(); + this.vdChanged = true; + } + + @Unique + @Override + public boolean isClientViewDistanceChanged() { + return this.vdChanged; + } + + @Unique + @Override + public int getClientViewDistance() { + this.vdChanged = false; + return this.clientVD; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java new file mode 100644 index 0000000..3a4e019 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java @@ -0,0 +1,35 @@ +package net.gensokyoreimagined.nitori.mixin.playerwatching.optimize_nearby_player_lookups; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.List; +import java.util.function.Predicate; + +@Mixin(Mob.class) +public abstract class MixinMobEntity extends LivingEntity { + + protected MixinMobEntity(EntityType entityType, Level world) { + super(entityType, world); + } + + @Redirect(method = "checkDespawn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;findNearbyPlayer(Lnet/minecraft/world/entity/Entity;DLjava/util/function/Predicate;)Lnet/minecraft/world/entity/player/Player;")) + private Player redirectGetClosestPlayer(Level instance, Entity entity, double v, Predicate predicate) { + final Player closestPlayer = instance.getNearestPlayer(entity, this.getType().getCategory().getDespawnDistance()); + if (closestPlayer != null) { + return closestPlayer; + } else { + final List players = this.level().players(); + if (players.isEmpty()) return null; + return players.get(0); + } + } + +} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 591967e..e219f15 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -39,6 +39,7 @@ "shapes.blockstate_cache.BlockMixin", "shapes.lazy_shape_context.EntityShapeContextMixin", "entity.fast_retrieval.SectionedEntityCacheMixin", + "entity.fast_hand_swing.LivingEntityMixin", "math.fast_blockops.DirectionMixin", "math.fast_blockops.BlockPosMixin", "math.fast_util.AxisCycleDirectionMixin$ForwardMixin", @@ -61,6 +62,8 @@ "util.accessors.EntityTrackingSectionAccessor", "util.accessors.ServerEntityManagerAccessor", "util.block_tracking.AbstractBlockStateMixin", - "MixinEntityTickList" + "playerwatching.MixinChunkFilter", + "playerwatching.MixinServerPlayerEntity", + "playerwatching.optimize_nearby_player_lookups.MixinMobEntity" ] } From cd9e06fea77e7be89bcebe787b3b4ed24f09e027 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Wed, 10 Jul 2024 03:47:04 +0300 Subject: [PATCH 12/21] Improve MobSpawns and BlockEntities prep to palette changes --- .../world/blockentity/SupportCache.java | 5 + .../world/chunk/LithiumHashPalette.java | 197 ++++++++++++++++++ .../chunk/palette/PalettedContainerMixin.java | 197 ++++++++++++++++++ .../mob_spawning/SpawnSettingsMixin.java | 36 ++++ .../support_cache/BlockEntityMixin.java | 37 ++++ .../DirectBlockEntityTickInvokerMixin.java | 53 +++++ .../support_cache/WorldChunkMixin.java | 49 +++++ src/main/resources/mixins.core.json | 4 + 8 files changed, 578 insertions(+) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/world/blockentity/SupportCache.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/collections/mob_spawning/SpawnSettingsMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/BlockEntityMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/DirectBlockEntityTickInvokerMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/WorldChunkMixin.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/blockentity/SupportCache.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/blockentity/SupportCache.java new file mode 100644 index 0000000..9c68885 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/blockentity/SupportCache.java @@ -0,0 +1,5 @@ +package net.gensokyoreimagined.nitori.common.world.blockentity; + +public interface SupportCache { + boolean lithium$isSupported(); +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java new file mode 100644 index 0000000..8d9d575 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java @@ -0,0 +1,197 @@ +package net.gensokyoreimagined.nitori.common.world.chunk; + +//import com.google.common.collect.ImmutableList; +//import it.unimi.dsi.fastutil.HashCommon; +//import it.unimi.dsi.fastutil.objects.Reference2IntMap; +//import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +//import net.minecraft.network.FriendlyByteBuf; +//import net.minecraft.network.VarInt; +//import net.minecraft.core.IdMap; +//import net.minecraft.world.level.chunk.MissingPaletteEntryException; +//import net.minecraft.world.level.chunk.Palette; +//import net.minecraft.world.level.chunk.PaletteResize; +// +//import java.util.Arrays; +//import java.util.List; +//import java.util.function.Predicate; +// +//import static it.unimi.dsi.fastutil.Hash.FAST_LOAD_FACTOR; +// +///** +// * Generally provides better performance over the vanilla {@link net.minecraft.world.chunk.BiMapPalette} when calling +// * {@link LithiumHashPalette#index(Object)} through using a faster backing map and reducing pointer chasing. +// */ +//public class LithiumHashPalette implements Palette { +// private static final int ABSENT_VALUE = -1; +// +// private final IdMap idList; +// private final PaletteResize resizeHandler; +// private final int indexBits; +// +// private final Reference2IntMap table; +// private T[] entries; +// private int size = 0; +// +// public LithiumHashPalette(IdMap idList, PaletteResize resizeHandler, int indexBits, T[] entries, Reference2IntMap table, int size) { +// this.idList = idList; +// this.resizeHandler = resizeHandler; +// this.indexBits = indexBits; +// this.entries = entries; +// this.table = table; +// this.size = size; +// } +// +// public LithiumHashPalette(IdMap idList, int bits, PaletteResizeListener resizeHandler, List list) { +// this(idList, bits, resizeHandler); +// +// for (T t : list) { +// this.addEntry(t); +// } +// } +// +// @SuppressWarnings("unchecked") +// public LithiumHashPalette(IdMap idList, int bits, PaletteResizeListener resizeHandler) { +// this.idList = idList; +// this.indexBits = bits; +// this.resizeHandler = resizeHandler; +// +// int capacity = 1 << bits; +// +// this.entries = (T[]) new Object[capacity]; +// this.table = new Reference2IntOpenHashMap<>(capacity, FAST_LOAD_FACTOR); +// this.table.defaultReturnValue(ABSENT_VALUE); +// } +// +// @Override +// public int index(T obj) { +// int id = this.table.getInt(obj); +// +// if (id == ABSENT_VALUE) { +// id = this.computeEntry(obj); +// } +// +// return id; +// } +// +// @Override +// public boolean hasAny(Predicate predicate) { +// for (int i = 0; i < this.size; ++i) { +// if (predicate.test(this.entries[i])) { +// return true; +// } +// } +// +// return false; +// } +// +// private int computeEntry(T obj) { +// int id = this.addEntry(obj); +// +// if (id >= 1 << this.indexBits) { +// if (this.resizeHandler == null) { +// throw new IllegalStateException("Cannot grow"); +// } else { +// id = this.resizeHandler.onResize(this.indexBits + 1, obj); +// } +// } +// +// return id; +// } +// +// private int addEntry(T obj) { +// int nextId = this.size; +// +// if (nextId >= this.entries.length) { +// this.resize(this.size); +// } +// +// this.table.put(obj, nextId); +// this.entries[nextId] = obj; +// +// this.size++; +// +// return nextId; +// } +// +// private void resize(int neededCapacity) { +// this.entries = Arrays.copyOf(this.entries, HashCommon.nextPowerOfTwo(neededCapacity + 1)); +// } +// +// @Override +// public T get(int id) { +// T[] entries = this.entries; +// +// T entry = null; +// if (id >= 0 && id < entries.length) { +// entry = entries[id]; +// } +// +// if (entry != null) { +// return entry; +// } else { +// throw new MissingPaletteEntryException(id); +// } +// } +// +// @Override +// public void readPacket(FriendlyByteBuf buf) { +// this.clear(); +// +// int entryCount = buf.readVarInt(); +// +// for (int i = 0; i < entryCount; ++i) { +// this.addEntry(this.idList.get(buf.readVarInt())); +// } +// } +// +// @Override +// public void writePacket(FriendlyByteBuf buf) { +// int size = this.size; +// buf.writeVarInt(size); +// +// for (int i = 0; i < size; ++i) { +// buf.writeVarInt(this.idList.getRawId(this.get(i))); +// } +// } +// +// @Override +// public int getPacketSize() { +// int size = VarInt.getSizeInBytes(this.size); +// +// for (int i = 0; i < this.size; ++i) { +// size += VarInt.getSizeInBytes(this.idList.getRawId(this.get(i))); +// } +// +// return size; +// } +// +// @Override +// public int getSize() { +// return this.size; +// } +// +// @Override +// public Palette copy() { +// return new LithiumHashPalette<>(this.idList, this.resizeHandler, this.indexBits, this.entries.clone(), new Reference2IntOpenHashMap<>(this.table), this.size); +// } +// +// private void clear() { +// Arrays.fill(this.entries, null); +// this.table.clear(); +// this.size = 0; +// } +// +// public List getElements() { +// ImmutableList.Builder builder = new ImmutableList.Builder<>(); +// for (T entry : this.entries) { +// if (entry != null) { +// builder.add(entry); +// } +// } +// return builder.build(); +// } +// +// public static Palette create(int bits, IdMap idList, PaletteResizeListener listener, List list) { +// return new LithiumHashPalette<>(idList, bits, listener, list); +// } +//} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java new file mode 100644 index 0000000..4f4f52f --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java @@ -0,0 +1,197 @@ +package net.gensokyoreimagined.nitori.mixin.chunk.palette; + +//import com.google.common.collect.ImmutableList; +//import it.unimi.dsi.fastutil.HashCommon; +//import it.unimi.dsi.fastutil.objects.Reference2IntMap; +//import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +//import net.minecraft.network.PacketByteBuf; +//import net.minecraft.network.encoding.VarInts; +//import net.minecraft.util.collection.IndexedIterable; +//import net.minecraft.world.chunk.EntryMissingException; +//import net.minecraft.world.chunk.Palette; +//import net.minecraft.world.chunk.PaletteResizeListener; +// +//import java.util.Arrays; +//import java.util.List; +//import java.util.function.Predicate; +// +//import static it.unimi.dsi.fastutil.Hash.FAST_LOAD_FACTOR; +// +///** +// * Generally provides better performance over the vanilla {@link net.minecraft.world.chunk.BiMapPalette} when calling +// * {@link LithiumHashPalette#index(Object)} through using a faster backing map and reducing pointer chasing. +// */ +//public class LithiumHashPalette implements Palette { +// private static final int ABSENT_VALUE = -1; +// +// private final IndexedIterable idList; +// private final PaletteResizeListener resizeHandler; +// private final int indexBits; +// +// private final Reference2IntMap table; +// private T[] entries; +// private int size = 0; +// +// public LithiumHashPalette(IndexedIterable idList, PaletteResizeListener resizeHandler, int indexBits, T[] entries, Reference2IntMap table, int size) { +// this.idList = idList; +// this.resizeHandler = resizeHandler; +// this.indexBits = indexBits; +// this.entries = entries; +// this.table = table; +// this.size = size; +// } +// +// public LithiumHashPalette(IndexedIterable idList, int bits, PaletteResizeListener resizeHandler, List list) { +// this(idList, bits, resizeHandler); +// +// for (T t : list) { +// this.addEntry(t); +// } +// } +// +// @SuppressWarnings("unchecked") +// public LithiumHashPalette(IndexedIterable idList, int bits, PaletteResizeListener resizeHandler) { +// this.idList = idList; +// this.indexBits = bits; +// this.resizeHandler = resizeHandler; +// +// int capacity = 1 << bits; +// +// this.entries = (T[]) new Object[capacity]; +// this.table = new Reference2IntOpenHashMap<>(capacity, FAST_LOAD_FACTOR); +// this.table.defaultReturnValue(ABSENT_VALUE); +// } +// +// @Override +// public int index(T obj) { +// int id = this.table.getInt(obj); +// +// if (id == ABSENT_VALUE) { +// id = this.computeEntry(obj); +// } +// +// return id; +// } +// +// @Override +// public boolean hasAny(Predicate predicate) { +// for (int i = 0; i < this.size; ++i) { +// if (predicate.test(this.entries[i])) { +// return true; +// } +// } +// +// return false; +// } +// +// private int computeEntry(T obj) { +// int id = this.addEntry(obj); +// +// if (id >= 1 << this.indexBits) { +// if (this.resizeHandler == null) { +// throw new IllegalStateException("Cannot grow"); +// } else { +// id = this.resizeHandler.onResize(this.indexBits + 1, obj); +// } +// } +// +// return id; +// } +// +// private int addEntry(T obj) { +// int nextId = this.size; +// +// if (nextId >= this.entries.length) { +// this.resize(this.size); +// } +// +// this.table.put(obj, nextId); +// this.entries[nextId] = obj; +// +// this.size++; +// +// return nextId; +// } +// +// private void resize(int neededCapacity) { +// this.entries = Arrays.copyOf(this.entries, HashCommon.nextPowerOfTwo(neededCapacity + 1)); +// } +// +// @Override +// public T get(int id) { +// T[] entries = this.entries; +// +// T entry = null; +// if (id >= 0 && id < entries.length) { +// entry = entries[id]; +// } +// +// if (entry != null) { +// return entry; +// } else { +// throw new EntryMissingException(id); +// } +// } +// +// @Override +// public void readPacket(PacketByteBuf buf) { +// this.clear(); +// +// int entryCount = buf.readVarInt(); +// +// for (int i = 0; i < entryCount; ++i) { +// this.addEntry(this.idList.get(buf.readVarInt())); +// } +// } +// +// @Override +// public void writePacket(PacketByteBuf buf) { +// int size = this.size; +// buf.writeVarInt(size); +// +// for (int i = 0; i < size; ++i) { +// buf.writeVarInt(this.idList.getRawId(this.get(i))); +// } +// } +// +// @Override +// public int getPacketSize() { +// int size = VarInts.getSizeInBytes(this.size); +// +// for (int i = 0; i < this.size; ++i) { +// size += VarInts.getSizeInBytes(this.idList.getRawId(this.get(i))); +// } +// +// return size; +// } +// +// @Override +// public int getSize() { +// return this.size; +// } +// +// @Override +// public Palette copy() { +// return new LithiumHashPalette<>(this.idList, this.resizeHandler, this.indexBits, this.entries.clone(), new Reference2IntOpenHashMap<>(this.table), this.size); +// } +// +// private void clear() { +// Arrays.fill(this.entries, null); +// this.table.clear(); +// this.size = 0; +// } +// +// public List getElements() { +// ImmutableList.Builder builder = new ImmutableList.Builder<>(); +// for (T entry : this.entries) { +// if (entry != null) { +// builder.add(entry); +// } +// } +// return builder.build(); +// } +// +// public static Palette create(int bits, IndexedIterable idList, PaletteResizeListener listener, List list) { +// return new LithiumHashPalette<>(idList, bits, listener, list); +// } +//} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/mob_spawning/SpawnSettingsMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/mob_spawning/SpawnSettingsMixin.java new file mode 100644 index 0000000..54e6d58 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/mob_spawning/SpawnSettingsMixin.java @@ -0,0 +1,36 @@ +package net.gensokyoreimagined.nitori.mixin.collections.mob_spawning; + +import com.google.common.collect.Maps; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.util.random.WeightedRandomList; +import net.minecraft.world.level.biome.MobSpawnSettings; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Map; + +@Mixin(MobSpawnSettings.class) +public class SpawnSettingsMixin { + @Mutable + @Shadow + @Final + private Map> spawners; + + + @Inject(method = "(FLjava/util/Map;Ljava/util/Map;)V", at = @At("RETURN")) + private void reinit(float creatureSpawnProbability, Map> spawners, Map, MobSpawnSettings.MobSpawnCost> spawnCosts, CallbackInfo ci) { + Map> spawns = Maps.newEnumMap(MobCategory.class); + + for (Map.Entry> entry : this.spawners.entrySet()) { + spawns.put(entry.getKey(), entry.getValue()); + } + + this.spawners = spawns; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/BlockEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/BlockEntityMixin.java new file mode 100644 index 0000000..c34d426 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/BlockEntityMixin.java @@ -0,0 +1,37 @@ +package net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.support_cache; + +import net.gensokyoreimagined.nitori.common.world.blockentity.SupportCache; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.core.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +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.callback.CallbackInfo; + +@Mixin(BlockEntity.class) +public abstract class BlockEntityMixin implements SupportCache { + @Shadow + public abstract BlockEntityType getType(); + + @Unique + private boolean supportTestResult; + + @Inject(method = "(Lnet/minecraft/world/level/block/entity/BlockEntityType;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)V", at = @At("RETURN")) + private void initSupportCache(BlockEntityType type, BlockPos pos, BlockState cachedState, CallbackInfo ci) { + this.supportTestResult = this.getType().isValid(cachedState); + } + + @Inject(method = "setBlockState", at = @At("RETURN")) + private void updateSupportCache(BlockState cachedState, CallbackInfo ci) { + this.supportTestResult = this.getType().isValid(cachedState); + } + + @Override + public boolean lithium$isSupported() { + return this.supportTestResult; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/DirectBlockEntityTickInvokerMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/DirectBlockEntityTickInvokerMixin.java new file mode 100644 index 0000000..747b6f4 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/DirectBlockEntityTickInvokerMixin.java @@ -0,0 +1,53 @@ +package net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.support_cache; + +import net.gensokyoreimagined.nitori.common.world.blockentity.SupportCache; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.chunk.LevelChunk; +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.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; + +@Mixin(targets = "net.minecraft.world.level.chunk.LevelChunk$BoundTickingBlockEntity") +public class DirectBlockEntityTickInvokerMixin { + + @Shadow + @Final + private T blockEntity; + + @Redirect( + method = "tick", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/chunk/LevelChunk;getBlockState(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/block/state/BlockState;" + ), + slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;push(Ljava/util/function/Supplier;)V"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/BlockEntityTicker;tick(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/entity/BlockEntity;)V") + ) + ) + private BlockState getCachedState(LevelChunk chunk, BlockPos pos) { + return this.blockEntity.getBlockState(); + } + + @Redirect( + method = "tick()V", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/entity/BlockEntityType;isValid(Lnet/minecraft/world/level/block/state/BlockState;)Z" + ), + slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;push(Ljava/util/function/Supplier;)V"), + to = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/BlockEntityTicker;tick(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/entity/BlockEntity;)V") + ) + ) + private boolean cachedIsSupported(BlockEntityType blockEntityType, BlockState block) { + return ((SupportCache) this.blockEntity).lithium$isSupported(); + } + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/WorldChunkMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/WorldChunkMixin.java new file mode 100644 index 0000000..528caf1 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/block_entity_ticking/support_cache/WorldChunkMixin.java @@ -0,0 +1,49 @@ +package net.gensokyoreimagined.nitori.mixin.world.block_entity_ticking.support_cache; + +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.chunk.LevelChunk; +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; + + +@Mixin(LevelChunk.class) +public abstract class WorldChunkMixin { + + @Shadow + public abstract BlockState getBlockState(BlockPos pos); + + @Redirect( + method = "setBlockState(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;ZZ)Lnet/minecraft/world/level/block/state/BlockState;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/EntityBlock;newBlockEntity(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/world/level/block/entity/BlockEntity;" + ) + ) + private BlockEntity createBlockEntityWithCachedStateFix(EntityBlock EntityBlock, BlockPos pos, BlockState state) { + return EntityBlock.newBlockEntity(pos, this.getBlockState(pos)); + } + + @Inject( + method = "setBlockState(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;ZZ)Lnet/minecraft/world/level/block/state/BlockState;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/level/block/entity/BlockEntity;setBlockState(Lnet/minecraft/world/level/block/state/BlockState;)V", + shift = At.Shift.AFTER + ) + ) + private void fixCachedState(BlockPos pos, BlockState state, boolean flag, boolean doPlace, CallbackInfoReturnable cir, @Local BlockEntity blockEntity) { + BlockState updatedBlockState = this.getBlockState(pos); + if (updatedBlockState != state) { + //noinspection deprecation + blockEntity.setBlockState(updatedBlockState); + } + } +} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index e219f15..98efe52 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -53,11 +53,15 @@ "collections.chunk_tickets.SortedArraySetMixin", "collections.block_entity_tickers.WorldChunkMixin", "collections.goals.GoalSelectorMixin", + "collections.mob_spawning.SpawnSettingsMixin", "world.block_entity_ticking.sleeping.WrappedBlockEntityTickInvokerAccessor", "world.block_entity_ticking.sleeping.campfire.CampfireBlockEntityMixin", "world.block_entity_ticking.sleeping.campfire.unlit.CampfireBlockEntityMixin", "world.block_entity_ticking.sleeping.campfire.lit.CampfireBlockEntityMixin", "world.block_entity_ticking.sleeping.furnace.AbstractFurnaceBlockEntityMixin", + "world.block_entity_ticking.support_cache.DirectBlockEntityTickInvokerMixin", + "world.block_entity_ticking.support_cache.BlockEntityMixin", + "world.block_entity_ticking.support_cache.WorldChunkMixin", "util.accessors.ClientEntityManagerAccessor", "util.accessors.EntityTrackingSectionAccessor", "util.accessors.ServerEntityManagerAccessor", From eb47da27728d462a7096aa6156152ecea9c0a4f0 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Wed, 10 Jul 2024 07:48:04 +0300 Subject: [PATCH 13/21] disable regressed mixin and doggy pls check the commented mixins --- .../common/util/EquipmentSlotConstants.java | 12 +++ .../living_entity/LivingEntityMixin.java | 23 +++++ .../mixin/alloc/nbt/NbtCompoundMixin.java | 90 +++++++++++++++++++ .../mixin/{ => needs_testing}/MixinLevel.java | 2 +- src/main/resources/mixins.core.json | 1 - 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/util/EquipmentSlotConstants.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/nbt/NbtCompoundMixin.java rename src/main/java/net/gensokyoreimagined/nitori/mixin/{ => needs_testing}/MixinLevel.java (97%) diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/util/EquipmentSlotConstants.java b/src/main/java/net/gensokyoreimagined/nitori/common/util/EquipmentSlotConstants.java new file mode 100644 index 0000000..360fc23 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/util/EquipmentSlotConstants.java @@ -0,0 +1,12 @@ +package net.gensokyoreimagined.nitori.common.util; + +import net.minecraft.world.entity.EquipmentSlot; + +/** + * Pre-initialized constants to avoid unnecessary allocations. + */ +public final class EquipmentSlotConstants { + private EquipmentSlotConstants() {} + + public static final EquipmentSlot[] ALL = EquipmentSlot.values(); +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java new file mode 100644 index 0000000..e858d18 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java @@ -0,0 +1,23 @@ +package net.gensokyoreimagined.nitori.mixin.alloc.enum_values.living_entity; + +//import net.gensokyoreimagined.nitori.common.util.EquipmentSlotConstants; +//import net.minecraft.world.entity.EquipmentSlot; +//import net.minecraft.world.entity.LivingEntity; +//import org.spongepowered.asm.mixin.Mixin; +//import org.spongepowered.asm.mixin.injection.At; +//import org.spongepowered.asm.mixin.injection.Redirect; +// +//@Mixin(LivingEntity.class) +//public class LivingEntityMixin { +// +// @Redirect( +// method = "collectEquipmentChanges()Ljava/util/Map;", +// at = @At( +// value = "INVOKE", +// target = "Lnet/minecraft/world/entity/LivingEntity;equipmentHasChanged(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z" +// ) +// ) +// private EquipmentSlot[] removeAllocation() { +// return EquipmentSlotConstants.ALL; +// } +//} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/nbt/NbtCompoundMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/nbt/NbtCompoundMixin.java new file mode 100644 index 0000000..712d7e0 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/nbt/NbtCompoundMixin.java @@ -0,0 +1,90 @@ +package net.gensokyoreimagined.nitori.mixin.alloc.nbt; + +//import com.google.common.collect.Maps; +//import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +//import net.minecraft.nbt.CompoundTag; +//import net.minecraft.nbt.Tag; +//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.injection.At; +//import org.spongepowered.asm.mixin.injection.ModifyArg; +//import org.spongepowered.asm.mixin.injection.ModifyVariable; +//import org.spongepowered.asm.mixin.injection.Redirect; +// +//import java.util.HashMap; +//import java.util.Map; +// +///** +// * Use {@link Object2ObjectOpenHashMap} instead of {@link HashMap} to reduce NBT memory consumption and improve +// * iteration speed. +// * +// * @author Maity +// */ +//@Mixin(CompoundTag.class) +//public class CompoundTagMixin { +// +// @Shadow +// @Final +// private Map entries; +// +// @ModifyArg( +// method = "()V", +// at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;(Ljava/util/Map;)V") +// ) +// private static Map useFasterCollection(Map oldMap) { +// return new Object2ObjectOpenHashMap<>(); +// } +// +// @Redirect( +// method = "()V", +// at = @At( +// value = "INVOKE", +// target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", +// remap = false +// ) +// ) +// private static HashMap removeOldMapAlloc() { +// return null; +// } +// +// /** +// * @reason Use faster collection +// * @author Maity +// */ +// @Overwrite +// public CompoundTag copy() { +// // [VanillaCopy] HashMap is replaced with Object2ObjectOpenHashMap +// var map = new Object2ObjectOpenHashMap<>(Maps.transformValues(this.entries, Tag::copy)); +// return new CompoundTag(map); +// } +// +// @Mixin(targets = "net.minecraft.nbt.CompoundTag$1") +// static class Type { +// +// @ModifyVariable( +// method = "loadCompound", +// at = @At( +// value = "INVOKE_ASSIGN", +// target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", +// remap = false +// ) +// ) +// private static Map useFasterCollection(Map map) { +// return new Object2ObjectOpenHashMap<>(); +// } +// +// @Redirect( +// method = "loadCompound", +// at = @At( +// value = "INVOKE", +// target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", +// remap = false +// ) +// ) +// private static HashMap removeOldMapAlloc() { +// return null; +// } +// } +//} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinLevel.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/MixinLevel.java similarity index 97% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/MixinLevel.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/MixinLevel.java index bac3ad4..0bad6c1 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinLevel.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/MixinLevel.java @@ -12,7 +12,7 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package net.gensokyoreimagined.nitori.mixin; +package net.gensokyoreimagined.nitori.mixin.needs_testing; import com.google.common.collect.Lists; import net.gensokyoreimagined.nitori.common.util.collections.HashedReferenceList; diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 98efe52..038501c 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -17,7 +17,6 @@ "MixinEntitySectionStorage", "MixinGameRules", "MixinIteratorSafeOrderedReferenceSet", - "MixinLevel", "MixinLevelStorageAccess", "MixinMob", "math.sine_lut.MixinMth", From 2a7939026f9d90d48db31308fd0197d6aab04e5d Mon Sep 17 00:00:00 2001 From: DoggySazHi Date: Tue, 9 Jul 2024 23:22:17 -0700 Subject: [PATCH 14/21] reimu AAAAAAAAAAAAAAAAA --- .../world/chunk/LithiumHashPalette.java | 392 +++++++++--------- .../living_entity/LivingEntityMixin.java | 42 +- .../mixin/alloc/nbt/NbtCompoundMixin.java | 90 ---- .../chunk/palette/PaletteResizeAccessor.java | 10 + .../PalettedContainerConfigurationMixin.java | 19 + .../chunk/palette/PalettedContainerMixin.java | 260 ++++-------- src/main/resources/mixins.core.json | 4 +- 7 files changed, 326 insertions(+), 491 deletions(-) delete mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/nbt/NbtCompoundMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PaletteResizeAccessor.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerConfigurationMixin.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java index 8d9d575..2edeaac 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java @@ -1,197 +1,199 @@ package net.gensokyoreimagined.nitori.common.world.chunk; -//import com.google.common.collect.ImmutableList; -//import it.unimi.dsi.fastutil.HashCommon; -//import it.unimi.dsi.fastutil.objects.Reference2IntMap; -//import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; -//import net.minecraft.network.FriendlyByteBuf; -//import net.minecraft.network.VarInt; -//import net.minecraft.core.IdMap; -//import net.minecraft.world.level.chunk.MissingPaletteEntryException; -//import net.minecraft.world.level.chunk.Palette; -//import net.minecraft.world.level.chunk.PaletteResize; -// -//import java.util.Arrays; -//import java.util.List; -//import java.util.function.Predicate; -// -//import static it.unimi.dsi.fastutil.Hash.FAST_LOAD_FACTOR; -// -///** -// * Generally provides better performance over the vanilla {@link net.minecraft.world.chunk.BiMapPalette} when calling -// * {@link LithiumHashPalette#index(Object)} through using a faster backing map and reducing pointer chasing. -// */ -//public class LithiumHashPalette implements Palette { -// private static final int ABSENT_VALUE = -1; -// -// private final IdMap idList; -// private final PaletteResize resizeHandler; -// private final int indexBits; -// -// private final Reference2IntMap table; -// private T[] entries; -// private int size = 0; -// -// public LithiumHashPalette(IdMap idList, PaletteResize resizeHandler, int indexBits, T[] entries, Reference2IntMap table, int size) { -// this.idList = idList; -// this.resizeHandler = resizeHandler; -// this.indexBits = indexBits; -// this.entries = entries; -// this.table = table; -// this.size = size; -// } -// -// public LithiumHashPalette(IdMap idList, int bits, PaletteResizeListener resizeHandler, List list) { -// this(idList, bits, resizeHandler); -// -// for (T t : list) { -// this.addEntry(t); -// } -// } -// -// @SuppressWarnings("unchecked") -// public LithiumHashPalette(IdMap idList, int bits, PaletteResizeListener resizeHandler) { -// this.idList = idList; -// this.indexBits = bits; -// this.resizeHandler = resizeHandler; -// -// int capacity = 1 << bits; -// -// this.entries = (T[]) new Object[capacity]; -// this.table = new Reference2IntOpenHashMap<>(capacity, FAST_LOAD_FACTOR); -// this.table.defaultReturnValue(ABSENT_VALUE); -// } -// -// @Override -// public int index(T obj) { -// int id = this.table.getInt(obj); -// -// if (id == ABSENT_VALUE) { -// id = this.computeEntry(obj); -// } -// -// return id; -// } -// -// @Override -// public boolean hasAny(Predicate predicate) { -// for (int i = 0; i < this.size; ++i) { -// if (predicate.test(this.entries[i])) { -// return true; -// } -// } -// -// return false; -// } -// -// private int computeEntry(T obj) { -// int id = this.addEntry(obj); -// -// if (id >= 1 << this.indexBits) { -// if (this.resizeHandler == null) { -// throw new IllegalStateException("Cannot grow"); -// } else { -// id = this.resizeHandler.onResize(this.indexBits + 1, obj); -// } -// } -// -// return id; -// } -// -// private int addEntry(T obj) { -// int nextId = this.size; -// -// if (nextId >= this.entries.length) { -// this.resize(this.size); -// } -// -// this.table.put(obj, nextId); -// this.entries[nextId] = obj; -// -// this.size++; -// -// return nextId; -// } -// -// private void resize(int neededCapacity) { -// this.entries = Arrays.copyOf(this.entries, HashCommon.nextPowerOfTwo(neededCapacity + 1)); -// } -// -// @Override -// public T get(int id) { -// T[] entries = this.entries; -// -// T entry = null; -// if (id >= 0 && id < entries.length) { -// entry = entries[id]; -// } -// -// if (entry != null) { -// return entry; -// } else { -// throw new MissingPaletteEntryException(id); -// } -// } -// -// @Override -// public void readPacket(FriendlyByteBuf buf) { -// this.clear(); -// -// int entryCount = buf.readVarInt(); -// -// for (int i = 0; i < entryCount; ++i) { -// this.addEntry(this.idList.get(buf.readVarInt())); -// } -// } -// -// @Override -// public void writePacket(FriendlyByteBuf buf) { -// int size = this.size; -// buf.writeVarInt(size); -// -// for (int i = 0; i < size; ++i) { -// buf.writeVarInt(this.idList.getRawId(this.get(i))); -// } -// } -// -// @Override -// public int getPacketSize() { -// int size = VarInt.getSizeInBytes(this.size); -// -// for (int i = 0; i < this.size; ++i) { -// size += VarInt.getSizeInBytes(this.idList.getRawId(this.get(i))); -// } -// -// return size; -// } -// -// @Override -// public int getSize() { -// return this.size; -// } -// -// @Override -// public Palette copy() { -// return new LithiumHashPalette<>(this.idList, this.resizeHandler, this.indexBits, this.entries.clone(), new Reference2IntOpenHashMap<>(this.table), this.size); -// } -// -// private void clear() { -// Arrays.fill(this.entries, null); -// this.table.clear(); -// this.size = 0; -// } -// -// public List getElements() { -// ImmutableList.Builder builder = new ImmutableList.Builder<>(); -// for (T entry : this.entries) { -// if (entry != null) { -// builder.add(entry); -// } -// } -// return builder.build(); -// } -// -// public static Palette create(int bits, IdMap idList, PaletteResizeListener listener, List list) { -// return new LithiumHashPalette<>(idList, bits, listener, list); -// } -//} \ No newline at end of file +import com.google.common.collect.ImmutableList; +import it.unimi.dsi.fastutil.HashCommon; +import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import net.gensokyoreimagined.nitori.mixin.chunk.palette.PaletteResizeAccessor; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.VarInt; +import net.minecraft.core.IdMap; +import net.minecraft.world.level.chunk.MissingPaletteEntryException; +import net.minecraft.world.level.chunk.Palette; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +import static it.unimi.dsi.fastutil.Hash.FAST_LOAD_FACTOR; + +/** +* Generally provides better performance over the vanilla {@link net.minecraft.world.level.chunk.HashMapPalette} when calling +* {@link LithiumHashPalette#idFor(Object)} through using a faster backing map and reducing pointer chasing. +*/ +public class LithiumHashPalette implements Palette { + private static final int ABSENT_VALUE = -1; + + private final IdMap idList; + // private final PaletteResize resizeHandler; + private final PaletteResizeAccessor resizeHandler; + private final int indexBits; + + private final Reference2IntMap table; + private T[] entries; + private int size = 0; + + public LithiumHashPalette(IdMap idList, PaletteResizeAccessor resizeHandler, int indexBits, T[] entries, Reference2IntMap table, int size) { + this.idList = idList; + this.resizeHandler = resizeHandler; + this.indexBits = indexBits; + this.entries = entries; + this.table = table; + this.size = size; + } + + public LithiumHashPalette(IdMap idList, int bits, PaletteResizeAccessor resizeHandler, List list) { + this(idList, bits, resizeHandler); + + for (T t : list) { + this.addEntry(t); + } + } + + @SuppressWarnings("unchecked") + public LithiumHashPalette(IdMap idList, int bits, PaletteResizeAccessor resizeHandler) { + this.idList = idList; + this.indexBits = bits; + this.resizeHandler = resizeHandler; + + int capacity = 1 << bits; + + this.entries = (T[]) new Object[capacity]; + this.table = new Reference2IntOpenHashMap<>(capacity, FAST_LOAD_FACTOR); + this.table.defaultReturnValue(ABSENT_VALUE); + } + + @Override + public int idFor(T obj) { + int id = this.table.getInt(obj); + + if (id == ABSENT_VALUE) { + id = this.computeEntry(obj); + } + + return id; + } + + @Override + public boolean maybeHas(Predicate predicate) { + for (int i = 0; i < this.size; ++i) { + if (predicate.test(this.entries[i])) { + return true; + } + } + + return false; + } + + private int computeEntry(T obj) { + int id = this.addEntry(obj); + + if (id >= 1 << this.indexBits) { + if (this.resizeHandler == null) { + throw new IllegalStateException("Cannot grow"); + } else { + id = this.resizeHandler.callOnResize(this.indexBits + 1, obj); + } + } + + return id; + } + + private int addEntry(T obj) { + int nextId = this.size; + + if (nextId >= this.entries.length) { + this.resize(this.size); + } + + this.table.put(obj, nextId); + this.entries[nextId] = obj; + + this.size++; + + return nextId; + } + + private void resize(int neededCapacity) { + this.entries = Arrays.copyOf(this.entries, HashCommon.nextPowerOfTwo(neededCapacity + 1)); + } + + @Override + public @NotNull T valueFor(int id) { + T[] entries = this.entries; + + T entry = null; + if (id >= 0 && id < entries.length) { + entry = entries[id]; + } + + if (entry != null) { + return entry; + } else { + throw new MissingPaletteEntryException(id); + } + } + + @Override + public void read(FriendlyByteBuf buf) { + this.clear(); + + int entryCount = buf.readVarInt(); + + for (int i = 0; i < entryCount; ++i) { + this.addEntry(this.idList.byId(buf.readVarInt())); + } + } + + @Override + public void write(FriendlyByteBuf buf) { + int size = this.size; + buf.writeVarInt(size); + + for (int i = 0; i < size; ++i) { + buf.writeVarInt(this.idList.getId(this.valueFor(i))); + } + } + + @Override + public int getSerializedSize() { + int size = VarInt.getByteSize(this.size); + + for (int i = 0; i < this.size; ++i) { + size += VarInt.getByteSize(this.idList.getId(this.valueFor(i))); + } + + return size; + } + + @Override + public int getSize() { + return this.size; + } + + @Override + public Palette copy() { + return new LithiumHashPalette<>(this.idList, this.resizeHandler, this.indexBits, this.entries.clone(), new Reference2IntOpenHashMap<>(this.table), this.size); + } + + private void clear() { + Arrays.fill(this.entries, null); + this.table.clear(); + this.size = 0; + } + + public List getElements() { + ImmutableList.Builder builder = new ImmutableList.Builder<>(); + for (T entry : this.entries) { + if (entry != null) { + builder.add(entry); + } + } + return builder.build(); + } + + public static Palette create(int bits, IdMap idList, PaletteResizeAccessor listener, List list) { + return new LithiumHashPalette<>(idList, bits, listener, list); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java index e858d18..df5e6c3 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java @@ -1,23 +1,23 @@ package net.gensokyoreimagined.nitori.mixin.alloc.enum_values.living_entity; -//import net.gensokyoreimagined.nitori.common.util.EquipmentSlotConstants; -//import net.minecraft.world.entity.EquipmentSlot; -//import net.minecraft.world.entity.LivingEntity; -//import org.spongepowered.asm.mixin.Mixin; -//import org.spongepowered.asm.mixin.injection.At; -//import org.spongepowered.asm.mixin.injection.Redirect; -// -//@Mixin(LivingEntity.class) -//public class LivingEntityMixin { -// -// @Redirect( -// method = "collectEquipmentChanges()Ljava/util/Map;", -// at = @At( -// value = "INVOKE", -// target = "Lnet/minecraft/world/entity/LivingEntity;equipmentHasChanged(Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z" -// ) -// ) -// private EquipmentSlot[] removeAllocation() { -// return EquipmentSlotConstants.ALL; -// } -//} \ No newline at end of file +import net.gensokyoreimagined.nitori.common.util.EquipmentSlotConstants; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(LivingEntity.class) +public class LivingEntityMixin { + + @Redirect( + method = "collectEquipmentChanges()Ljava/util/Map;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/entity/EquipmentSlot;values()[Lnet/minecraft/world/entity/EquipmentSlot;" + ) + ) + private EquipmentSlot[] removeAllocation() { + return EquipmentSlotConstants.ALL; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/nbt/NbtCompoundMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/nbt/NbtCompoundMixin.java deleted file mode 100644 index 712d7e0..0000000 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/nbt/NbtCompoundMixin.java +++ /dev/null @@ -1,90 +0,0 @@ -package net.gensokyoreimagined.nitori.mixin.alloc.nbt; - -//import com.google.common.collect.Maps; -//import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -//import net.minecraft.nbt.CompoundTag; -//import net.minecraft.nbt.Tag; -//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.injection.At; -//import org.spongepowered.asm.mixin.injection.ModifyArg; -//import org.spongepowered.asm.mixin.injection.ModifyVariable; -//import org.spongepowered.asm.mixin.injection.Redirect; -// -//import java.util.HashMap; -//import java.util.Map; -// -///** -// * Use {@link Object2ObjectOpenHashMap} instead of {@link HashMap} to reduce NBT memory consumption and improve -// * iteration speed. -// * -// * @author Maity -// */ -//@Mixin(CompoundTag.class) -//public class CompoundTagMixin { -// -// @Shadow -// @Final -// private Map entries; -// -// @ModifyArg( -// method = "()V", -// at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;(Ljava/util/Map;)V") -// ) -// private static Map useFasterCollection(Map oldMap) { -// return new Object2ObjectOpenHashMap<>(); -// } -// -// @Redirect( -// method = "()V", -// at = @At( -// value = "INVOKE", -// target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", -// remap = false -// ) -// ) -// private static HashMap removeOldMapAlloc() { -// return null; -// } -// -// /** -// * @reason Use faster collection -// * @author Maity -// */ -// @Overwrite -// public CompoundTag copy() { -// // [VanillaCopy] HashMap is replaced with Object2ObjectOpenHashMap -// var map = new Object2ObjectOpenHashMap<>(Maps.transformValues(this.entries, Tag::copy)); -// return new CompoundTag(map); -// } -// -// @Mixin(targets = "net.minecraft.nbt.CompoundTag$1") -// static class Type { -// -// @ModifyVariable( -// method = "loadCompound", -// at = @At( -// value = "INVOKE_ASSIGN", -// target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", -// remap = false -// ) -// ) -// private static Map useFasterCollection(Map map) { -// return new Object2ObjectOpenHashMap<>(); -// } -// -// @Redirect( -// method = "loadCompound", -// at = @At( -// value = "INVOKE", -// target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", -// remap = false -// ) -// ) -// private static HashMap removeOldMapAlloc() { -// return null; -// } -// } -//} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PaletteResizeAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PaletteResizeAccessor.java new file mode 100644 index 0000000..b84feb3 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PaletteResizeAccessor.java @@ -0,0 +1,10 @@ +package net.gensokyoreimagined.nitori.mixin.chunk.palette; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(targets = "net.minecraft.world.level.chunk.PaletteResize") +public abstract class PaletteResizeAccessor { + @Invoker + public abstract int callOnResize(int newBits, T object); +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerConfigurationMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerConfigurationMixin.java new file mode 100644 index 0000000..9076a67 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerConfigurationMixin.java @@ -0,0 +1,19 @@ +package net.gensokyoreimagined.nitori.mixin.chunk.palette; + +// import net.minecraft.core.IdMap; +// import net.minecraft.world.level.chunk.Palette; +// import net.minecraft.world.level.chunk.PaletteResize; +// import net.minecraft.world.level.chunk.PalettedContainer; +// import org.spongepowered.asm.mixin.Mixin; +// import org.spongepowered.asm.mixin.gen.Invoker; +// +// @Mixin(targets = "net.minecraft.world.level.chunk.PalettedContainer.Configuration") +// public abstract class PalettedContainerConfigurationMixin { +// @Invoker("(Lnet/minecraft/world/level/chunk/Palette$Factory;I)V") +// public static PalettedContainerConfigurationMixin create(Palette.Factory factory, int bits) { +// throw new AssertionError("mukyu~!"); +// } +// +// @Invoker +// public abstract PalettedContainer.Data createData(IdMap idList, PaletteResize listener, int size); +// } diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java index 4f4f52f..409e5b9 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java @@ -1,197 +1,89 @@ package net.gensokyoreimagined.nitori.mixin.chunk.palette; -//import com.google.common.collect.ImmutableList; -//import it.unimi.dsi.fastutil.HashCommon; -//import it.unimi.dsi.fastutil.objects.Reference2IntMap; -//import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; -//import net.minecraft.network.PacketByteBuf; -//import net.minecraft.network.encoding.VarInts; -//import net.minecraft.util.collection.IndexedIterable; -//import net.minecraft.world.chunk.EntryMissingException; -//import net.minecraft.world.chunk.Palette; -//import net.minecraft.world.chunk.PaletteResizeListener; +// import net.gensokyoreimagined.nitori.common.world.chunk.LithiumHashPalette; +// import net.minecraft.core.IdMap; +// import net.minecraft.world.level.chunk.PalettedContainer; +// import org.jetbrains.annotations.NotNull; +// import org.spongepowered.asm.mixin.*; +// import net.minecraft.world.level.chunk.Palette; // -//import java.util.Arrays; -//import java.util.List; -//import java.util.function.Predicate; +// import static net.minecraft.world.level.chunk.PalettedContainer.Strategy.LINEAR_PALETTE_FACTORY; +// import static net.minecraft.world.level.chunk.PalettedContainer.Strategy.SINGLE_VALUE_PALETTE_FACTORY; // -//import static it.unimi.dsi.fastutil.Hash.FAST_LOAD_FACTOR; +// @Mixin(PalettedContainer.Strategy.class) +// public abstract class PalettedContainerMixin { +// @Mutable +// @Shadow +// @Final +// public static PalettedContainer.Strategy SECTION_STATES; // -///** -// * Generally provides better performance over the vanilla {@link net.minecraft.world.chunk.BiMapPalette} when calling -// * {@link LithiumHashPalette#index(Object)} through using a faster backing map and reducing pointer chasing. -// */ -//public class LithiumHashPalette implements Palette { -// private static final int ABSENT_VALUE = -1; +// @Unique +// private static final PalettedContainerConfigurationMixin[] BLOCKSTATE_DATA_PROVIDERS; +// @Unique +// private static final PalettedContainerConfigurationMixin[] BIOME_DATA_PROVIDERS; // -// private final IndexedIterable idList; -// private final PaletteResizeListener resizeHandler; -// private final int indexBits; // -// private final Reference2IntMap table; -// private T[] entries; -// private int size = 0; +// @Unique +// private static final Palette.Factory HASH = LithiumHashPalette::create; +// @Mutable +// @Shadow +// @Final +// public static PalettedContainer.Strategy SECTION_BIOMES; +// @Shadow +// @Final +// static Palette.Factory GLOBAL_PALETTE_FACTORY; // -// public LithiumHashPalette(IndexedIterable idList, PaletteResizeListener resizeHandler, int indexBits, T[] entries, Reference2IntMap table, int size) { -// this.idList = idList; -// this.resizeHandler = resizeHandler; -// this.indexBits = indexBits; -// this.entries = entries; -// this.table = table; -// this.size = size; -// } +// /* +// * @reason Replace the hash palette from vanilla with our own and change the threshold for usage to only 3 bits, +// * as our implementation performs better at smaller key ranges. +// * @author JellySquid, 2No2Name (avoid Configuration duplication, use hash palette for 3 bit biomes) +// */ +// static { +// Palette.Factory idListFactory = GLOBAL_PALETTE_FACTORY; // -// public LithiumHashPalette(IndexedIterable idList, int bits, PaletteResizeListener resizeHandler, List list) { -// this(idList, bits, resizeHandler); +// PalettedContainerConfigurationMixin arrayConfiguration4bit = PalettedContainerConfigurationMixin.create(LINEAR_PALETTE_FACTORY, 4); +// PalettedContainerConfigurationMixin hashConfiguration4bit = PalettedContainerConfigurationMixin.create(HASH, 4); +// BLOCKSTATE_DATA_PROVIDERS = new PalettedContainerConfigurationMixin[]{ +// PalettedContainerConfigurationMixin.create(SINGLE_VALUE_PALETTE_FACTORY, 0), +// // Bits 1-4 must all pass 4 bits as parameter, otherwise chunk sections will corrupt. +// arrayConfiguration4bit, +// arrayConfiguration4bit, +// hashConfiguration4bit, +// hashConfiguration4bit, +// PalettedContainerConfigurationMixin.create(HASH, 5), +// PalettedContainerConfigurationMixin.create(HASH, 6), +// PalettedContainerConfigurationMixin.create(HASH, 7), +// PalettedContainerConfigurationMixin.create(HASH, 8) +// }; // -// for (T t : list) { -// this.addEntry(t); -// } -// } +// SECTION_STATES = new PalettedContainer.Strategy(4) { +// @Override +// public @NotNull PalettedContainerConfigurationMixin getConfiguration(@NotNull IdMap idList, int bits) { +// if (bits >= 0 && bits < BLOCKSTATE_DATA_PROVIDERS.length) { +// //noinspection unchecked +// return (PalettedContainerConfigurationMixin) BLOCKSTATE_DATA_PROVIDERS[bits]; +// } +// return PalettedContainerConfigurationMixin.create(idListFactory, MathHelper.ceilLog2(idList.size())); +// } +// }; // -// @SuppressWarnings("unchecked") -// public LithiumHashPalette(IndexedIterable idList, int bits, PaletteResizeListener resizeHandler) { -// this.idList = idList; -// this.indexBits = bits; -// this.resizeHandler = resizeHandler; +// BIOME_DATA_PROVIDERS = new PalettedContainerConfigurationMixin[]{ +// PalettedContainerConfigurationMixin.create(SINGLE_VALUE_PALETTE_FACTORY, 0), +// PalettedContainerConfigurationMixin.create(LINEAR_PALETTE_FACTORY, 1), +// PalettedContainerConfigurationMixin.create(LINEAR_PALETTE_FACTORY, 2), +// PalettedContainerConfigurationMixin.create(HASH, 3) +// }; // -// int capacity = 1 << bits; // -// this.entries = (T[]) new Object[capacity]; -// this.table = new Reference2IntOpenHashMap<>(capacity, FAST_LOAD_FACTOR); -// this.table.defaultReturnValue(ABSENT_VALUE); -// } -// -// @Override -// public int index(T obj) { -// int id = this.table.getInt(obj); -// -// if (id == ABSENT_VALUE) { -// id = this.computeEntry(obj); -// } -// -// return id; -// } -// -// @Override -// public boolean hasAny(Predicate predicate) { -// for (int i = 0; i < this.size; ++i) { -// if (predicate.test(this.entries[i])) { -// return true; -// } -// } -// -// return false; -// } -// -// private int computeEntry(T obj) { -// int id = this.addEntry(obj); -// -// if (id >= 1 << this.indexBits) { -// if (this.resizeHandler == null) { -// throw new IllegalStateException("Cannot grow"); -// } else { -// id = this.resizeHandler.onResize(this.indexBits + 1, obj); -// } -// } -// -// return id; -// } -// -// private int addEntry(T obj) { -// int nextId = this.size; -// -// if (nextId >= this.entries.length) { -// this.resize(this.size); -// } -// -// this.table.put(obj, nextId); -// this.entries[nextId] = obj; -// -// this.size++; -// -// return nextId; -// } -// -// private void resize(int neededCapacity) { -// this.entries = Arrays.copyOf(this.entries, HashCommon.nextPowerOfTwo(neededCapacity + 1)); -// } -// -// @Override -// public T get(int id) { -// T[] entries = this.entries; -// -// T entry = null; -// if (id >= 0 && id < entries.length) { -// entry = entries[id]; -// } -// -// if (entry != null) { -// return entry; -// } else { -// throw new EntryMissingException(id); -// } -// } -// -// @Override -// public void readPacket(PacketByteBuf buf) { -// this.clear(); -// -// int entryCount = buf.readVarInt(); -// -// for (int i = 0; i < entryCount; ++i) { -// this.addEntry(this.idList.get(buf.readVarInt())); -// } -// } -// -// @Override -// public void writePacket(PacketByteBuf buf) { -// int size = this.size; -// buf.writeVarInt(size); -// -// for (int i = 0; i < size; ++i) { -// buf.writeVarInt(this.idList.getRawId(this.get(i))); -// } -// } -// -// @Override -// public int getPacketSize() { -// int size = VarInts.getSizeInBytes(this.size); -// -// for (int i = 0; i < this.size; ++i) { -// size += VarInts.getSizeInBytes(this.idList.getRawId(this.get(i))); -// } -// -// return size; -// } -// -// @Override -// public int getSize() { -// return this.size; -// } -// -// @Override -// public Palette copy() { -// return new LithiumHashPalette<>(this.idList, this.resizeHandler, this.indexBits, this.entries.clone(), new Reference2IntOpenHashMap<>(this.table), this.size); -// } -// -// private void clear() { -// Arrays.fill(this.entries, null); -// this.table.clear(); -// this.size = 0; -// } -// -// public List getElements() { -// ImmutableList.Builder builder = new ImmutableList.Builder<>(); -// for (T entry : this.entries) { -// if (entry != null) { -// builder.add(entry); -// } -// } -// return builder.build(); -// } -// -// public static Palette create(int bits, IndexedIterable idList, PaletteResizeListener listener, List list) { -// return new LithiumHashPalette<>(idList, bits, listener, list); -// } -//} \ No newline at end of file +// SECTION_BIOMES = new PalettedContainer.Strategy(2) { +// @Override +// public @NotNull PalettedContainerConfigurationMixin getConfiguration(@NotNull IdMap idList, int bits) { +// if (bits >= 0 && bits < BIOME_DATA_PROVIDERS.length) { +// //noinspection unchecked +// return (PalettedContainerConfigurationMixin) BIOME_DATA_PROVIDERS[bits]; +// } +// return PalettedContainerConfigurationMixin.create(idListFactory, MathHelper.ceilLog2(idList.size())); +// } +// }; +// } +// } \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 038501c..97149fb 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -67,6 +67,8 @@ "util.block_tracking.AbstractBlockStateMixin", "playerwatching.MixinChunkFilter", "playerwatching.MixinServerPlayerEntity", - "playerwatching.optimize_nearby_player_lookups.MixinMobEntity" + "playerwatching.optimize_nearby_player_lookups.MixinMobEntity", + "alloc.enum_values.living_entity.LivingEntityMixin", + "chunk.palette.PaletteResizeAccessor", ] } From 4d5b6e71b6d898753ef4c27a1bdf8797a81fa51b Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 11 Jul 2024 07:51:08 +0300 Subject: [PATCH 15/21] Inlining, Faster Math --- .../nitori/common/math/FasterMathUtil.java | 46 ++++++ .../world/ChunkAwareEntityIterable.java | 9 ++ .../living_entity/LivingEntityMixin.java | 2 +- .../attributes/AttributeContainerMixin.java | 40 ++++++ .../mob_spawning/SpawnSettingsMixin.java | 2 +- .../spawning/ServerChunkManagerMixin.java | 29 ++++ .../ChunkSectionPosAccessor.java | 13 ++ .../OptimizedBlockPosBitsMixin.java | 67 +++++++++ .../OptimizedChunkSecPosBitsMixin.java | 136 ++++++++++++++++++ .../nitori/mixin/math/joml/JOMLMixin.java | 64 +++++++++ .../rounding/FastRoundingVoxShapeMixin.java | 26 ++++ src/main/resources/mixins.core.json | 6 +- 12 files changed, 436 insertions(+), 4 deletions(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/math/FasterMathUtil.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/common/world/ChunkAwareEntityIterable.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/collections/attributes/AttributeContainerMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/experimental/spawning/ServerChunkManagerMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/ChunkSectionPosAccessor.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/OptimizedBlockPosBitsMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/OptimizedChunkSecPosBitsMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/math/joml/JOMLMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/math/rounding/FastRoundingVoxShapeMixin.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/math/FasterMathUtil.java b/src/main/java/net/gensokyoreimagined/nitori/common/math/FasterMathUtil.java new file mode 100644 index 0000000..4e4f31a --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/math/FasterMathUtil.java @@ -0,0 +1,46 @@ +package net.gensokyoreimagined.nitori.common.math; + +/** + * @author jafama library + * https://github.com/jeffhain/jafama + */ +public final class FasterMathUtil { + private static final int MAX_FLOAT_EXPONENT = 127; + private static final int MAX_DOUBLE_EXPONENT = 1023; + + public static int round(float a) { + final int bits = Float.floatToRawIntBits(a); + final int biasedExp = ((bits >> 23) & 0xFF); + final int shift = (23 - 1 + MAX_FLOAT_EXPONENT) - biasedExp; + if ((shift & -32) == 0) { + int bitsSignum = (((bits >> 31) << 1) + 1); + int extendedMantissa = (0x00800000 | (bits & 0x007FFFFF)) * bitsSignum; + return ((extendedMantissa >> shift) + 1) >> 1; + } else { + return (int) a; + } + } + + public static long round(double a) { + final long bits = Double.doubleToRawLongBits(a); + final int biasedExp = (((int)(bits >> 52)) & 0x7FF); + final int shift = (52 - 1 + MAX_DOUBLE_EXPONENT) - biasedExp; + if ((shift & -64) == 0) { + long bitsSignum = (((bits >> 63) << 1) + 1); + long extendedMantissa = (0x0010000000000000L | (bits & 0x000FFFFFFFFFFFFFL)) * bitsSignum; + return ((extendedMantissa >> shift) + 1L) >> 1; + } else { + return (long) a; + } + } + + public static float positiveModuloForPositiveIntegerDivisor(float dividend, float divisor) { + var modulo = dividend % divisor; + return modulo < 0 ? modulo + divisor : modulo; + } + + public static double positiveModuloForPositiveIntegerDivisor(double dividend, double divisor) { + var modulo = dividend % divisor; + return modulo < 0 ? modulo + divisor : modulo; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/ChunkAwareEntityIterable.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/ChunkAwareEntityIterable.java new file mode 100644 index 0000000..1e3136a --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/ChunkAwareEntityIterable.java @@ -0,0 +1,9 @@ +package net.gensokyoreimagined.nitori.common.world; + +//import net.minecraft.world.level.entity.EntityAccess; +// +//public interface ChunkAwareEntityIterable { +// Iterable lithium$IterateEntitiesInTrackedSections(); +//} + +//PersistentEntitySectionManager does not work on paper \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java index df5e6c3..fce653b 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java @@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; public class LivingEntityMixin { @Redirect( - method = "collectEquipmentChanges()Ljava/util/Map;", + method = "collectEquipmentChanges", at = @At( value = "INVOKE", target = "Lnet/minecraft/world/entity/EquipmentSlot;values()[Lnet/minecraft/world/entity/EquipmentSlot;" diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/attributes/AttributeContainerMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/attributes/AttributeContainerMixin.java new file mode 100644 index 0000000..3f8ccca --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/attributes/AttributeContainerMixin.java @@ -0,0 +1,40 @@ +package net.gensokyoreimagined.nitori.mixin.collections.attributes; + +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import net.minecraft.world.entity.ai.attributes.AttributeMap; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.entity.ai.attributes.AttributeInstance; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Map; +import java.util.Set; + +@Mixin(AttributeMap.class) +public class AttributeContainerMixin { + @Mutable + @Shadow + @Final + private Map attributes; + + @Mutable + @Shadow + @Final + private Set dirtyAttributes; + + @Inject( + method = "", + at = @At("RETURN") + ) + private void initCollections(AttributeSupplier defaultAttributes, CallbackInfo ci) { + this.attributes = new Reference2ReferenceOpenHashMap<>(0); + this.dirtyAttributes = new ReferenceOpenHashSet<>(0); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/mob_spawning/SpawnSettingsMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/mob_spawning/SpawnSettingsMixin.java index 54e6d58..5051546 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/mob_spawning/SpawnSettingsMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/mob_spawning/SpawnSettingsMixin.java @@ -23,7 +23,7 @@ public class SpawnSettingsMixin { private Map> spawners; - @Inject(method = "(FLjava/util/Map;Ljava/util/Map;)V", at = @At("RETURN")) + @Inject(method = "", at = @At("RETURN")) private void reinit(float creatureSpawnProbability, Map> spawners, Map, MobSpawnSettings.MobSpawnCost> spawnCosts, CallbackInfo ci) { Map> spawns = Maps.newEnumMap(MobCategory.class); diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/experimental/spawning/ServerChunkManagerMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/experimental/spawning/ServerChunkManagerMixin.java new file mode 100644 index 0000000..51c7f82 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/experimental/spawning/ServerChunkManagerMixin.java @@ -0,0 +1,29 @@ +package net.gensokyoreimagined.nitori.mixin.experimental.spawning; + +//import net.gensokyoreimagined.nitori.common.world.ChunkAwareEntityIterable; +//import net.gensokyoreimagined.nitori.mixin.util.accessors.ServerEntityManagerAccessor; +//import net.gensokyoreimagined.nitori.mixin.util.accessors.ServerWorldAccessor; +//import net.minecraft.server.level.ServerChunkCache; +//import net.minecraft.server.level.ServerLevel; +//import net.minecraft.world.entity.Entity; +//import org.spongepowered.asm.mixin.Mixin; +//import org.spongepowered.asm.mixin.injection.At; +//import org.spongepowered.asm.mixin.injection.Redirect; +// +//@Mixin(ServerChunkCache.class) +//public class ServerChunkManagerMixin { +// +// @Redirect( +// method = "tickChunks", +// at = @At( +// value = "INVOKE", +// target = "Lnet/minecraft/server/level/ServerLevel;getAllEntities()Ljava/lang/Iterable;" +// ) +// ) +// private Iterable iterateEntitiesChunkAware(ServerLevel serverLevel) { +// //noinspection unchecked +// return ((ChunkAwareEntityIterable) ((ServerEntityManagerAccessor) ((ServerWorldAccessor) serverLevel).getEntityManager()).getSectionStorage()).lithium$IterateEntitiesInTrackedSections(); +// } +//} + +//PersistentEntitySectionManager does not work on paper \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/ChunkSectionPosAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/ChunkSectionPosAccessor.java new file mode 100644 index 0000000..257d81c --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/ChunkSectionPosAccessor.java @@ -0,0 +1,13 @@ +package net.gensokyoreimagined.nitori.mixin.logic.fast_bits_blockpos; + +import net.minecraft.core.SectionPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(SectionPos.class) +public interface ChunkSectionPosAccessor { + @Invoker("") + public static SectionPos invokeChunkSectionPos(int i, int j, int k) { + throw new AssertionError(); + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/OptimizedBlockPosBitsMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/OptimizedBlockPosBitsMixin.java new file mode 100644 index 0000000..10a259e --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/OptimizedBlockPosBitsMixin.java @@ -0,0 +1,67 @@ +package net.gensokyoreimagined.nitori.mixin.logic.fast_bits_blockpos; + +import net.minecraft.core.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +/** + * Credit to PaperMC Patch #0421 + */ +@Mixin(BlockPos.class) +public class OptimizedBlockPosBitsMixin { + + /** + * @author QPCrummer + * @reason Inline + */ + @Overwrite + public static long offset(long value, int x, int y, int z) { + return asLong((int) (value >> 38) + x, (int) ((value << 52) >> 52) + y, (int) ((value << 26) >> 38) + z); + } + + /** + * @author QPCrummer + * @reason Inline + */ + @Overwrite + public static int getX(long packedPos) { + return (int) (packedPos >> 38); + } + + /** + * @author QPCrummer + * @reason Inline + */ + @Overwrite + public static int getY(long packedPos) { + return (int) ((packedPos << 52) >> 52); + } + + /** + * @author QPCrummer + * @reason Inline + */ + @Overwrite + public static int getZ(long packedPos) { + return (int) ((packedPos << 26) >> 38); + } + + /** + * @author QPCrummer + * @reason Inline + */ + @Overwrite + public static BlockPos of(long packedPos) { + return new BlockPos((int) (packedPos >> 38), (int) ((packedPos << 52) >> 52), (int) ((packedPos << 26) >> 38)); + } + + /** + * @author QPCrummer + * @reason Inline + */ + @Overwrite + public static long asLong(int x, int y, int z) { + return (((long) x & (long) 67108863) << 38) | (((long) y & (long) 4095)) | (((long) z & (long) 67108863) << 12); + } + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/OptimizedChunkSecPosBitsMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/OptimizedChunkSecPosBitsMixin.java new file mode 100644 index 0000000..3515375 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/logic/fast_bits_blockpos/OptimizedChunkSecPosBitsMixin.java @@ -0,0 +1,136 @@ +package net.gensokyoreimagined.nitori.mixin.logic.fast_bits_blockpos; + +//TODO: impl this + +//import net.minecraft.core.BlockPos; +//import net.minecraft.world.level.ChunkPos; +//import net.minecraft.core.SectionPos; +//import net.minecraft.core.Vec3i; +//import org.spongepowered.asm.mixin.Mixin; +//import org.spongepowered.asm.mixin.Overwrite; +//import org.spongepowered.asm.mixin.Shadow; +// +//import java.util.stream.Stream; +// +//@Mixin(SectionPos.class) +//public abstract class OptimizedChunkSecPosBitsMixin extends Vec3i { +// @Shadow +// public static Stream betweenClosedStream(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { +// return null; +// } +// +// +// +// public OptimizedChunkSecPosBitsMixin(int x, int y, int z) { +// super(x, y, z); +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public static SectionPos of(BlockPos pos) { +// return ChunkSectionPosAccessor.invokeChunkSectionPos(pos.getX() >> 4, pos.getY() >> 4, pos.getZ() >> 4); +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public static SectionPos of(long packed) { +// return ChunkSectionPosAccessor.invokeChunkSectionPos((int) (packed >> 42), (int) (packed << 44 >> 44), (int) (packed << 22 >> 42)); +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public static long offset(long packed, int x, int y, int z) { +// return (((long) ((int) (packed >> 42) + x) & 4194303L) << 42) | (((long) ((int) (packed << 44 >> 44) + y) & 1048575L)) | (((long) ((int) (packed << 22 >> 42) + z) & 4194303L) << 20); +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public static short sectionRelativePos(BlockPos pos) { +// return (short) ((pos.getX() & 15) << 8 | (pos.getZ() & 15) << 4 | pos.getY() & 15); +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public final int minBlockX() { +// return this.getX() << 4; +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public int getMinY() { +// return this.getY() << 4; +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public int getMinZ() { +// return this.getZ() << 4; +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public static long blockToSection(long blockPos) { +// return (((long) (int) (blockPos >> 42) & 4194303L) << 42) | (((long) (int) ((blockPos << 52) >> 56) & 1048575L)) | (((long) (int) ((blockPos << 26) >> 42) & 4194303L) << 20); +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public static long asLong(int x, int y, int z) { +// return (((long) x & 4194303L) << 42) | (((long) y & 1048575L)) | (((long) z & 4194303L) << 20); +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public long asLong() { +// return (((long) getX() & 4194303L) << 42) | (((long) getY() & 1048575L)) | (((long) getZ() & 4194303L) << 20); +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public static Stream betweenClosedStream(SectionPos center, int radius) { +// return betweenClosedStream(center.getX() - radius, center.getY() - radius, center.getZ() - radius, center.getX() + radius, center.getY() + radius, center.getZ() + radius); +// } +// +// /** +// * @author QPCrummer +// * @reason Inline +// */ +// @Overwrite +// public static Stream betweenClosedStream(ChunkPos center, int radius, int minY, int maxY) { +// return betweenClosedStream(center.x - radius, 0, center.z - radius, center.x + radius, 15, center.z + radius); +// } +// +//} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/math/joml/JOMLMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/joml/JOMLMixin.java new file mode 100644 index 0000000..19149a9 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/joml/JOMLMixin.java @@ -0,0 +1,64 @@ +package net.gensokyoreimagined.nitori.mixin.math.joml; + +import org.joml.Math; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(value = Math.class, remap = false) +public class JOMLMixin { + @Redirect(method = "", at = @At(value = "FIELD", target = "Lorg/joml/Options;FASTMATH:Z")) + private static boolean redirectFastMath() { + return true; + } + + @Redirect(method = "", at = @At(value = "FIELD", target = "Lorg/joml/Options;SIN_LOOKUP:Z")) + private static boolean redirectSinLookup() { + return true; + } + + @Redirect(method = "sin(F)F", at = @At(value = "FIELD", target = "Lorg/joml/Options;FASTMATH:Z")) + private static boolean redirectFastMathLookup1() { + return true; + } + + @Redirect(method = "sin(F)F", at = @At(value = "FIELD", target = "Lorg/joml/Options;SIN_LOOKUP:Z")) + private static boolean redirectSinLookup1() { + return true; + } + + @Redirect(method = "sin(D)D", at = @At(value = "FIELD", target = "Lorg/joml/Options;FASTMATH:Z")) + private static boolean redirectFastMathLookup2() { + return true; + } + + @Redirect(method = "sin(D)D", at = @At(value = "FIELD", target = "Lorg/joml/Options;SIN_LOOKUP:Z")) + private static boolean redirectSinLookup2() { + return true; + } + + @Redirect(method = "cos(F)F", at = @At(value = "FIELD", target = "Lorg/joml/Options;FASTMATH:Z")) + private static boolean redirectFastMathLookup3() { + return true; + } + + @Redirect(method = "cos(D)D", at = @At(value = "FIELD", target = "Lorg/joml/Options;FASTMATH:Z")) + private static boolean redirectFastMathLookup4() { + return true; + } + + @Redirect(method = "cosFromSin(FF)F", at = @At(value = "FIELD", target = "Lorg/joml/Options;FASTMATH:Z")) + private static boolean redirectFastMathLookup5() { + return true; + } + + @Redirect(method = "cosFromSin(DD)D", at = @At(value = "FIELD", target = "Lorg/joml/Options;FASTMATH:Z")) + private static boolean redirectFastMathLookup6() { + return true; + } + + @Redirect(method = "atan2(DD)D", at = @At(value = "FIELD", target = "Lorg/joml/Options;FASTMATH:Z")) + private static boolean redirectFastMathLookup7() { + return true; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/math/rounding/FastRoundingVoxShapeMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/rounding/FastRoundingVoxShapeMixin.java new file mode 100644 index 0000000..8654f7a --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/math/rounding/FastRoundingVoxShapeMixin.java @@ -0,0 +1,26 @@ +package net.gensokyoreimagined.nitori.mixin.math.rounding; + +import net.gensokyoreimagined.nitori.common.math.FasterMathUtil; +import net.minecraft.world.phys.shapes.Shapes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(value = Shapes.class, priority = 1010) +public class FastRoundingVoxShapeMixin { + @Redirect( + method = "create(DDDDDD)Lnet/minecraft/world/phys/shapes/VoxelShape;", + require = 0, + at = @At(value = "INVOKE", target = "Ljava/lang/Math;round(D)J")) + private static long fasterRoundCuboid(double value) { + return FasterMathUtil.round(value); + } + + @Redirect( + method = "findBits", + require = 0, + at = @At(value = "INVOKE", target = "Ljava/lang/Math;round(D)J")) + private static long fasterRoundResolution(double value) { + return FasterMathUtil.round(value); + } +} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 97149fb..aeaf6e0 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -47,12 +47,15 @@ "math.vec.FastMathVec3DMixin", "math.general.GenericFastMathMixin", "math.intrinsic.MathHelperIntrinsicMixin", + "math.joml.JOMLMixin", + "math.rounding.FastRoundingVoxShapeMixin", "collections.entity_filtering.TypeFilterableListMixin", "collections.entity_by_type.TypeFilterableListMixin", "collections.chunk_tickets.SortedArraySetMixin", "collections.block_entity_tickers.WorldChunkMixin", "collections.goals.GoalSelectorMixin", "collections.mob_spawning.SpawnSettingsMixin", + "collections.attributes.AttributeContainerMixin", "world.block_entity_ticking.sleeping.WrappedBlockEntityTickInvokerAccessor", "world.block_entity_ticking.sleeping.campfire.CampfireBlockEntityMixin", "world.block_entity_ticking.sleeping.campfire.unlit.CampfireBlockEntityMixin", @@ -68,7 +71,6 @@ "playerwatching.MixinChunkFilter", "playerwatching.MixinServerPlayerEntity", "playerwatching.optimize_nearby_player_lookups.MixinMobEntity", - "alloc.enum_values.living_entity.LivingEntityMixin", - "chunk.palette.PaletteResizeAccessor", + "logic.fast_bits_blockpos.OptimizedBlockPosBitsMixin" ] } From eb42627111aa4e3c46ae5e9fb5c446244cdc1e58 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Thu, 11 Jul 2024 08:23:07 +0300 Subject: [PATCH 16/21] Move stuff and small refactor --- .../world/chunk/LithiumHashPalette.java | 2 +- .../spawning/ServerChunkManagerMixin.java | 29 --------- .../brain/BrainMixin.java | 2 +- .../chunk/palette/PaletteResizeAccessor.java | 2 +- .../PalettedContainerConfigurationMixin.java | 2 +- .../chunk/palette/PalettedContainerMixin.java | 2 +- .../inline_height/WorldChunkMixin.java | 2 +- .../inline_height/WorldMixin.java | 2 +- .../enum_values}/LivingEntityMixin.java | 2 +- .../block_tracking/ChunkSectionMixin.java | 3 +- src/main/resources/mixins.core.json | 62 +++++++++---------- 11 files changed, 40 insertions(+), 70 deletions(-) delete mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/experimental/spawning/ServerChunkManagerMixin.java rename src/main/java/net/gensokyoreimagined/nitori/mixin/{collections => needs_testing}/brain/BrainMixin.java (95%) rename src/main/java/net/gensokyoreimagined/nitori/mixin/{ => needs_testing}/chunk/palette/PaletteResizeAccessor.java (79%) rename src/main/java/net/gensokyoreimagined/nitori/mixin/{ => needs_testing}/chunk/palette/PalettedContainerConfigurationMixin.java (92%) rename src/main/java/net/gensokyoreimagined/nitori/mixin/{ => needs_testing}/chunk/palette/PalettedContainerMixin.java (98%) rename src/main/java/net/gensokyoreimagined/nitori/mixin/{world => needs_testing}/inline_height/WorldChunkMixin.java (95%) rename src/main/java/net/gensokyoreimagined/nitori/mixin/{world => needs_testing}/inline_height/WorldMixin.java (97%) rename src/main/java/net/gensokyoreimagined/nitori/mixin/{alloc/enum_values/living_entity => needs_testing/living_entity/enum_values}/LivingEntityMixin.java (89%) rename src/main/java/net/gensokyoreimagined/nitori/mixin/{ => needs_testing}/util/block_tracking/ChunkSectionMixin.java (98%) diff --git a/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java b/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java index 2edeaac..c8c89ad 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java +++ b/src/main/java/net/gensokyoreimagined/nitori/common/world/chunk/LithiumHashPalette.java @@ -4,7 +4,7 @@ import com.google.common.collect.ImmutableList; import it.unimi.dsi.fastutil.HashCommon; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; -import net.gensokyoreimagined.nitori.mixin.chunk.palette.PaletteResizeAccessor; +import net.gensokyoreimagined.nitori.mixin.needs_testing.chunk.palette.PaletteResizeAccessor; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.VarInt; import net.minecraft.core.IdMap; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/experimental/spawning/ServerChunkManagerMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/experimental/spawning/ServerChunkManagerMixin.java deleted file mode 100644 index 51c7f82..0000000 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/experimental/spawning/ServerChunkManagerMixin.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.gensokyoreimagined.nitori.mixin.experimental.spawning; - -//import net.gensokyoreimagined.nitori.common.world.ChunkAwareEntityIterable; -//import net.gensokyoreimagined.nitori.mixin.util.accessors.ServerEntityManagerAccessor; -//import net.gensokyoreimagined.nitori.mixin.util.accessors.ServerWorldAccessor; -//import net.minecraft.server.level.ServerChunkCache; -//import net.minecraft.server.level.ServerLevel; -//import net.minecraft.world.entity.Entity; -//import org.spongepowered.asm.mixin.Mixin; -//import org.spongepowered.asm.mixin.injection.At; -//import org.spongepowered.asm.mixin.injection.Redirect; -// -//@Mixin(ServerChunkCache.class) -//public class ServerChunkManagerMixin { -// -// @Redirect( -// method = "tickChunks", -// at = @At( -// value = "INVOKE", -// target = "Lnet/minecraft/server/level/ServerLevel;getAllEntities()Ljava/lang/Iterable;" -// ) -// ) -// private Iterable iterateEntitiesChunkAware(ServerLevel serverLevel) { -// //noinspection unchecked -// return ((ChunkAwareEntityIterable) ((ServerEntityManagerAccessor) ((ServerWorldAccessor) serverLevel).getEntityManager()).getSectionStorage()).lithium$IterateEntitiesInTrackedSections(); -// } -//} - -//PersistentEntitySectionManager does not work on paper \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/brain/BrainMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/brain/BrainMixin.java similarity index 95% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/collections/brain/BrainMixin.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/brain/BrainMixin.java index 614d6ef..f650d18 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/collections/brain/BrainMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/brain/BrainMixin.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.collections.brain; +package net.gensokyoreimagined.nitori.mixin.needs_testing.brain; import com.google.common.collect.ImmutableList; import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PaletteResizeAccessor.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/chunk/palette/PaletteResizeAccessor.java similarity index 79% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PaletteResizeAccessor.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/chunk/palette/PaletteResizeAccessor.java index b84feb3..12e075f 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PaletteResizeAccessor.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/chunk/palette/PaletteResizeAccessor.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.chunk.palette; +package net.gensokyoreimagined.nitori.mixin.needs_testing.chunk.palette; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerConfigurationMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/chunk/palette/PalettedContainerConfigurationMixin.java similarity index 92% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerConfigurationMixin.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/chunk/palette/PalettedContainerConfigurationMixin.java index 9076a67..9446cbb 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerConfigurationMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/chunk/palette/PalettedContainerConfigurationMixin.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.chunk.palette; +package net.gensokyoreimagined.nitori.mixin.needs_testing.chunk.palette; // import net.minecraft.core.IdMap; // import net.minecraft.world.level.chunk.Palette; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/chunk/palette/PalettedContainerMixin.java similarity index 98% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/chunk/palette/PalettedContainerMixin.java index 409e5b9..b895c3e 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/chunk/palette/PalettedContainerMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/chunk/palette/PalettedContainerMixin.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.chunk.palette; +package net.gensokyoreimagined.nitori.mixin.needs_testing.chunk.palette; // import net.gensokyoreimagined.nitori.common.world.chunk.LithiumHashPalette; // import net.minecraft.core.IdMap; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/inline_height/WorldChunkMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/inline_height/WorldChunkMixin.java similarity index 95% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/world/inline_height/WorldChunkMixin.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/inline_height/WorldChunkMixin.java index b4c20f9..9f887dc 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/inline_height/WorldChunkMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/inline_height/WorldChunkMixin.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.world.inline_height; +package net.gensokyoreimagined.nitori.mixin.needs_testing.inline_height; import net.minecraft.core.BlockPos; import net.minecraft.world.level.LevelHeightAccessor; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/inline_height/WorldMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/inline_height/WorldMixin.java similarity index 97% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/world/inline_height/WorldMixin.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/inline_height/WorldMixin.java index 3e010f7..c748344 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/inline_height/WorldMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/inline_height/WorldMixin.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.world.inline_height; +package net.gensokyoreimagined.nitori.mixin.needs_testing.inline_height; //import net.minecraft.core.RegistryAccess; //import net.minecraft.resources.ResourceKey; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/living_entity/enum_values/LivingEntityMixin.java similarity index 89% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/living_entity/enum_values/LivingEntityMixin.java index fce653b..570d7d6 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/enum_values/living_entity/LivingEntityMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/living_entity/enum_values/LivingEntityMixin.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.alloc.enum_values.living_entity; +package net.gensokyoreimagined.nitori.mixin.needs_testing.living_entity.enum_values; import net.gensokyoreimagined.nitori.common.util.EquipmentSlotConstants; import net.minecraft.world.entity.EquipmentSlot; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/util/block_tracking/ChunkSectionMixin.java similarity index 98% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/util/block_tracking/ChunkSectionMixin.java index 22d90ef..b38c251 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/block_tracking/ChunkSectionMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/needs_testing/util/block_tracking/ChunkSectionMixin.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.util.block_tracking; +package net.gensokyoreimagined.nitori.mixin.needs_testing.util.block_tracking; import net.gensokyoreimagined.nitori.common.block.*; import net.gensokyoreimagined.nitori.common.entity.block_tracking.ChunkSectionChangeCallback; @@ -9,7 +9,6 @@ import net.minecraft.core.SectionPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.PalettedContainer; -import net.minecraft.world.level.material.FluidState; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index aeaf6e0..78acb17 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -8,7 +8,6 @@ "server": [ "ChunkMapMixin", "ChunkMapMixin$TrackedEntity", - "math.fast_util.MixinAABB", "MixinBlock", "MixinBlockPos", "MixinChunkEntitySlices", @@ -19,7 +18,6 @@ "MixinIteratorSafeOrderedReferenceSet", "MixinLevelStorageAccess", "MixinMob", - "math.sine_lut.MixinMth", "MixinNoiseBasedChunkGenerator", "MixinPlayer", "MixinPlayerList", @@ -28,49 +26,51 @@ "MixinServerEntity", "MixinSpongeSIMD", "MixinWorldGenRegion", - "alloc.chunk_ticking.ServerChunkManagerMixin", "alloc.blockstate.StateMixin", - "alloc.composter.ComposterMixin$ComposterBlockFullComposterInventoryMixin", - "alloc.composter.ComposterMixin$ComposterBlockDummyInventoryMixin", + "alloc.chunk_ticking.ServerChunkManagerMixin", "alloc.composter.ComposterMixin$ComposterBlockComposterInventoryMixin", - "util.MixinLevelBlockEntityRetrieval", + "alloc.composter.ComposterMixin$ComposterBlockDummyInventoryMixin", + "alloc.composter.ComposterMixin$ComposterBlockFullComposterInventoryMixin", "cached_hashcode.BlockNeighborGroupMixin", - "shapes.blockstate_cache.BlockMixin", - "shapes.lazy_shape_context.EntityShapeContextMixin", - "entity.fast_retrieval.SectionedEntityCacheMixin", + "collections.attributes.AttributeContainerMixin", + "collections.block_entity_tickers.WorldChunkMixin", + "collections.chunk_tickets.SortedArraySetMixin", + "collections.entity_by_type.TypeFilterableListMixin", + "collections.entity_filtering.TypeFilterableListMixin", + "collections.goals.GoalSelectorMixin", + "collections.mob_spawning.SpawnSettingsMixin", "entity.fast_hand_swing.LivingEntityMixin", - "math.fast_blockops.DirectionMixin", + "entity.fast_retrieval.SectionedEntityCacheMixin", + "logic.fast_bits_blockpos.OptimizedBlockPosBitsMixin", "math.fast_blockops.BlockPosMixin", - "math.fast_util.AxisCycleDirectionMixin$ForwardMixin", + "math.fast_blockops.DirectionMixin", "math.fast_util.AxisCycleDirectionMixin$BackwardMixin", + "math.fast_util.AxisCycleDirectionMixin$ForwardMixin", "math.fast_util.DirectionMixin", - "math.vec.FastMathVec3DMixin", + "math.fast_util.MixinAABB", "math.general.GenericFastMathMixin", "math.intrinsic.MathHelperIntrinsicMixin", "math.joml.JOMLMixin", "math.rounding.FastRoundingVoxShapeMixin", - "collections.entity_filtering.TypeFilterableListMixin", - "collections.entity_by_type.TypeFilterableListMixin", - "collections.chunk_tickets.SortedArraySetMixin", - "collections.block_entity_tickers.WorldChunkMixin", - "collections.goals.GoalSelectorMixin", - "collections.mob_spawning.SpawnSettingsMixin", - "collections.attributes.AttributeContainerMixin", - "world.block_entity_ticking.sleeping.WrappedBlockEntityTickInvokerAccessor", - "world.block_entity_ticking.sleeping.campfire.CampfireBlockEntityMixin", - "world.block_entity_ticking.sleeping.campfire.unlit.CampfireBlockEntityMixin", - "world.block_entity_ticking.sleeping.campfire.lit.CampfireBlockEntityMixin", - "world.block_entity_ticking.sleeping.furnace.AbstractFurnaceBlockEntityMixin", - "world.block_entity_ticking.support_cache.DirectBlockEntityTickInvokerMixin", - "world.block_entity_ticking.support_cache.BlockEntityMixin", - "world.block_entity_ticking.support_cache.WorldChunkMixin", + "math.sine_lut.MixinMth", + "math.vec.FastMathVec3DMixin", + "playerwatching.MixinChunkFilter", + "playerwatching.MixinServerPlayerEntity", + "playerwatching.optimize_nearby_player_lookups.MixinMobEntity", + "shapes.blockstate_cache.BlockMixin", + "shapes.lazy_shape_context.EntityShapeContextMixin", + "util.MixinLevelBlockEntityRetrieval", "util.accessors.ClientEntityManagerAccessor", "util.accessors.EntityTrackingSectionAccessor", "util.accessors.ServerEntityManagerAccessor", "util.block_tracking.AbstractBlockStateMixin", - "playerwatching.MixinChunkFilter", - "playerwatching.MixinServerPlayerEntity", - "playerwatching.optimize_nearby_player_lookups.MixinMobEntity", - "logic.fast_bits_blockpos.OptimizedBlockPosBitsMixin" + "world.block_entity_ticking.sleeping.WrappedBlockEntityTickInvokerAccessor", + "world.block_entity_ticking.sleeping.campfire.CampfireBlockEntityMixin", + "world.block_entity_ticking.sleeping.campfire.lit.CampfireBlockEntityMixin", + "world.block_entity_ticking.sleeping.campfire.unlit.CampfireBlockEntityMixin", + "world.block_entity_ticking.sleeping.furnace.AbstractFurnaceBlockEntityMixin", + "world.block_entity_ticking.support_cache.BlockEntityMixin", + "world.block_entity_ticking.support_cache.DirectBlockEntityTickInvokerMixin", + "world.block_entity_ticking.support_cache.WorldChunkMixin" ] } From 330a563d22844f2203fcffc960dea38a6f8a846c Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 12 Jul 2024 01:28:46 +0300 Subject: [PATCH 17/21] NoFallDamage check at ground --- .../entity/fall_damage/NoFallDamageMixin.java | 31 +++++++++++++++++++ src/main/resources/mixins.core.json | 1 + 2 files changed, 32 insertions(+) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fall_damage/NoFallDamageMixin.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fall_damage/NoFallDamageMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fall_damage/NoFallDamageMixin.java new file mode 100644 index 0000000..0ccf756 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/fall_damage/NoFallDamageMixin.java @@ -0,0 +1,31 @@ +package net.gensokyoreimagined.nitori.mixin.entity.fall_damage; + +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.core.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Entity.class) +public abstract class NoFallDamageMixin { + + @Shadow public abstract boolean onGround(); + + @Shadow public abstract void resetFallDistance(); + + + @Inject(method = "checkFallDamage", at = @At("HEAD"), cancellable = true) + private void cancelFallDamage(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition, CallbackInfo ci) { + if (!(((Entity)(Object)this) instanceof LivingEntity)) { + if (this.onGround()) { + this.resetFallDistance(); + } + ci.cancel(); + } + } + +} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 78acb17..aa2e35c 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -41,6 +41,7 @@ "collections.mob_spawning.SpawnSettingsMixin", "entity.fast_hand_swing.LivingEntityMixin", "entity.fast_retrieval.SectionedEntityCacheMixin", + "entity.fall_damage.NoFallDamageMixin", "logic.fast_bits_blockpos.OptimizedBlockPosBitsMixin", "math.fast_blockops.BlockPosMixin", "math.fast_blockops.DirectionMixin", From 8a772889b5d68340aa42e1d98ec56634f994ccbf Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 12 Jul 2024 04:28:17 +0300 Subject: [PATCH 18/21] Inline VarInts, Improve Running particles --- .../sprinting_particles/EntityMixin.java | 26 ++++++++ .../mixin/network/microopt/VarIntsMixin.java | 61 +++++++++++++++++++ .../SimpleVoxelShapeMixin.java | 38 ++++++++++++ .../nitori/mixin/util/network/VarIntUtil.java | 20 ++++++ src/main/resources/mixins.core.json | 2 + 5 files changed, 147 insertions(+) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/entity/sprinting_particles/EntityMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/VarIntsMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/SimpleVoxelShapeMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/util/network/VarIntUtil.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/sprinting_particles/EntityMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/sprinting_particles/EntityMixin.java new file mode 100644 index 0000000..0d79adb --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/sprinting_particles/EntityMixin.java @@ -0,0 +1,26 @@ +package net.gensokyoreimagined.nitori.mixin.entity.sprinting_particles; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +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.Redirect; + +@Mixin(Entity.class) +public abstract class EntityMixin { + + @Shadow + public abstract Level level(); + + @Redirect( + method = "baseTick", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;canSpawnSprintParticle()Z") + ) + private boolean skipParticlesOnServerSide(Entity instance) { + if (instance.level().isClientSide()) { + return instance.canSpawnSprintParticle(); + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/VarIntsMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/VarIntsMixin.java new file mode 100644 index 0000000..eebd332 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/VarIntsMixin.java @@ -0,0 +1,61 @@ +package net.gensokyoreimagined.nitori.mixin.network.microopt; + +import io.netty.buffer.ByteBuf; +import net.gensokyoreimagined.nitori.mixin.util.network.VarIntUtil; +import net.minecraft.network.VarInt; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@Mixin(VarInt.class) +public class VarIntsMixin { + /** + * @author Andrew Steinborn + * @reason optimized version + */ + @Overwrite + public static int getByteSize(int v) { + return VarIntUtil.getVarIntLength(v); + } + + /** + * @author Andrew Steinborn + * @reason optimized version + */ + @Overwrite + public static ByteBuf write(ByteBuf buf, int value) { + // Peel the one and two byte count cases explicitly as they are the most common VarInt sizes + // that the server will send, to improve inlining. + if ((value & (0xFFFFFFFF << 7)) == 0) { + buf.writeByte(value); + } else if ((value & (0xFFFFFFFF << 14)) == 0) { + int w = (value & 0x7F | 0x80) << 8 | (value >>> 7); + buf.writeShort(w); + } else { + writeOld(buf, value); + } + + return buf; + } + + private static void writeOld(ByteBuf buf, int value) { + // See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/ + if ((value & (0xFFFFFFFF << 7)) == 0) { + buf.writeByte(value); + } else if ((value & (0xFFFFFFFF << 14)) == 0) { + int w = (value & 0x7F | 0x80) << 8 | (value >>> 7); + buf.writeShort(w); + } else if ((value & (0xFFFFFFFF << 21)) == 0) { + int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14); + buf.writeMedium(w); + } else if ((value & (0xFFFFFFFF << 28)) == 0) { + int w = (value & 0x7F | 0x80) << 24 | (((value >>> 7) & 0x7F | 0x80) << 16) + | ((value >>> 14) & 0x7F | 0x80) << 8 | (value >>> 21); + buf.writeInt(w); + } else { + int w = (value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16 + | ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80); + buf.writeInt(w); + buf.writeByte(value >>> 28); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/SimpleVoxelShapeMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/SimpleVoxelShapeMixin.java new file mode 100644 index 0000000..d940d94 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/SimpleVoxelShapeMixin.java @@ -0,0 +1,38 @@ +package net.gensokyoreimagined.nitori.mixin.shapes.precompute_shape_arrays; + +//import it.unimi.dsi.fastutil.doubles.DoubleList; +//import net.minecraft.core.Direction; +//import net.minecraft.world.phys.shapes.CubePointRange; +//import net.minecraft.world.phys.shapes.CubeVoxelShape; +//import net.minecraft.world.phys.shapes.DiscreteVoxelShape; +//import org.spongepowered.asm.mixin.Mixin; +//import org.spongepowered.asm.mixin.Overwrite; +//import org.spongepowered.asm.mixin.injection.At; +//import org.spongepowered.asm.mixin.injection.Inject; +//import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +// +//@Mixin(CubeVoxelShape.class) +//public class SimpleVoxelShapeMixin { +// private static final Direction.Axis[] AXIS = Direction.Axis.values(); +// +// private DoubleList[] parts; +// +// @Inject(method = "", at = @At("RETURN")) +// private void CubeVoxelShape(DiscreteVoxelShape voxels, CallbackInfo ci) { +// this.parts = new DoubleList[AXIS.length]; +// +// for (Direction.Axis axis : AXIS) { +// this.parts[axis.ordinal()] = new CubePointRange(voxels.getSize(axis)); +// } +// } +// +// /** +// * @author JellySquid +// * @reason Use the cached array +// */ +// @Overwrite +// public DoubleList getCoords(Direction.Axis axis) { +// return this.parts[axis.ordinal()]; +// } +// +//} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/util/network/VarIntUtil.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/network/VarIntUtil.java new file mode 100644 index 0000000..88490da --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/util/network/VarIntUtil.java @@ -0,0 +1,20 @@ +package net.gensokyoreimagined.nitori.mixin.util.network; + +/** + * Maps VarInt byte sizes to a lookup table corresponding to the number of bits in the integer, + * from zero to 32. + */ +public class VarIntUtil { + private static final int[] VARINT_EXACT_BYTE_LENGTHS = new int[33]; + + static { + for (int i = 0; i <= 32; ++i) { + VARINT_EXACT_BYTE_LENGTHS[i] = (int) Math.ceil((31d - (i - 1)) / 7d); + } + VARINT_EXACT_BYTE_LENGTHS[32] = 1; // Special case for 0. + } + + public static int getVarIntLength(int value) { + return VARINT_EXACT_BYTE_LENGTHS[Integer.numberOfLeadingZeros(value)]; + } +} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index aa2e35c..b0d60b8 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -42,6 +42,7 @@ "entity.fast_hand_swing.LivingEntityMixin", "entity.fast_retrieval.SectionedEntityCacheMixin", "entity.fall_damage.NoFallDamageMixin", + "entity.sprinting_particles.EntityMixin", "logic.fast_bits_blockpos.OptimizedBlockPosBitsMixin", "math.fast_blockops.BlockPosMixin", "math.fast_blockops.DirectionMixin", @@ -55,6 +56,7 @@ "math.rounding.FastRoundingVoxShapeMixin", "math.sine_lut.MixinMth", "math.vec.FastMathVec3DMixin", + "network.microopt.VarIntsMixin", "playerwatching.MixinChunkFilter", "playerwatching.MixinServerPlayerEntity", "playerwatching.optimize_nearby_player_lookups.MixinMobEntity", From a98e77aade5b7646dea422bd221d1fcda1ea3ac7 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 12 Jul 2024 05:00:11 +0300 Subject: [PATCH 19/21] W.I.P Enum Redirector, Improve Encoding --- .../network/microopt/StringEncodingMixin.java | 34 +++++ .../redirector/RedirectorTransformer.java | 124 ++++++++++++++++++ src/main/resources/mixins.core.json | 1 + 3 files changed, 159 insertions(+) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/StringEncodingMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/redirector/RedirectorTransformer.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/StringEncodingMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/StringEncodingMixin.java new file mode 100644 index 0000000..130ba69 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/StringEncodingMixin.java @@ -0,0 +1,34 @@ +package net.gensokyoreimagined.nitori.mixin.network.microopt; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.handler.codec.EncoderException; +import net.minecraft.network.Utf8String; +import net.minecraft.network.VarInt; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.nio.charset.StandardCharsets; + +@Mixin(Utf8String.class) +public class StringEncodingMixin { + /** + * @author Andrew Steinborn + * @reason optimized version + */ + @Overwrite + public static void write(ByteBuf buf, CharSequence string, int length) { + // Mojang almost gets it right, but stumbles at the finish line... + if (string.length() > length) { + throw new EncoderException("String too big (was " + string.length() + " characters, max " + length + ")"); + } + int utf8Bytes = ByteBufUtil.utf8Bytes(string); + int maxBytesPermitted = ByteBufUtil.utf8MaxBytes(length); + if (utf8Bytes > maxBytesPermitted) { + throw new EncoderException("String too big (was " + utf8Bytes + " bytes encoded, max " + maxBytesPermitted + ")"); + } else { + VarInt.write(buf, utf8Bytes); + buf.writeCharSequence(string, StandardCharsets.UTF_8); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/redirector/RedirectorTransformer.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/redirector/RedirectorTransformer.java new file mode 100644 index 0000000..3ae1e2c --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/redirector/RedirectorTransformer.java @@ -0,0 +1,124 @@ +package net.gensokyoreimagined.nitori.mixin.redirector; + +// https://github.com/MCTeamPotato/Redirector/issues/9 + + + +//import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +//import org.jetbrains.annotations.Contract; +//import org.jetbrains.annotations.NotNull; +//import org.objectweb.asm.ClassReader; +//import org.objectweb.asm.ClassWriter; +//import org.objectweb.asm.Opcodes; +//import org.objectweb.asm.tree.AbstractInsnNode; +//import org.objectweb.asm.tree.ClassNode; +//import org.objectweb.asm.tree.InsnList; +//import org.objectweb.asm.tree.MethodNode; +// +//import java.lang.instrument.ClassFileTransformer; +//import java.security.ProtectionDomain; +//import java.util.ListIterator; +//import java.util.Map; +// +//public class RedirectorTransformer implements ClassFileTransformer { +// public static @NotNull String getSuperClass(byte[] clazz) { +// Map utfMap = new Int2IntOpenHashMap(); +// Map classMap = new Int2IntOpenHashMap(); +// int constantsCount = readUnsignedShort(clazz, 8); +// int passcount = 10; +// for (int i = 1; i < constantsCount; i++) { +// int size; +// switch (clazz[passcount]) { +// case 9: +// case 10: +// case 11: +// case 3: +// case 4: +// case 12: +// case 18: +// size = 5; +// break; +// case 5: +// case 6: +// size = 9; +// break; +// case 1://UTF8 +// int UTFSize = readUnsignedShort(clazz, passcount + 1); +// size = 3 + UTFSize; +// utfMap.put(i, passcount); +// break; +// case 15: +// size = 4; +// break; +// case 7://class +// size = 3; +// int index = readUnsignedShort(clazz, passcount + 1); +// classMap.put(i, index); +// break; +// default: +// size = 3; +// break; +// } +// passcount += size; +// +// } +// passcount += 4; +// passcount = readUnsignedShort(clazz, passcount); +// passcount = classMap.get(passcount); +// passcount = utfMap.get(passcount); +// int UTFSize = readUnsignedShort(clazz, passcount + 1); +// return readUTF8(clazz, passcount + 3, UTFSize); +// } +// +// @Contract(pure = true) +// public static int readUnsignedShort(byte @NotNull [] b, int index) { +// return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); +// } +// +// @Contract(value = "_, _, _ -> new", pure = true) +// public static @NotNull String readUTF8(byte[] b, int index, int length) { +// char[] str = new char[length]; +// for (int i = 0; i < length; i++) { +// str[i] = (char) b[i + index]; +// } +// return new String(str); +// } +// +// @Override +// public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] basicClass) { +// try { +// String superClass = getSuperClass(basicClass); +// if (!"java/lang/Enum".equals(superClass)) return basicClass; +// ClassReader classReader = new ClassReader(basicClass); +// if ("java/lang/Enum".equals(classReader.getSuperName())) { +// ClassNode cn = new ClassNode(); +// classReader.accept(cn, 0); +// for (MethodNode mn : cn.methods) { +// if ("values".equals(mn.name) && mn.desc.contains("()")) { +// InsnList il = mn.instructions; +// ListIterator iterator = il.iterator(); +// AbstractInsnNode n1 = null; +// AbstractInsnNode n2 = null; +// while (iterator.hasNext()) { +// AbstractInsnNode note = iterator.next(); +// if (Opcodes.GETSTATIC == note.getOpcode()) { +// n1 = note; +// } else if (Opcodes.ARETURN == note.getOpcode()) { +// n2 = note; +// } +// } +// il.clear(); +// il.add(n1); +// il.add(n2); +// } +// } +// ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); +// cn.accept(classWriter); +// return classWriter.toByteArray(); +// } +// } catch (Exception e) { +// return basicClass; +// } +// return basicClass; +// } +//} \ No newline at end of file diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index b0d60b8..23bb71e 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -57,6 +57,7 @@ "math.sine_lut.MixinMth", "math.vec.FastMathVec3DMixin", "network.microopt.VarIntsMixin", + "network.microopt.StringEncodingMixin", "playerwatching.MixinChunkFilter", "playerwatching.MixinServerPlayerEntity", "playerwatching.optimize_nearby_player_lookups.MixinMobEntity", From dabb5dd1592993d50452ca295437ef8037d17484 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Fri, 12 Jul 2024 08:21:07 +0300 Subject: [PATCH 20/21] Faster Filtering, Faster Voxel Shapes --- .../access/IThreadedAnvilChunkStorage.java | 34 ++++ .../nitori/access/ITypeFilterableList.java | 8 + .../FractionalDoubleListMixin.java | 33 ++++ .../SimpleVoxelShapeMixin.java | 92 ++++++----- .../specialized_shapes/VoxelShapeMixin.java | 151 ++++++++++++++++++ .../specialized_shapes/VoxelShapesMixin.java | 113 +++++++++++++ .../move_zero_velocity/MixinEntity.java | 34 ++++ .../collections/MixinTypeFilterableList.java | 76 +++++++++ .../playerwatching/MixinChunkFilter.java | 2 +- .../MixinServerPlayerEntity.java | 2 +- .../MixinMobEntity.java | 2 +- .../DisablePortalChecksMixin.java | 23 +++ src/main/resources/mixins.core.json | 14 +- 13 files changed, 541 insertions(+), 43 deletions(-) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/access/IThreadedAnvilChunkStorage.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/access/ITypeFilterableList.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/FractionalDoubleListMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/specialized_shapes/VoxelShapeMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/specialized_shapes/VoxelShapesMixin.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/entity/move_zero_velocity/MixinEntity.java create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/general/collections/MixinTypeFilterableList.java rename src/main/java/net/gensokyoreimagined/nitori/mixin/{ => vmp}/playerwatching/MixinChunkFilter.java (91%) rename src/main/java/net/gensokyoreimagined/nitori/mixin/{ => vmp}/playerwatching/MixinServerPlayerEntity.java (95%) rename src/main/java/net/gensokyoreimagined/nitori/mixin/{ => vmp}/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java (93%) create mode 100644 src/main/java/net/gensokyoreimagined/nitori/mixin/world/portal_checks/DisablePortalChecksMixin.java diff --git a/src/main/java/net/gensokyoreimagined/nitori/access/IThreadedAnvilChunkStorage.java b/src/main/java/net/gensokyoreimagined/nitori/access/IThreadedAnvilChunkStorage.java new file mode 100644 index 0000000..7264911 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/access/IThreadedAnvilChunkStorage.java @@ -0,0 +1,34 @@ +package net.gensokyoreimagined.nitori.access; + +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.PlayerMap; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.util.thread.BlockableEventLoop; +import org.apache.commons.lang3.mutable.MutableObject; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(ServerChunkCache.class) +public interface IThreadedAnvilChunkStorage { + + @Invoker + ChunkHolder invokeGetCurrentChunkHolder(long pos); + + @Invoker + ChunkHolder invokeGetChunkHolder(long pos); + + @Accessor + ServerLevel getLevel(); + + @Accessor + PlayerMap getPlayerChunkWatchingManager(); + + @Accessor + BlockableEventLoop getMainThreadExecutor(); + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/access/ITypeFilterableList.java b/src/main/java/net/gensokyoreimagined/nitori/access/ITypeFilterableList.java new file mode 100644 index 0000000..57583d2 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/access/ITypeFilterableList.java @@ -0,0 +1,8 @@ +package net.gensokyoreimagined.nitori.access; + +public interface ITypeFilterableList { + + Object[] getBackingArray(); + + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/FractionalDoubleListMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/FractionalDoubleListMixin.java new file mode 100644 index 0000000..d24dfa9 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/FractionalDoubleListMixin.java @@ -0,0 +1,33 @@ +package net.gensokyoreimagined.nitori.mixin.shapes.precompute_shape_arrays; + +import net.minecraft.world.phys.shapes.CubePointRange; +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.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(CubePointRange.class) +public class FractionalDoubleListMixin { + @Shadow + @Final + private int parts; + + private double scale; + + @Inject(method = "(I)V", at = @At("RETURN")) + public void initScale(int sectionCount, CallbackInfo ci) { + this.scale = 1.0D / this.parts; + } + + /** + * @author JellySquid + * @reason Replace division with multiplication + */ + @Overwrite + public double getDouble(int position) { + return position * this.scale; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/SimpleVoxelShapeMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/SimpleVoxelShapeMixin.java index d940d94..72471be 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/SimpleVoxelShapeMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/precompute_shape_arrays/SimpleVoxelShapeMixin.java @@ -1,38 +1,58 @@ package net.gensokyoreimagined.nitori.mixin.shapes.precompute_shape_arrays; -//import it.unimi.dsi.fastutil.doubles.DoubleList; -//import net.minecraft.core.Direction; -//import net.minecraft.world.phys.shapes.CubePointRange; -//import net.minecraft.world.phys.shapes.CubeVoxelShape; -//import net.minecraft.world.phys.shapes.DiscreteVoxelShape; -//import org.spongepowered.asm.mixin.Mixin; -//import org.spongepowered.asm.mixin.Overwrite; -//import org.spongepowered.asm.mixin.injection.At; -//import org.spongepowered.asm.mixin.injection.Inject; -//import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -// -//@Mixin(CubeVoxelShape.class) -//public class SimpleVoxelShapeMixin { -// private static final Direction.Axis[] AXIS = Direction.Axis.values(); -// -// private DoubleList[] parts; -// -// @Inject(method = "", at = @At("RETURN")) -// private void CubeVoxelShape(DiscreteVoxelShape voxels, CallbackInfo ci) { -// this.parts = new DoubleList[AXIS.length]; -// -// for (Direction.Axis axis : AXIS) { -// this.parts[axis.ordinal()] = new CubePointRange(voxels.getSize(axis)); -// } -// } -// -// /** -// * @author JellySquid -// * @reason Use the cached array -// */ -// @Overwrite -// public DoubleList getCoords(Direction.Axis axis) { -// return this.parts[axis.ordinal()]; -// } -// -//} \ No newline at end of file +import it.unimi.dsi.fastutil.doubles.DoubleList; +import net.minecraft.core.Direction; +import net.minecraft.world.phys.shapes.CubePointRange; +import net.minecraft.world.phys.shapes.CubeVoxelShape; +import net.minecraft.world.phys.shapes.DiscreteVoxelShape; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +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.callback.CallbackInfo; + +import java.lang.reflect.Constructor; + +@Mixin(CubeVoxelShape.class) +public class SimpleVoxelShapeMixin { + private static final Direction.Axis[] AXIS = Direction.Axis.values(); + + private DoubleList[] list; + + @Unique + private static Constructor nitori$cubePointRangeConstructor; + + @Unique + private CubePointRange nitori$cubePointRange(int sectionCount) { + try { + if (nitori$cubePointRangeConstructor == null) { + nitori$cubePointRangeConstructor = CubePointRange.class.getDeclaredConstructor(int.class); + nitori$cubePointRangeConstructor.setAccessible(true); + } + + return nitori$cubePointRangeConstructor.newInstance(sectionCount); + } catch (Exception ex) { + throw new AssertionError("Failed to find CubePointRange constructor - " + ex.getMessage(), ex); + } + } + + @Inject(method = "", at = @At("RETURN")) + private void onConstructed(DiscreteVoxelShape voxels, CallbackInfo ci) { + this.list = new DoubleList[AXIS.length]; + + for (Direction.Axis axis : AXIS) { + this.list[axis.ordinal()] = nitori$cubePointRange(voxels.getSize(axis)); + } + } + + /** + * @author JellySquid + * @reason Use the cached array + */ + @Overwrite + public DoubleList getCoords(Direction.Axis axis) { + return this.list[axis.ordinal()]; + } + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/specialized_shapes/VoxelShapeMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/specialized_shapes/VoxelShapeMixin.java new file mode 100644 index 0000000..e4d726a --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/specialized_shapes/VoxelShapeMixin.java @@ -0,0 +1,151 @@ +package net.gensokyoreimagined.nitori.mixin.shapes.specialized_shapes; + +import it.unimi.dsi.fastutil.doubles.DoubleList; +import net.minecraft.core.AxisCycle; +import net.minecraft.world.phys.AABB; +import net.minecraft.core.Direction; +import net.minecraft.world.phys.shapes.DiscreteVoxelShape; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +/** + * Implement faster methods for determining penetration during collision resolution. + */ +@Mixin(VoxelShape.class) +public abstract class VoxelShapeMixin { + private static final double POSITIVE_EPSILON = +1.0E-7D; + private static final double NEGATIVE_EPSILON = -1.0E-7D; + + @Shadow + @Final + public DiscreteVoxelShape shape; + + @Shadow + public abstract boolean isEmpty(); + + @Shadow + protected abstract double get(Direction.Axis axis, int index); + + @Shadow + public abstract DoubleList getCoords(Direction.Axis axis); + + /** + * @reason Use optimized implementation which delays searching for coordinates as long as possible + * @author JellySquid + */ + @Overwrite + public double collideX(AxisCycle cycleDirection, AABB box, double maxDist) { + if (this.isEmpty()) { + return maxDist; + } + + if (Math.abs(maxDist) < POSITIVE_EPSILON) { + return 0.0D; + } + + AxisCycle cycle = cycleDirection.inverse(); + + Direction.Axis axisX = cycle.cycle(Direction.Axis.X); + Direction.Axis axisY = cycle.cycle(Direction.Axis.Y); + Direction.Axis axisZ = cycle.cycle(Direction.Axis.Z); + + int minY = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + int minZ = Integer.MIN_VALUE; + int maxZ = Integer.MIN_VALUE; + + int x, y, z; + + double dist; + + if (maxDist > 0.0D) { + double max = box.max(axisX); + int maxIdx = this.findIndex(axisX, max - POSITIVE_EPSILON); + + int maxX = this.shape.getSize(axisX); + + for (x = maxIdx + 1; x < maxX; ++x) { + minY = minY == Integer.MIN_VALUE ? Math.max(0, this.findIndex(axisY, box.min(axisY) + POSITIVE_EPSILON)) : minY; + maxY = maxY == Integer.MIN_VALUE ? Math.min(this.shape.getSize(axisY), this.findIndex(axisY, box.max(axisY) - POSITIVE_EPSILON) + 1) : maxY; + + for (y = minY; y < maxY; ++y) { + minZ = minZ == Integer.MIN_VALUE ? Math.max(0, this.findIndex(axisZ, box.min(axisZ) + POSITIVE_EPSILON)) : minZ; + maxZ = maxZ == Integer.MIN_VALUE ? Math.min(this.shape.getSize(axisZ), this.findIndex(axisZ, box.max(axisZ) - POSITIVE_EPSILON) + 1) : maxZ; + + for (z = minZ; z < maxZ; ++z) { + if (this.shape.isFullWide(cycle, x, y, z)) { + dist = this.get(axisX, x) - max; + + if (dist >= NEGATIVE_EPSILON) { + maxDist = Math.min(maxDist, dist); + } + + return maxDist; + } + } + } + } + } else if (maxDist < 0.0D) { + double min = box.min(axisX); + int minIdx = this.findIndex(axisX, min + POSITIVE_EPSILON); + + for (x = minIdx - 1; x >= 0; --x) { + minY = minY == Integer.MIN_VALUE ? Math.max(0, this.findIndex(axisY, box.min(axisY) + POSITIVE_EPSILON)) : minY; + maxY = maxY == Integer.MIN_VALUE ? Math.min(this.shape.getSize(axisY), this.findIndex(axisY, box.max(axisY) - POSITIVE_EPSILON) + 1) : maxY; + + for (y = minY; y < maxY; ++y) { + minZ = minZ == Integer.MIN_VALUE ? Math.max(0, this.findIndex(axisZ, box.min(axisZ) + POSITIVE_EPSILON)) : minZ; + maxZ = maxZ == Integer.MIN_VALUE ? Math.min(this.shape.getSize(axisZ), this.findIndex(axisZ, box.max(axisZ) - POSITIVE_EPSILON) + 1) : maxZ; + + for (z = minZ; z < maxZ; ++z) { + if (this.shape.isFullWide(cycle, x, y, z)) { + dist = this.get(axisX, x + 1) - min; + + if (dist <= POSITIVE_EPSILON) { + maxDist = Math.max(maxDist, dist); + } + + return maxDist; + } + } + } + } + } + + return maxDist; + } + + /** + * Inlines the lambda passed to MathHelper#binarySearch. Simplifies the implementation very slightly for additional + * speed. + * + * @reason Use faster implementation + * @author JellySquid + */ + @Overwrite + public int findIndex(Direction.Axis axis, double coord) { + DoubleList list = this.getCoords(axis); + + int size = this.shape.getSize(axis); + + int start = 0; + int end = size + 1 - start; + + while (end > 0) { + int middle = end / 2; + int idx = start + middle; + + if (idx >= 0 && (idx > size || coord < list.getDouble(idx))) { + end = middle; + } else { + start = idx + 1; + end -= middle + 1; + } + } + + return start - 1; + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/specialized_shapes/VoxelShapesMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/specialized_shapes/VoxelShapesMixin.java new file mode 100644 index 0000000..43c827e --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/shapes/specialized_shapes/VoxelShapesMixin.java @@ -0,0 +1,113 @@ +package net.gensokyoreimagined.nitori.mixin.shapes.specialized_shapes; + +//TODO: Later + +//import me.jellysquid.mods.lithium.common.shapes.VoxelShapeAlignedCuboid; +//import me.jellysquid.mods.lithium.common.shapes.VoxelShapeEmpty; +//import me.jellysquid.mods.lithium.common.shapes.VoxelShapeSimpleCube; +//import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape; +//import net.minecraft.world.phys.shapes.DiscreteVoxelShape; +//import net.minecraft.world.phys.shapes.VoxelShape; +//import net.minecraft.world.phys.shapes.Shapes; +//import org.spongepowered.asm.mixin.*; +// +///** +// * Shape specialization allows us to optimize comparison logic by guaranteeing certain constraints about the +// * configuration of vertices in a given shape. For example, most block shapes consist of only one cuboid and by +// * nature, only one voxel. This fact can be taken advantage of to create an optimized implementation which avoids +// * scanning over voxels as there are only ever two given vertices in the shape, allowing simple math operations to be +// * used for determining intersection and penetration. +// *

+// * In most cases, comparison logic is rather simple as the game often only deals with empty shapes or simple cubes. +// * Specialization provides a significant speed-up to entity collision resolution and various other parts of the game +// * without needing invasive patches, as we can simply replace the types returned by this class. Modern processors +// * (along with the help of the potent JVM) make the cost of dynamic dispatch negligible when compared to the execution +// * times of shape comparison methods. +// */ +//@Mixin(Shapes.class) +//public abstract class VoxelShapesMixin { +// @Mutable +// @Shadow +// @Final +// public static final VoxelShape INFINITY; +// +// @Mutable +// @Shadow +// @Final +// private static final VoxelShape BLOCK; +// +// @Mutable +// @Shadow +// @Final +// private static final VoxelShape EMPTY; +// +// private static final DiscreteVoxelShape FULL_CUBE_VOXELS; +// +// // Re-initialize the global cached shapes with our specialized ones. This will happen right after all the static +// // state has been initialized and before any external classes access it. +// static { +// // [VanillaCopy] The FULL_CUBE and UNBOUNDED shape is initialized with a single 1x1x1 voxel as neither will +// // contain multiple inner cuboids. +// FULL_CUBE_VOXELS = new BitSetDiscreteVoxelShape(1, 1, 1); +// FULL_CUBE_VOXELS.fill(0, 0, 0); +// +// // Used in some rare cases to indicate a shape which encompasses the entire world (such as a moving world border) +// INFINITY = new VoxelShapeSimpleCube(FULL_CUBE_VOXELS, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, +// Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); +// +// // Represents a full-block cube shape, such as that for a dirt block. +// BLOCK = new VoxelShapeSimpleCube(FULL_CUBE_VOXELS, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0); +// +// // Represents an empty cube shape with no vertices that cannot be collided with. +// EMPTY = new VoxelShapeEmpty(new BitSetDiscreteVoxelShape(0, 0, 0)); +// } +// +// /** +// * Vanilla implements some very complex logic in this function in order to allow entity boxes to be used in +// * collision resolution the same way as block shapes. The specialized simple cube shape however can trivially +// * represent these cases with nothing more than the two vertexes. This provides a modest speed up for entity +// * collision code by allowing them to also use our optimized shapes. +// *

+// * Vanilla uses different kinds of VoxelShapes depending on the size and position of the box. +// * A box that isn't aligned with 1/8th of a block will become a very simple ArrayVoxelShape, while others +// * will become a "SimpleVoxelShape" with a BitSetVoxelSet that possibly has a higher resolution (1-3 bits) per axis. +// *

+// * Shapes that have a high resolution (e.g. extended piston base has 2 bits on one axis) have collision +// * layers inside them. An upwards extended piston base has extra collision boxes at 0.25 and 0.5 height. +// * Slabs don't have extra collision boxes, because they are only as high as the smallest height that is possible +// * with their bit resolution (1, so half a block). +// * +// * @reason Use our optimized shape types +// * @author JellySquid, 2No2Name +// */ +// @Overwrite +// public static VoxelShape cuboidUnchecked(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { +// if (maxX - minX < 1.0E-7D || maxY - minY < 1.0E-7D || maxZ - minZ < 1.0E-7D) { +// return EMPTY; +// } +// +// int xRes; +// int yRes; +// int zRes; +// //findRequiredBitResolution(...) looks unnecessarily slow, and it seems to unintentionally return -1 on inputs like -1e-8, +// //A faster implementation is not in the scope of this mixin. +// +// //Description of what vanilla does: +// //If the VoxelShape cannot be represented by a BitSet with 3 bit resolution on any axis (BitSetVoxelSet), +// //a shape without boxes inside will be used in vanilla (ArrayVoxelShape with only 2 PointPositions on each axis) +// +// if ((xRes = Shapes.findBits(minX, maxX)) < 0 || +// (yRes = Shapes.findBits(minY, maxY)) < 0 || +// (zRes = Shapes.findBits(minZ, maxZ)) < 0) { +// //vanilla uses ArrayVoxelShape here without any rounding of the coordinates +// return new VoxelShapeSimpleCube(FULL_CUBE_VOXELS, minX, minY, minZ, maxX, maxY, maxZ); +// } else { +// if (xRes == 0 && yRes == 0 && zRes == 0) { +// return BLOCK; +// } +// // vanilla would use a SimpleVoxelShape with a BitSetVoxelSet of resolution of xRes, yRes, zRes here, we match its behavior +// return new VoxelShapeAlignedCuboid(Math.round(minX * 8D) / 8D, Math.round(minY * 8D) / 8D, Math.round(minZ * 8D) / 8D, +// Math.round(maxX * 8D) / 8D, Math.round(maxY * 8D) / 8D, Math.round(maxZ * 8D) / 8D, xRes, yRes, zRes); +// } +// } +//} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/entity/move_zero_velocity/MixinEntity.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/entity/move_zero_velocity/MixinEntity.java new file mode 100644 index 0000000..e21d7b5 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/entity/move_zero_velocity/MixinEntity.java @@ -0,0 +1,34 @@ +package net.gensokyoreimagined.nitori.mixin.vmp.entity.move_zero_velocity; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.MoverType; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; +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.callback.CallbackInfo; + +@Mixin(Entity.class) +public class MixinEntity { + + @Shadow private AABB bb; + @Unique + private boolean boundingBoxChanged = false; + + @Inject(method = "move", at = @At("HEAD"), cancellable = true) + private void onMove(MoverType movementType, Vec3 movement, CallbackInfo ci) { + if (!boundingBoxChanged && movement.equals(Vec3.ZERO)) { + ci.cancel(); + boundingBoxChanged = false; + } + } + + @Inject(method = "setBoundingBox", at = @At("HEAD")) + private void onBoundingBoxChanged(AABB boundingBox, CallbackInfo ci) { + if (!this.bb.equals(boundingBox)) boundingBoxChanged = true; + } + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/general/collections/MixinTypeFilterableList.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/general/collections/MixinTypeFilterableList.java new file mode 100644 index 0000000..21c74eb --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/general/collections/MixinTypeFilterableList.java @@ -0,0 +1,76 @@ +package net.gensokyoreimagined.nitori.mixin.vmp.general.collections; + +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.gensokyoreimagined.nitori.access.ITypeFilterableList; +import net.minecraft.util.ClassInstanceMultiMap; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.*; + +@Mixin(value = ClassInstanceMultiMap.class, priority = 1005) // priority compatibility hack for lithium +public abstract class MixinTypeFilterableList extends AbstractCollection implements ITypeFilterableList { + + @Mutable + @Shadow @Final private Map, List> byClass; + + @Mutable + @Shadow @Final private List allInstances; + + @Shadow @Final private Class baseClass; + + @Redirect(method = "", at = @At(value = "FIELD", target = "Lnet/minecraft/util/ClassInstanceMultiMap;byClass:Ljava/util/Map;", opcode = Opcodes.PUTFIELD)) + private void redirectSetElementsByType(ClassInstanceMultiMap instance, Map, List> value) { + this.byClass = new Object2ObjectLinkedOpenHashMap<>(); + } + + @Redirect(method = "", at = @At(value = "FIELD", target = "Lnet/minecraft/util/ClassInstanceMultiMap;allInstances:Ljava/util/List;", opcode = Opcodes.PUTFIELD)) + private void redirectSetAllElements(ClassInstanceMultiMap instance, List value) { + this.allInstances = new ObjectArrayList<>(); + } + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false)) + private HashMap redirectNewHashMap() { + return null; // avoid unnecessary alloc + } + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Lists;newArrayList()Ljava/util/ArrayList;", remap = false)) + private ArrayList redirectNewArrayList() { + return null; + } + + @Override + public Object[] getBackingArray() { + return ((ObjectArrayList) this.allInstances).elements(); + } + + /** + * @author ishland + * @reason use fastutil array list for faster iteration & use array for filtering iteration + */ + @Overwrite + public Collection find(Class type) { + List cached = this.byClass.get(type); + if (cached != null) return (Collection) cached; + + if (!this.baseClass.isAssignableFrom(type)) { + throw new IllegalArgumentException("Don't know how to search for " + type); + } else { + List list = this.byClass.computeIfAbsent(type, + typeClass -> { + ObjectArrayList ts = new ObjectArrayList<>(this.allInstances.size()); + for (Object _allElement : ((ObjectArrayList) this.allInstances).elements()) { + if (typeClass.isInstance(_allElement)) { + ts.add((T) _allElement); + } + } + return ts; + } + ); + return (Collection) list; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinChunkFilter.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/playerwatching/MixinChunkFilter.java similarity index 91% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinChunkFilter.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/playerwatching/MixinChunkFilter.java index 0c04e76..2a6da58 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinChunkFilter.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/playerwatching/MixinChunkFilter.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.playerwatching; +package net.gensokyoreimagined.nitori.mixin.vmp.playerwatching; import net.minecraft.server.level.ChunkTrackingView; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinServerPlayerEntity.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/playerwatching/MixinServerPlayerEntity.java similarity index 95% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinServerPlayerEntity.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/playerwatching/MixinServerPlayerEntity.java index 5357e9b..36d5eff 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/MixinServerPlayerEntity.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/playerwatching/MixinServerPlayerEntity.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.playerwatching; +package net.gensokyoreimagined.nitori.mixin.vmp.playerwatching; import net.gensokyoreimagined.nitori.common.chunkwatching.PlayerClientVDTracking; import net.minecraft.server.level.ClientInformation; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java similarity index 93% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java index 3a4e019..24b16cc 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/vmp/playerwatching/optimize_nearby_player_lookups/MixinMobEntity.java @@ -1,4 +1,4 @@ -package net.gensokyoreimagined.nitori.mixin.playerwatching.optimize_nearby_player_lookups; +package net.gensokyoreimagined.nitori.mixin.vmp.playerwatching.optimize_nearby_player_lookups; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/world/portal_checks/DisablePortalChecksMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/portal_checks/DisablePortalChecksMixin.java new file mode 100644 index 0000000..3b25743 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/world/portal_checks/DisablePortalChecksMixin.java @@ -0,0 +1,23 @@ +package net.gensokyoreimagined.nitori.mixin.world.portal_checks; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.portal.PortalShape; +import net.minecraft.world.phys.Vec3; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityDimensions; +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({PortalShape.class}) +public class DisablePortalChecksMixin { + @Inject( + at = {@At("HEAD")}, + method = {"findCollisionFreePosition"}, + cancellable = true + ) + private static void init(Vec3 fallback, ServerLevel world, Entity entity, EntityDimensions dimensions, CallbackInfoReturnable cir) { + cir.setReturnValue(fallback); + } +} diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 23bb71e..67be2c4 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -58,11 +58,16 @@ "math.vec.FastMathVec3DMixin", "network.microopt.VarIntsMixin", "network.microopt.StringEncodingMixin", - "playerwatching.MixinChunkFilter", - "playerwatching.MixinServerPlayerEntity", - "playerwatching.optimize_nearby_player_lookups.MixinMobEntity", + "vmp.playerwatching.MixinChunkFilter", + "vmp.playerwatching.MixinServerPlayerEntity", + "vmp.playerwatching.optimize_nearby_player_lookups.MixinMobEntity", + "vmp.general.collections.MixinTypeFilterableList", + "vmp.entity.move_zero_velocity.MixinEntity", "shapes.blockstate_cache.BlockMixin", "shapes.lazy_shape_context.EntityShapeContextMixin", + "shapes.precompute_shape_arrays.FractionalDoubleListMixin", + "shapes.precompute_shape_arrays.SimpleVoxelShapeMixin", + "shapes.specialized_shapes.VoxelShapeMixin", "util.MixinLevelBlockEntityRetrieval", "util.accessors.ClientEntityManagerAccessor", "util.accessors.EntityTrackingSectionAccessor", @@ -75,6 +80,7 @@ "world.block_entity_ticking.sleeping.furnace.AbstractFurnaceBlockEntityMixin", "world.block_entity_ticking.support_cache.BlockEntityMixin", "world.block_entity_ticking.support_cache.DirectBlockEntityTickInvokerMixin", - "world.block_entity_ticking.support_cache.WorldChunkMixin" + "world.block_entity_ticking.support_cache.WorldChunkMixin", + "world.portal_checks.DisablePortalChecksMixin" ] } From 6cbee7408f6ce0ff16a0ba89d9cce23391daa297 Mon Sep 17 00:00:00 2001 From: Taiyou06 Date: Sat, 13 Jul 2024 13:29:15 +0300 Subject: [PATCH 21/21] Update VarIntsMixin.java --- .../nitori/mixin/network/microopt/VarIntsMixin.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/VarIntsMixin.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/VarIntsMixin.java index eebd332..632d41f 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/VarIntsMixin.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/network/microopt/VarIntsMixin.java @@ -22,18 +22,17 @@ public class VarIntsMixin { * @reason optimized version */ @Overwrite - public static ByteBuf write(ByteBuf buf, int value) { + public static ByteBuf write(ByteBuf buf, int i) { // Peel the one and two byte count cases explicitly as they are the most common VarInt sizes // that the server will send, to improve inlining. - if ((value & (0xFFFFFFFF << 7)) == 0) { - buf.writeByte(value); - } else if ((value & (0xFFFFFFFF << 14)) == 0) { - int w = (value & 0x7F | 0x80) << 8 | (value >>> 7); + if ((i & (0xFFFFFFFF << 7)) == 0) { + buf.writeByte(i); + } else if ((i & (0xFFFFFFFF << 14)) == 0) { + int w = (i & 0x7F | 0x80) << 8 | (i >>> 7); buf.writeShort(w); } else { - writeOld(buf, value); + writeOld(buf, i); } - return buf; }