Skip to content

Commit

Permalink
(Horrible) temporary work-around for arch loom getting common mixin r…
Browse files Browse the repository at this point in the history
…emap wrong for Forge
  • Loading branch information
gniftygnome committed Jul 3, 2024
1 parent b6e87e5 commit d34921a
Show file tree
Hide file tree
Showing 12 changed files with 675 additions and 2 deletions.
3 changes: 2 additions & 1 deletion forge/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ loom {
convertAccessWideners = true
extraAccessWideners.add loom.accessWidenerPath.get().asFile.name

mixinConfig("biolith.mixins.json", "biolith.forge.mixins.json")
mixinConfig("biolith.forge.mixins.json")
}
}

Expand Down Expand Up @@ -74,6 +74,7 @@ remapJar {
}

sourcesJar {
duplicatesStrategy = DuplicatesStrategy.WARN
def commonSources = project(":common").sourcesJar
dependsOn commonSources
from commonSources.archiveFile.map { zipTree(it) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.terraformersmc.biolith.impl.mixin;

import com.terraformersmc.biolith.impl.Biolith;
import com.terraformersmc.biolith.impl.biome.InterfaceBiomeSource;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.world.biome.source.BiomeSource;
import net.minecraft.world.dimension.DimensionType;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;

@Mixin(BiomeSource.class)
public class MixinBiomeSource implements InterfaceBiomeSource {
private RegistryEntry<DimensionType> biolith$dimensionTypeEntry;

@Override
public @Nullable RegistryEntry<DimensionType> biolith$getDimensionType() {
return biolith$dimensionTypeEntry;
}

@Override
public void biolith$setDimensionType(RegistryEntry<DimensionType> dimensionTypeEntry) {
if (biolith$dimensionTypeEntry != null &&
biolith$dimensionTypeEntry.hasKeyAndValue() && dimensionTypeEntry.hasKeyAndValue() &&
!biolith$dimensionTypeEntry.getKey().orElseThrow().equals(dimensionTypeEntry.getKey().orElseThrow())) {
Biolith.LOGGER.warn("Dimension Type modified: from '{}' to '{}'",
biolith$dimensionTypeEntry.getKey().orElseThrow(), dimensionTypeEntry.getKey().orElseThrow());
}

biolith$dimensionTypeEntry = dimensionTypeEntry;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.terraformersmc.biolith.impl.mixin;

import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import net.minecraft.world.gen.surfacebuilder.MaterialRules;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(ChunkGeneratorSettings.class)
public interface MixinChunkGeneratorSettings {
@Final
@Mutable
@Accessor("surfaceRule")
void biolith$setSurfaceRule(MaterialRules.MaterialRule ruleSource);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.terraformersmc.biolith.impl.mixin;

import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import com.terraformersmc.biolith.impl.data.BiomePlacementLoader;
import com.terraformersmc.biolith.impl.data.SurfaceGenerationLoader;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.resource.ResourceReloader;
import net.minecraft.resource.featuretoggle.FeatureSet;
import net.minecraft.server.DataPackContents;
import net.minecraft.server.command.CommandManager;
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 java.util.List;
import java.util.stream.Stream;

@Mixin(DataPackContents.class)
public abstract class MixinDataPackContents {
@Unique
private BiomePlacementLoader biomePlacementLoader;
@Unique
private SurfaceGenerationLoader surfaceGenerationLoader;

@Inject(method = "<init>", at = @At("TAIL"))
private void biolith$addDataPackContents(DynamicRegistryManager.Immutable dynamicRegistryManager, FeatureSet enabledFeatures, CommandManager.RegistrationEnvironment environment, int functionPermissionLevel, CallbackInfo ci) {
biomePlacementLoader = new BiomePlacementLoader();
surfaceGenerationLoader = new SurfaceGenerationLoader();
}

@ModifyReturnValue(method = "getContents", at = @At("RETURN"))
@SuppressWarnings("unused")
private List<ResourceReloader> biolith$addReloadersToContents(List<ResourceReloader> original) {
return Stream.concat(original.stream(), Stream.of(biomePlacementLoader, surfaceGenerationLoader)).toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.terraformersmc.biolith.impl.mixin;

import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.chunk.ChunkGenerator;
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;

@Mixin(DimensionOptions.class)
public class MixinDimensionOptions {
@Inject(method = "<init>", at = @At("RETURN"))
private void biolith$storeDimensionTypeToBiomeSource(RegistryEntry<DimensionType> dimensionTypeEntry, ChunkGenerator chunkGenerator, CallbackInfo ci) {
chunkGenerator.getBiomeSource().biolith$setDimensionType(dimensionTypeEntry);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.terraformersmc.biolith.impl.mixin;

import com.google.common.collect.Streams;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import com.terraformersmc.biolith.impl.biome.BiomeCoordinator;
import com.terraformersmc.biolith.impl.surface.SurfaceRuleCollector;
import net.minecraft.registry.*;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldGenerationProgressListener;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.dimension.DimensionTypes;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import net.minecraft.world.gen.chunk.NoiseChunkGenerator;
import net.minecraft.world.gen.surfacebuilder.MaterialRules;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

@Mixin(MinecraftServer.class)
public abstract class MixinMinecraftServer {
@Shadow
@Final
private Map<RegistryKey<World>, ServerWorld> worlds;

@WrapOperation(method = "<init>", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/registry/CombinedDynamicRegistries;getCombinedRegistryManager()Lnet/minecraft/registry/DynamicRegistryManager$Immutable;",
opcode = Opcodes.PUTFIELD,
ordinal = 0
))
@SuppressWarnings("unused")
private DynamicRegistryManager.Immutable biolith$earlyCaptureRegistries(CombinedDynamicRegistries<ServerDynamicRegistryType> instance, Operation<DynamicRegistryManager.Immutable> original) {
// This capture updates any registry manager we scraped previously with the final version.
BiomeCoordinator.setRegistryManager(instance);

return original.call(instance);
}

@Inject(method = "createWorlds", at = @At(value = "RETURN"), locals = LocalCapture.CAPTURE_FAILHARD)
private void biolith$prependSurfaceRules(WorldGenerationProgressListener worldGenerationProgressListener, CallbackInfo ci, @Local Registry<DimensionOptions> dimensionOptionsRegistry) {
MaterialRules.MaterialRule[] rulesType = new MaterialRules.MaterialRule[0];

for (World world : worlds.values()) {
DimensionOptions dimensionOptions = null;
SurfaceRuleCollector surfaceRuleCollector = null;
Optional<RegistryKey<DimensionType>> dimensionKey = world.getDimensionEntry().getKey();

if (dimensionKey.isPresent()) {
if (DimensionTypes.OVERWORLD.equals(dimensionKey.get())) {
dimensionOptions = dimensionOptionsRegistry.get(DimensionOptions.OVERWORLD);
surfaceRuleCollector = SurfaceRuleCollector.OVERWORLD;
} else if (DimensionTypes.THE_NETHER.equals(dimensionKey.get())) {
dimensionOptions = dimensionOptionsRegistry.get(DimensionOptions.NETHER);
surfaceRuleCollector = SurfaceRuleCollector.NETHER;
} else if (DimensionTypes.THE_END.equals(dimensionKey.get())) {
dimensionOptions = dimensionOptionsRegistry.get(DimensionOptions.END);
surfaceRuleCollector = SurfaceRuleCollector.END;
}
}

// TODO: Consider whether we need to guard against modifying the same ChunkGeneratorSettings more than once...
if (dimensionOptions != null && surfaceRuleCollector.getRuleCount() > 0) {
ChunkGenerator chunkGenerator = dimensionOptions.chunkGenerator();
//ChunkGenerator chunkGenerator = ((ServerChunkManager) world.getChunkManager()).threadedAnvilChunkStorage.chunkGenerator;
if (chunkGenerator instanceof NoiseChunkGenerator noiseChunkGenerator) {
ChunkGeneratorSettings chunkGeneratorSettings = noiseChunkGenerator.getSettings().value();

((MixinChunkGeneratorSettings)(Object) chunkGeneratorSettings).biolith$setSurfaceRule(
MaterialRules.sequence(Streams.concat(
Arrays.stream(surfaceRuleCollector.getAll()),
Stream.of(chunkGeneratorSettings.surfaceRule()))
.toList().toArray(rulesType)));
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.terraformersmc.biolith.impl.mixin;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.datafixers.util.Either;
import com.mojang.datafixers.util.Pair;
import com.terraformersmc.biolith.impl.biome.BiolithFittestNodes;
import com.terraformersmc.biolith.impl.biome.BiomeCoordinator;
import com.terraformersmc.biolith.impl.compat.BiolithCompat;
import com.terraformersmc.biolith.impl.compat.VanillaCompat;
import com.terraformersmc.biolith.impl.platform.Services;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.BiomeSource;
import net.minecraft.world.biome.source.MultiNoiseBiomeSource;
import net.minecraft.world.biome.source.MultiNoiseBiomeSourceParameterList;
import net.minecraft.world.biome.source.util.MultiNoiseUtil;
import net.minecraft.world.dimension.DimensionTypes;
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.CallbackInfoReturnable;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

// Inject before TerraBlender so we can ensure our tree search and placement overrides get used.
@Mixin(value = MultiNoiseBiomeSource.class, priority = 900)
public abstract class MixinMultiNoiseBiomeSource extends BiomeSource {
@Shadow
protected abstract MultiNoiseUtil.Entries<RegistryEntry<Biome>> getBiomeEntries();

private MultiNoiseUtil.Entries<RegistryEntry<Biome>> biolith$biomeEntries;

// Inject noise points the first time somebody requests them.
@WrapOperation(
method = "getBiomeEntries",
at = @At(value = "INVOKE", target = "Lcom/mojang/datafixers/util/Either;map(Ljava/util/function/Function;Ljava/util/function/Function;)Ljava/lang/Object;")
)
@SuppressWarnings("unused")
private Object biolith$injectEntries(Either<MultiNoiseUtil.Entries<RegistryEntry<Biome>>, RegistryEntry<MultiNoiseBiomeSourceParameterList>> instance, Function<MultiNoiseUtil.Entries<RegistryEntry<Biome>>, MultiNoiseUtil.Entries<RegistryEntry<Biome>>> leftMap, Function<RegistryEntry<MultiNoiseBiomeSourceParameterList>, MultiNoiseUtil.Entries<RegistryEntry<Biome>>> rightMap, Operation<Object> original) {
synchronized (this) {
// Only compute this once, since our version is more expensive than Mojang's.
if (biolith$biomeEntries == null) {
// Mojang does the exact same cast on the return of this operation.
//noinspection unchecked
MultiNoiseUtil.Entries<RegistryEntry<Biome>> originalEntries =
(MultiNoiseUtil.Entries<RegistryEntry<Biome>>) original.call(instance, leftMap, rightMap);

if (this.biolith$getDimensionType().matchesKey(DimensionTypes.OVERWORLD)) {
List<Pair<MultiNoiseUtil.NoiseHypercube, RegistryEntry<Biome>>> parameterList = new ArrayList<>(256);

// Remove any biomes matching removals
originalEntries.getEntries().stream()
.filter(BiomeCoordinator.OVERWORLD::removalFilter)
.forEach(parameterList::add);

// Add all biomes from additions, replacements, and sub-biome requests
BiomeCoordinator.OVERWORLD.writeBiomeEntries(parameterList::add);

biolith$biomeEntries = new MultiNoiseUtil.Entries<>(parameterList);
} else if (this.biolith$getDimensionType().matchesKey(DimensionTypes.THE_NETHER)) {
List<Pair<MultiNoiseUtil.NoiseHypercube, RegistryEntry<Biome>>> parameterList = new ArrayList<>(64);

// Remove any biomes matching removals
originalEntries.getEntries().stream()
.filter(BiomeCoordinator.NETHER::removalFilter)
.forEach(parameterList::add);

// Add all biomes from additions, replacements, and sub-biome requests
BiomeCoordinator.NETHER.writeBiomeEntries(parameterList::add);

biolith$biomeEntries = new MultiNoiseUtil.Entries<>(parameterList);
} else {
biolith$biomeEntries = originalEntries;
}
}
} // synchronized (this)

return biolith$biomeEntries;
}

// We calculate the vanilla/datapack biome, then we apply any overlays.
@Inject(method = "getBiome", at = @At("HEAD"), cancellable = true)
private void biolith$getBiome(int x, int y, int z, MultiNoiseUtil.MultiNoiseSampler noise, CallbackInfoReturnable<RegistryEntry<Biome>> cir) {
MultiNoiseUtil.NoiseValuePoint noisePoint = noise.sample(x, y, z);
BiolithFittestNodes<RegistryEntry<Biome>> fittestNodes = null;

// Find the biome via TerraBlender if available.
if (BiolithCompat.COMPAT_TERRABLENDER) {
fittestNodes = Services.PLATFORM.getTerraBlenderCompat().getBiome(x, y, z, noisePoint, getBiomeEntries());
}

// Find the biome via Vanilla (including datapacks) if none was provided by TerraBlender.
if (fittestNodes == null) {
fittestNodes = VanillaCompat.getBiome(noisePoint, getBiomeEntries());
}

// Apply biome overlays.
if (this.biolith$getDimensionType().matchesKey(DimensionTypes.OVERWORLD)) {
cir.setReturnValue(BiomeCoordinator.OVERWORLD.getReplacement(x, y, z, noisePoint, fittestNodes));
} else if (this.biolith$getDimensionType().matchesKey(DimensionTypes.THE_NETHER)) {
cir.setReturnValue(BiomeCoordinator.NETHER.getReplacement(x, y, z, noisePoint, fittestNodes));
} else {
cir.setReturnValue(fittestNodes.ultimate().value);
}
}

@Override
public MultiNoiseUtil.Entries<RegistryEntry<Biome>> biolith$getBiomeEntries() {
return biolith$biomeEntries;
}
}
Loading

0 comments on commit d34921a

Please sign in to comment.