Skip to content

Commit

Permalink
Misc improvements (#22)
Browse files Browse the repository at this point in the history
* Fix running in dev

* Port entity fluid optimization to Forge

* Improve efficiency of Forge isInFluidType() extension

* Do not bother making the hashed reference list for tiny pools

* Reduce overhead of Forge spawning event

* Disable some known-problematic mixin packages by default

* Port POI mixin to Forge

* Update mixin config file
  • Loading branch information
embeddedt committed Mar 7, 2024
1 parent 5f80f74 commit 2c2f0be
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 82 deletions.
15 changes: 15 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ jar {
from sourceSets.api.output.resourcesDir
}

loom {
runs {
client {
mods {
radium {
sourceSet sourceSets.main
sourceSet sourceSets.api
}
}
}
}
}

repositories {
allprojects {
repositories {
Expand Down Expand Up @@ -141,12 +154,14 @@ tasks.withType(AbstractArchiveTask) {
}

//Mixin hotswap
/*
afterEvaluate {
loom.runs.configureEach {
// https://fabricmc.net/wiki/tutorial:mixin_hotswaps
vmArg "-javaagent:${ configurations.compileClasspath.find { it.name.contains("sponge-mixin") } }"
}
}
*/

// configure the maven publication
publishing {
Expand Down
4 changes: 2 additions & 2 deletions lithium-mixin-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ Optimizations related to blocks
FluidStates store directly whether they are empty

### `mixin.block.hopper`
(default: `true`)
(default: `false`)
Reduces hopper lag using caching, notification systems and BlockEntity sleeping
Requirements:
- `mixin.util.entity_movement_tracking=true`
Expand Down Expand Up @@ -479,7 +479,7 @@ Various world related optimizations
Various BlockEntity ticking optimizations

### `mixin.world.block_entity_ticking.sleeping`
(default: `true`)
(default: `false`)
Allows BlockEntities to sleep, meaning they are no longer ticked until woken up, e.g. by updates to their inventory or block state

### `mixin.world.block_entity_ticking.sleeping.brewing_stand`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ public class BlockStateFlags {
//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 ANY_FLUID;

public static final TrackedBlockStatePredicate[] FLAGS;

Expand Down Expand Up @@ -77,24 +76,15 @@ public boolean test(BlockState operand) {
countingFlags.add(OVERSIZED_SHAPE);

if (FluidCachingEntity.class.isAssignableFrom(Entity.class)) {
WATER = new TrackedBlockStatePredicate(countingFlags.size()) {
ANY_FLUID = new TrackedBlockStatePredicate(countingFlags.size()) {
@Override
public boolean test(BlockState operand) {
return operand.getFluidState().getFluid().isIn(FluidTags.WATER);
return !operand.getFluidState().isEmpty();
}
};
countingFlags.add(WATER);

LAVA = new TrackedBlockStatePredicate(countingFlags.size()) {
@Override
public boolean test(BlockState operand) {
return operand.getFluidState().getFluid().isIn(FluidTags.LAVA);
}
};
countingFlags.add(LAVA);
countingFlags.add(ANY_FLUID);
} else {
WATER = null;
LAVA = null;
ANY_FLUID = null;
}

if (BlockStatePathingCache.class.isAssignableFrom(AbstractBlock.AbstractBlockState.class)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package me.jellysquid.mods.lithium.common.world;

public interface PotentialSpawnsExtended {
boolean radium$wasListModified();
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
package me.jellysquid.mods.lithium.mixin.ai.poi;

import me.jellysquid.mods.lithium.common.world.interests.types.PointOfInterestTypeHelper;
import net.minecraft.block.BlockState;
import net.minecraft.world.poi.PointOfInterestTypes;
import org.spongepowered.asm.mixin.Final;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
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.ModifyArg;

import java.util.Set;
import java.util.Map;

/**
* Replaces the backing map type with a faster collection type which uses reference equality.
*/
@Mixin(PointOfInterestTypes.class)
@Mixin(targets = { "net/minecraftforge/registries/GameData$PointOfInterestTypeCallbacks" })
public class PointOfInterestTypesMixin {
@Shadow
@Final
protected static Set<BlockState> f_218067_;

static {
// POI_STATES_TO_TYPE = new Reference2ReferenceOpenHashMap<>(POI_STATES_TO_TYPE); TODO why it broke?

PointOfInterestTypeHelper.init(f_218067_);
@ModifyArg(method = "onCreate", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/registries/IForgeRegistryInternal;setSlaveMap(Lnet/minecraft/util/Identifier;Ljava/lang/Object;)V"), index = 1)
private Object changeMapType(Object obj) {
return new Reference2ReferenceOpenHashMap<>((Map<?, ?>)obj);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
@MixinConfigDependency(dependencyPath = "mixin.util.entity_movement_tracking"),
@MixinConfigDependency(dependencyPath = "mixin.util.block_entity_retrieval"),
@MixinConfigDependency(dependencyPath = "mixin.util.inventory_change_listening")
}
},
enabled = false
)
package me.jellysquid.mods.lithium.mixin.block.hopper;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package me.jellysquid.mods.lithium.mixin.collections.mob_spawning;

import me.jellysquid.mods.lithium.common.world.PotentialSpawnsExtended;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.util.collection.Pool;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.biome.SpawnSettings;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.event.level.LevelEvent;
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;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

@Mixin(ForgeEventFactory.class)
public class ForgeEventFactoryMixin {
/**
* @author embeddedt
* @reason Avoid the overhead of re-creating a pool in the event that the spawn list was not changed.
*/
@Inject(method = "getPotentialSpawns",
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/Pool;of(Ljava/util/List;)Lnet/minecraft/util/collection/Pool;"),
locals = LocalCapture.CAPTURE_FAILHARD,
cancellable = true
)
private static void reusePoolIfPossible(WorldAccess level, SpawnGroup category, BlockPos pos, Pool<SpawnSettings.SpawnEntry> oldList, CallbackInfoReturnable<Pool<SpawnSettings.SpawnEntry>> cir, LevelEvent.PotentialSpawns event) {
if(!((PotentialSpawnsExtended)event).radium$wasListModified()) {
cir.setReturnValue(oldList);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class PoolMixin<E extends Weighted> {
@Inject(method = "<init>(Ljava/util/List;)V", at = @At("RETURN"))
private void init(List<? extends E> entries, CallbackInfo ci) {
//We are using reference equality here, because all vanilla implementations of Weighted use reference equality
this.entryHashList = this.entries.size() > 4 ? this.entries : Collections.unmodifiableList(new HashedReferenceList<>(this.entries));
this.entryHashList = this.entries.size() < 4 ? this.entries : Collections.unmodifiableList(new HashedReferenceList<>(this.entries));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package me.jellysquid.mods.lithium.mixin.collections.mob_spawning;

import me.jellysquid.mods.lithium.common.world.PotentialSpawnsExtended;
import net.minecraftforge.event.level.LevelEvent;
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(LevelEvent.PotentialSpawns.class)
public class PotentialSpawnsEventMixin implements PotentialSpawnsExtended {
private boolean radium$listModified;

@Inject(method = "addSpawnerData", at = @At("RETURN"))
private void onAdd(CallbackInfo ci) {
radium$listModified = true;
}

@Inject(method = "removeSpawnerData", at = @At("RETURN"))
private void onRemove(CallbackInfoReturnable<Boolean> ci) {
radium$listModified = true;
}

@Override
public boolean radium$wasListModified() {
return radium$listModified;
}
}
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
package me.jellysquid.mods.lithium.mixin.entity.collisions.fluid;

import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import it.unimi.dsi.fastutil.objects.Object2DoubleMaps;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import me.jellysquid.mods.lithium.common.block.BlockCountingSection;
import me.jellysquid.mods.lithium.common.block.BlockStateFlags;
import me.jellysquid.mods.lithium.common.block.TrackedBlockStatePredicate;
import me.jellysquid.mods.lithium.common.entity.FluidCachingEntity;
import me.jellysquid.mods.lithium.common.util.Pos;
import net.minecraft.entity.Entity;
import net.minecraft.fluid.Fluid;
import net.minecraft.registry.tag.FluidTags;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.math.Box;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraftforge.fluids.FluidType;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

@Mixin(Entity.class)
public abstract class EntityMixin implements FluidCachingEntity { // TODO aready patched by forge
import java.util.function.BiPredicate;

/*@Shadow
@Mixin(Entity.class)
public abstract class EntityMixin implements FluidCachingEntity {
@Shadow
public abstract Box getBoundingBox();

@Shadow
public World world;

@Shadow
protected Object2DoubleMap<TagKey<Fluid>> fluidHeight;
protected Object2DoubleMap<FluidType> forgeFluidTypeHeight;

/**
* @author 2No2Name, embeddedt
* @reason Skip computing fluid heights if we know none of the relevant chunk sections contain any fluids.
*/
@Inject(
method = "updateMovementInFluid(Lnet/minecraft/registry/tag/TagKey;D)Z",
method = "updateFluidHeightAndDoFluidPushing",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/entity/Entity;isPushedByFluids()Z",
Expand All @@ -43,15 +49,7 @@ public abstract class EntityMixin implements FluidCachingEntity { // TODO aready
cancellable = true,
locals = LocalCapture.CAPTURE_FAILHARD
)
public void tryShortcutFluidPushing(TagKey<Fluid> tag, double speed, CallbackInfoReturnable<Boolean> cir, Box box, int x1, int x2, int y1, int y2, int z1, int z2, double zero) {
TrackedBlockStatePredicate blockStateFlag;
if (tag == FluidTags.WATER) {
blockStateFlag = BlockStateFlags.WATER;
} else if (tag == FluidTags.LAVA) {
blockStateFlag = BlockStateFlags.LAVA;
} else {
return;
}
public void tryShortcutFluidPushing(CallbackInfo ci, Box box, int x1, int x2, int y1, int y2, int z1, int z2, double zero) {
int chunkX1 = x1 >> 4;
int chunkZ1 = z1 >> 4;
int chunkX2 = ((x2 - 1) >> 4);
Expand All @@ -63,16 +61,63 @@ public void tryShortcutFluidPushing(TagKey<Fluid> tag, double speed, CallbackInf
Chunk chunk = this.world.getChunk(chunkX, chunkZ);
for (int chunkYIndex = chunkYIndex1; chunkYIndex <= chunkYIndex2; chunkYIndex++) {
ChunkSection section = chunk.getSectionArray()[chunkYIndex];
if (((BlockCountingSection) section).mayContainAny(blockStateFlag)) {
if (((BlockCountingSection) section).mayContainAny(BlockStateFlags.ANY_FLUID)) {
//fluid found, cannot skip code
return;
}
}
}
}

//side effects of not finding a fluid:
this.fluidHeight.put(tag, 0.0);
cir.setReturnValue(false);
}*/
//side effects of not finding a fluid.
if(!this.forgeFluidTypeHeight.isEmpty()) {
// only call clear() if map contains anything, because array maps use naive clear
this.forgeFluidTypeHeight.clear();
}
ci.cancel();
}

/**
* @author embeddedt
* @reason Skip running expensive logic from Forge if the entity is known not to be in a fluid.
*/
@Inject(method = "updateWaterState", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;checkWaterState()V", shift = At.Shift.AFTER), cancellable = true)
private void earlyExitIfTouchingNone(CallbackInfoReturnable<Boolean> cir) {
if(this.forgeFluidTypeHeight.isEmpty()) {
cir.setReturnValue(false);
}
}

/**
* @author embeddedt
* @reason early-exit for entities with no fluids (the likely case), avoid streams
*/
@Overwrite
public final boolean isInFluidType(BiPredicate<FluidType, Double> predicate, boolean forAllTypes) {
if(this.forgeFluidTypeHeight.isEmpty()) {
return false;
} else {
ObjectIterator<Object2DoubleMap.Entry<FluidType>> it = Object2DoubleMaps.fastIterator(this.forgeFluidTypeHeight);
if(forAllTypes) {
// Check if all fluids match
while (it.hasNext()) {
var entry = it.next();
if(!predicate.test(entry.getKey(), entry.getDoubleValue())) {
return false;
}
}
return true;
} else {
// Check if any fluid matches
while (it.hasNext()) {
var entry = it.next();
if(predicate.test(entry.getKey(), entry.getDoubleValue())) {
return true;
}
}
return false;
}

}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@MixinConfigOption(description = "Allows BlockEntities to sleep, meaning they are no longer ticked until woken up, e.g. by updates to their inventory or block state")
@MixinConfigOption(description = "Allows BlockEntities to sleep, meaning they are no longer ticked until woken up, e.g. by updates to their inventory or block state", enabled = false)
package me.jellysquid.mods.lithium.mixin.world.block_entity_ticking.sleeping;

import net.caffeinemc.gradle.MixinConfigOption;
2 changes: 2 additions & 0 deletions src/main/resources/lithium.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@
"collections.fluid_submersion.EntityMixin",
"collections.gamerules.GameRulesMixin",
"collections.goals.GoalSelectorMixin",
"collections.mob_spawning.ForgeEventFactoryMixin",
"collections.mob_spawning.PoolMixin",
"collections.mob_spawning.PotentialSpawnsEventMixin",
"collections.mob_spawning.SpawnSettingsMixin",
"entity.collisions.fluid.EntityMixin",
"entity.collisions.intersection.WorldMixin",
Expand Down

0 comments on commit 2c2f0be

Please sign in to comment.