Skip to content

Commit

Permalink
Improve various Ashy Shoals performance issues. (#163)
Browse files Browse the repository at this point in the history
- Allow Ashy Shoals in dimensions besides vanilla Nether
- Add config option to limit ash particle probability
  • Loading branch information
gniftygnome authored Jun 14, 2024
1 parent 902190a commit 04f3e92
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.terraformersmc.cinderscapes.mixin;

import com.terraformersmc.cinderscapes.Cinderscapes;
import com.terraformersmc.cinderscapes.config.CinderscapesConfig;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.world.biome.BiomeParticleConfig;
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.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

@Mixin(BiomeParticleConfig.class)
public class MixinBiomeParticleConfig {
@Shadow
@Mutable
@Final
private float probability;

@Unique
private static final float ASH_PARTICLE_LIMIT = 0.125f;

@Inject(method = "<init>", at = @At("TAIL"), locals = LocalCapture.NO_CAPTURE)
private void cinderscapes$configurableAshParticle(ParticleEffect particle, float probability, CallbackInfo ci) {
if (CinderscapesConfig.INSTANCE.limitAshParticles && probability > ASH_PARTICLE_LIMIT) {
Cinderscapes.LOGGER.info("Limiting ash particle probability from {} to {}", probability, ASH_PARTICLE_LIMIT);

this.probability = ASH_PARTICLE_LIMIT;
}
}
}
2 changes: 2 additions & 0 deletions client/src/main/resources/assets/cinderscapes/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@
"text.autoconfig.cinderscapes.title": "Cinderscapes Config",
"text.autoconfig.cinderscapes.option.easterEggs": "Enable Easter Eggs",
"text.autoconfig.cinderscapes.option.enableAshFall": "Enable Ash Fall",
"text.autoconfig.cinderscapes.option.limitAshParticles": "(Client) Limit ash particles",
"text.autoconfig.cinderscapes.option.limitAshParticles.@Tooltip": "Ash particle rate limit roughly that of Basalt Delta",
"text.autoconfig.cinderscapes.option.polypiteLuminance": "Polypite Quartz Brightness",
"text.autoconfig.cinderscapes.option.polypiteLuminance.@Tooltip": "Only affects newly generated chunks (lighting cache)",
"text.autoconfig.cinderscapes.option.biomes": "Modify Biome Generation",
Expand Down
1 change: 1 addition & 0 deletions client/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
]
},
"mixins": [
"mixins.cinderscapes-client.json"
],
"custom": {
"modmenu": {
Expand Down
11 changes: 11 additions & 0 deletions client/src/main/resources/mixins.cinderscapes-client.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"required": true,
"package": "com.terraformersmc.cinderscapes.mixin",
"compatibilityLevel": "JAVA_17",
"client": [
"MixinBiomeParticleConfig"
],
"injectors": {
"defaultRequire": 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public final class CinderscapesConfig implements ConfigData {

public boolean easterEggs = false;

public boolean limitAshParticles = false;

@ConfigEntry.Gui.Tooltip
@ConfigEntry.BoundedDiscrete(max = 15)
public int polypiteLuminance = 4;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,70 +7,98 @@
import net.minecraft.block.BlockState;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldGenerationProgressListener;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.ChunkSectionPos;
import net.minecraft.util.math.random.RandomSequencesState;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.world.MutableWorldProperties;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.dimension.DimensionTypes;
import net.minecraft.world.level.ServerWorldProperties;
import net.minecraft.world.level.storage.LevelStorage;
import net.minecraft.world.spawner.SpecialSpawner;
import org.jetbrains.annotations.Nullable;
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.LocalCapture;

import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
import java.util.function.Supplier;

@Mixin(ServerWorld.class)
abstract class MixinServerWorld extends World {
private MixinServerWorld(MutableWorldProperties properties, RegistryKey<World> registryRef, DynamicRegistryManager registryManager, RegistryEntry<DimensionType> dimensionEntry, Supplier<Profiler> profiler, boolean isClient, boolean debugWorld, long biomeAccess, int maxChainedNeighborUpdates) {
public abstract class MixinServerWorld extends World {
@Unique
private static Predicate<RegistryEntry<Biome>> ASHY_SHOALS_PREDICATE;

protected MixinServerWorld(MutableWorldProperties properties, RegistryKey<World> registryRef, DynamicRegistryManager registryManager, RegistryEntry<DimensionType> dimensionEntry, Supplier<Profiler> profiler, boolean isClient, boolean debugWorld, long biomeAccess, int maxChainedNeighborUpdates) {
super(properties, registryRef, registryManager, dimensionEntry, profiler, isClient, debugWorld, biomeAccess, maxChainedNeighborUpdates);
}

@Inject(method = "<init>", at = @At("TAIL"))
private void cinderscapes$cacheAshyShoalsEntry(MinecraftServer server, Executor workerExecutor, LevelStorage.Session session, ServerWorldProperties properties, RegistryKey<World> worldKey, DimensionOptions dimensionOptions, WorldGenerationProgressListener worldGenerationProgressListener, boolean debugWorld, long seed, List<SpecialSpawner> spawners, boolean shouldTickTime, @Nullable RandomSequencesState randomSequencesState, CallbackInfo ci) {
ASHY_SHOALS_PREDICATE = Predicate.isEqual(getRegistryManager().get(RegistryKeys.BIOME).entryOf(CinderscapesBiomes.ASHY_SHOALS));
}

/*
* NOTE: Unlike this mixin, vanilla evaluates the tick only at the surface Y level.
* This means there is no utility for us in reusing vanilla's calculations.
*/
@Inject(method="tickIceAndSnow", at = @At(value = "HEAD"), locals = LocalCapture.NO_CAPTURE)
@Inject(method = "tickIceAndSnow", at = @At("HEAD"), locals = LocalCapture.NO_CAPTURE)
private void cinderscapes$tickAsh(BlockPos tickPos, CallbackInfo ci) {
// Ashy shoals only exists in the nether, why do this iteration on any other dimension
if (!getDimensionEntry().matchesKey(DimensionTypes.THE_NETHER)) return;
if (CinderscapesConfig.INSTANCE.enableAshFall) {
BlockPos.Mutable pos = tickPos.mutableCopy();
if (!CinderscapesConfig.INSTANCE.enableAshFall) {
return;
}

for (; pos.getY() < 127; pos.setY(pos.getY() + 1)) {
BlockPos up = pos.up();
if (!canPlace(up)) {
continue;
} else if (!this.getBiome(up).matchesKey(CinderscapesBiomes.ASHY_SHOALS)) {
continue;
}
this.setBlockState(up, CinderscapesBlocks.ASH.getDefaultState());
BlockPos.Mutable pos = tickPos.mutableCopy();
ChunkSection[] sections = getChunk(tickPos).getSectionArray();
int sectionIndex;

for (pos.setY(pos.getY() + 1); pos.getY() < 127; pos.setY(pos.getY() + 1)) {
sectionIndex = ChunkSectionPos.getSectionCoord(pos.getY());

if (sections[sectionIndex].isEmpty() || !sections[sectionIndex].getBiomeContainer().hasAny(ASHY_SHOALS_PREDICATE)) {
pos.setY(ChunkSectionPos.getBlockCoord(sectionIndex + 1) - 1);
continue;
}

if (canPlaceAshAt(pos) && getBiome(pos).matchesKey(CinderscapesBiomes.ASHY_SHOALS)) {
setBlockState(pos, CinderscapesBlocks.ASH.getDefaultState());
break;
}
}
}

@Unique
private boolean canPlace(BlockPos pos){
return this.getBlockState(pos).isAir() &&
private boolean canPlaceAshAt(BlockPos.Mutable pos) {
return getBlockState(pos).isAir() &&
blockAbove(pos).isIn(CinderscapesBlockTags.ASH_PERMEABLE) &&
CinderscapesBlocks.ASH.getDefaultState().canPlaceAt(this, pos);
}

@Unique
private BlockState blockAbove(BlockPos pos) {
BlockPos iPos = pos.mutableCopy();
private BlockState blockAbove(BlockPos.Mutable pos) {
int originalY = pos.getY();
BlockState stateAbove;

//up() makes new immutable blockpos per call. This uses the Mutable as a Mutable.
//noinspection StatementWithEmptyBody
for (; isAir(iPos) && iPos.getY() < 127; iPos.setY(iPos.getY() + 1));
for (; isAir(pos) && pos.getY() < 127; pos.setY(pos.getY() + 1));

stateAbove = getBlockState(pos);
pos.setY(originalY);

return getBlockState(iPos);
return stateAbove;
}
}

0 comments on commit 04f3e92

Please sign in to comment.