Fabric/Quilt fertilization support

This commit is contained in:
Zoë
2022-07-11 21:49:16 -07:00
parent ba6d4649c6
commit 50377a1b89
20 changed files with 342 additions and 32 deletions

View File

@@ -29,16 +29,18 @@ object Versions {
object Mod {
const val mixin = "0.11.2+mixin.0.8.5"
const val minecraft = "1.19"
const val yarn = "$minecraft+build.1"
const val fabricLoader = "0.14.2"
const val minecraftGudAsm = "v0.3.1"
const val architecuryLoom = "0.12.0.290"
const val architecturyPlugin = "3.4-SNAPSHOT"
const val loomQuiltflower = "1.7.1"
const val lazyDfu = "0.1.2"
}

View File

@@ -3,7 +3,6 @@ package com.dfsek.terra.bukkit.nms.v1_19_R1.config;
import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.List;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.Music;
import net.minecraft.sounds.SoundEvent;
@@ -15,6 +14,9 @@ import net.minecraft.world.level.biome.Biome.Precipitation;
import net.minecraft.world.level.biome.Biome.TemperatureModifier;
import net.minecraft.world.level.biome.BiomeSpecialEffects.GrassColorModifier;
import net.minecraft.world.level.biome.MobSpawnSettings;
import java.util.List;
import com.dfsek.terra.api.properties.Properties;

View File

@@ -12,6 +12,12 @@ architectury {
dependencies {
shadedApi(project(":common:implementation:base"))
annotationProcessor("net.fabricmc:sponge-mixin:${Versions.Mod.mixin}")
annotationProcessor("dev.architectury:architectury-loom:${Versions.Mod.architecuryLoom}")
modImplementation("com.github.the-glitch-network:minecraft-gudasm:${Versions.Mod.minecraftGudAsm}")
include("com.github.the-glitch-network:minecraft-gudasm:${Versions.Mod.minecraftGudAsm}")
implementation(project(path = ":platforms:mixin-common", configuration = "namedElements")) { isTransitive = false }
"developmentFabric"(project(path = ":platforms:mixin-common", configuration = "namedElements")) { isTransitive = false }
shaded(project(path = ":platforms:mixin-common", configuration = "transformProductionFabric")) { isTransitive = false }

View File

@@ -0,0 +1,19 @@
package com.dfsek.terra.fabric;
import com.dfsek.terra.fabric.util.FabricLoaderUtil;
import net.gudenau.minecraft.asm.api.v1.AsmInitializer;
import net.gudenau.minecraft.asm.api.v1.AsmRegistry;
import com.dfsek.terra.lifecycle.asm.FertilizableASM;
import com.dfsek.terra.lifecycle.util.LoaderUtil;
import com.dfsek.terra.quilt.util.QuiltLoaderUtil;
public class FabricASMEntryPoint implements AsmInitializer {
@Override
public void onInitializeAsm() {
LoaderUtil.INSTANCE = new FabricLoaderUtil();
AsmRegistry.getInstance().registerTransformer(new FertilizableASM());
}
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.fabric.util;
import com.dfsek.terra.lifecycle.util.LoaderUtil;
import net.fabricmc.loader.api.FabricLoader;
public class FabricLoaderUtil extends LoaderUtil {
@Override
public String mapClassName(String namespace, String className) {
return FabricLoader.getInstance().getMappingResolver().mapClassName(namespace, className);
}
@Override
public String mapMethodName(String namespace, String owner, String name, String descriptor) {
return FabricLoader.getInstance().getMappingResolver().mapMethodName(namespace, owner, name, descriptor);
}
}

View File

@@ -18,6 +18,9 @@
"entrypoints": {
"main": [
"com.dfsek.terra.fabric.FabricEntryPoint"
],
"gud_asm": [
"com.dfsek.terra.fabric.FabricASMEntryPoint"
]
},
"mixins": [

View File

@@ -15,44 +15,52 @@ import net.minecraft.world.biome.BiomeEffects.GrassColorModifier;
import net.minecraft.world.biome.BiomeParticleConfig;
import net.minecraft.world.biome.SpawnSettings;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.dfsek.terra.api.properties.Properties;
import com.dfsek.terra.api.structure.configured.ConfiguredStructure;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class VanillaBiomeProperties implements ConfigTemplate, Properties {
@Value("minecraft.fertilizables")
@Default
private Map<Identifier, ProbabilityCollection<ConfiguredStructure>> fertilizables = Collections.emptyMap();
@Value("minecraft.tags")
@Default
private List<Identifier> tags = null;
private List<Identifier> tags = Collections.emptyList();
@Value("minecraft.colors.grass")
@Default
private Integer grassColor = null;
private Integer grassColor = 0;
@Value("minecraft.colors.fog")
@Default
private Integer fogColor = null;
private Integer fogColor = 0;
@Value("minecraft.colors.water")
@Default
private Integer waterColor = null;
private Integer waterColor = 0;
@Value("minecraft.colors.water-fog")
@Default
private Integer waterFogColor = null;
private Integer waterFogColor = 0;
@Value("minecraft.colors.foliage")
@Default
private Integer foliageColor = null;
private Integer foliageColor = 0;
@Value("minecraft.colors.sky")
@Default
private Integer skyColor = null;
private Integer skyColor = 0;
@Value("minecraft.colors.modifier")
@Default
private GrassColorModifier grassColorModifier = null;
private GrassColorModifier grassColorModifier = GrassColorModifier.NONE;
@Value("minecraft.particles")
@Default
@@ -60,19 +68,19 @@ public class VanillaBiomeProperties implements ConfigTemplate, Properties {
@Value("minecraft.climate.precipitation")
@Default
private Precipitation precipitation = null;
private Precipitation precipitation = Precipitation.NONE;
@Value("minecraft.climate.temperature")
@Default
private Float temperature = null;
private Float temperature = 0.0f;
@Value("minecraft.climate.temperature-modifier")
@Default
private TemperatureModifier temperatureModifier = null;
private TemperatureModifier temperatureModifier = TemperatureModifier.NONE;
@Value("minecraft.climate.downfall")
@Default
private Float downfall = null;
private Float downfall = 0.0f;
@Value("minecraft.sound.loop-sound.sound")
@Default
@@ -92,12 +100,16 @@ public class VanillaBiomeProperties implements ConfigTemplate, Properties {
@Value("minecraft.spawning")
@Default
private SpawnSettings spawnSettings = null;
private SpawnSettings spawnSettings = SpawnSettings.INSTANCE;
@Value("minecraft.villager-type")
@Default
private VillagerType villagerType = null;
public Map<Identifier, ProbabilityCollection<ConfiguredStructure>> getFertilizables() {
return fertilizables;
}
public List<Identifier> getTags() {
return tags;
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.mod.mixin;
import net.minecraft.block.Fertilizable;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(Fertilizable.class)
public class FertilizableMixin {
}

View File

@@ -4,16 +4,21 @@ import net.minecraft.tag.TagKey;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryEntry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.village.VillagerType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.structure.configured.ConfiguredStructure;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.mod.CommonPlatform;
import com.dfsek.terra.mod.config.ProtoPlatformBiome;
@@ -24,6 +29,12 @@ import com.dfsek.terra.mod.mixin.access.VillagerTypeAccessor;
public class BiomeUtil {
private static final Logger logger = LoggerFactory.getLogger(BiomeUtil.class);
public static final Map<RegistryEntry<net.minecraft.world.biome.Biome>, Map<Identifier, ProbabilityCollection<ConfiguredStructure>>>
TERRA_BIOME_FERTILIZABLE_MAP = new HashMap<>();
public static final Map<TagKey<net.minecraft.world.biome.Biome>, List<Identifier>>
TERRA_BIOME_TAG_MAP = new HashMap<>();
public static void registerBiomes() {
logger.info("Registering biomes...");
CommonPlatform.get().getConfigRegistry().forEach(pack -> { // Register all Terra biomes.
@@ -55,20 +66,22 @@ public class BiomeUtil {
protected static void registerBiome(Biome biome, ConfigPack pack,
com.dfsek.terra.api.registry.key.RegistryKey id) {
VanillaBiomeProperties vanillaBiomeProperties = biome.getContext().get(VanillaBiomeProperties.class);
net.minecraft.world.biome.Biome minecraftBiome = MinecraftUtil.createBiome(vanillaBiomeProperties);
Identifier identifier = new Identifier("terra", MinecraftUtil.createBiomeID(pack, id));
biome.setPlatformBiome(new ProtoPlatformBiome(identifier, registerBiome(identifier, minecraftBiome)));
Map villagerMap = VillagerTypeAccessor.getBiomeTypeToIdMap();
villagerMap.put(RegistryKey.of(Registry.BIOME_KEY, identifier),
Objects.requireNonNullElse(vanillaBiomeProperties.getVillagerType(), VillagerType.PLAINS));
TERRA_BIOME_FERTILIZABLE_MAP.put(RegistryEntry.of(minecraftBiome), vanillaBiomeProperties.getFertilizables());
for(Identifier tag : vanillaBiomeProperties.getTags()) {
MinecraftUtil.TERRA_BIOME_TAG_MAP.getOrDefault(TagKey.of(Registry.BIOME_KEY, tag), new ArrayList<>()).add(identifier);
TERRA_BIOME_TAG_MAP.getOrDefault(TagKey.of(Registry.BIOME_KEY, tag), new ArrayList<>()).add(identifier);
}
}
}

View File

@@ -0,0 +1,34 @@
package com.dfsek.terra.mod.util;
import com.dfsek.terra.api.util.Rotation;
import com.dfsek.terra.api.util.vector.Vector3Int;
import com.dfsek.terra.api.world.WritableWorld;
import net.minecraft.block.BlockState;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.util.registry.Registry;
import java.util.Map;
import com.dfsek.terra.api.structure.configured.ConfiguredStructure;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
public class FertilizableUtil {
public static boolean grow(ServerWorld world, Random random, BlockPos pos, BlockState state) {
Map<Identifier, ProbabilityCollection<ConfiguredStructure>> fertilizables = BiomeUtil.TERRA_BIOME_FERTILIZABLE_MAP.get(world.getBiome(pos));
if (fertilizables != null) {
ProbabilityCollection<ConfiguredStructure> probabilityCollection = fertilizables.get(Registry.BLOCK.getId(state.getBlock()));
if (probabilityCollection != null) {
ConfiguredStructure structure = probabilityCollection.get((java.util.Random) random);
structure.getStructure().get((java.util.Random) random).generate(Vector3Int.of(pos.getX(), pos.getY(), pos.getZ()), (WritableWorld) world, (java.util.Random) random, Rotation.NONE);
return true;
}
}
return false;
}
}

View File

@@ -3,7 +3,6 @@ package com.dfsek.terra.mod.util;
import net.minecraft.block.entity.LootableContainerBlockEntity;
import net.minecraft.block.entity.MobSpawnerBlockEntity;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.tag.TagKey;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
@@ -17,10 +16,7 @@ import net.minecraft.world.biome.GenerationSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -35,8 +31,6 @@ import com.dfsek.terra.mod.config.VanillaBiomeProperties;
public final class MinecraftUtil {
public static final Logger logger = LoggerFactory.getLogger(MinecraftUtil.class);
public static final Map<TagKey<Biome>, List<Identifier>>
TERRA_BIOME_TAG_MAP = new HashMap<>();
private MinecraftUtil() {

View File

@@ -56,7 +56,7 @@ public final class TagUtil {
logger.info("who let this data drive?");
Map<TagKey<Biome>, List<RegistryEntry<Biome>>> collect = tagsToMutableMap(registry);
MinecraftUtil.TERRA_BIOME_TAG_MAP.forEach((tag, biomeList) -> {
BiomeUtil.TERRA_BIOME_TAG_MAP.forEach((tag, biomeList) -> {
collect.getOrDefault(tag, new ArrayList<>())
.addAll(biomeList.stream()
.map(registry::getOrEmpty)

View File

@@ -11,6 +11,11 @@ dependencies {
annotationProcessor("net.fabricmc:sponge-mixin:${Versions.Mod.mixin}")
annotationProcessor("dev.architectury:architectury-loom:${Versions.Mod.architecuryLoom}")
modImplementation("com.github.the-glitch-network:minecraft-gudasm:${Versions.Mod.minecraftGudAsm}") {
exclude("net.fabricmc")
exclude("net.fabricmc.fabric-api")
}
implementation(project(path = ":platforms:mixin-common", configuration = "namedElements")) { isTransitive = false }
minecraft("com.mojang:minecraft:${Versions.Mod.minecraft}")

View File

@@ -0,0 +1,81 @@
package com.dfsek.terra.lifecycle.asm;
import com.dfsek.terra.mod.util.FertilizableUtil;
import net.gudenau.minecraft.asm.api.v1.Identifier;
import net.gudenau.minecraft.asm.api.v1.Transformer;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import java.io.IOException;
import com.dfsek.terra.lifecycle.util.ASMUtil;
import com.dfsek.terra.lifecycle.util.LoaderUtil;
import org.objectweb.asm.tree.VarInsnNode;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
public class FertilizableASM implements Transformer {
private static String fertilizableClassName = LoaderUtil.INSTANCE.mapClassName("intermediary", "net.minecraft.class_2256");
private static String fertilizableGrowMethodSignatureBase = String.format("(L%1$s;L%2$s;L%3$s;L%4$s;)",
LoaderUtil.INSTANCE.mapClassName("intermediary", "net.minecraft.class_3218").replace(".", "/"),
LoaderUtil.INSTANCE.mapClassName("intermediary", "net.minecraft.class_5819").replace(".", "/"),
LoaderUtil.INSTANCE.mapClassName("intermediary", "net.minecraft.class_2338").replace(".", "/"),
LoaderUtil.INSTANCE.mapClassName("intermediary", "net.minecraft.class_2680").replace(".", "/"));
private static String fertilizableGrowMethodSignature = fertilizableGrowMethodSignatureBase + "V";
private static String fertilizableGrowMethodName = LoaderUtil.INSTANCE.mapMethodName("intermediary", "net.minecraft.class_2256", "method_9652", "(Lnet/minecraft/class_3218;Lnet/minecraft/class_5819;Lnet/minecraft/class_2338;Lnet/minecraft/class_2680;)V");
@Override
public Identifier getName() {
return new Identifier("terra", "asm_test");
}
@Override
public boolean handlesClass(String name, String transformedName) {
return true;
}
@Override
public boolean transform(ClassNode classNode, Flags flags) {
try {
if (ASMUtil.inheritsFrom(classNode, fertilizableClassName.replace(".", "/"))) {
for (MethodNode method : classNode.methods) {
if (method.name.equals(fertilizableGrowMethodName)) {
if ((method.access & ACC_STATIC) == 0) {
if(method.desc.equals(fertilizableGrowMethodSignature)) {
InsnList list = new InsnList();
list.add(new VarInsnNode(Opcodes.ALOAD, 1));
list.add(new VarInsnNode(Opcodes.ALOAD, 2));
list.add(new VarInsnNode(Opcodes.ALOAD, 3));
list.add(new VarInsnNode(Opcodes.ALOAD, 4));
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, FertilizableUtil.class.getName().replace(".", "/"), "grow", fertilizableGrowMethodSignatureBase + "Z", false));
LabelNode jmp = new LabelNode();
list.add(new JumpInsnNode(Opcodes.IFNE, jmp));
list.add(new InsnNode(Opcodes.RETURN));
list.add(jmp);
method.instructions.insertBefore(method.instructions.getFirst(), list);
flags.requestFrames();
return true;
}
}
}
}
}
} catch(IOException e) {
throw new RuntimeException(e);
}
return false;
}
}

View File

@@ -0,0 +1,56 @@
package com.dfsek.terra.lifecycle.util;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import java.io.IOException;
import java.util.WeakHashMap;
import static org.objectweb.asm.ClassReader.SKIP_CODE;
import static org.objectweb.asm.ClassReader.SKIP_DEBUG;
import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
public class ASMUtil {
static final ThreadLocal<WeakHashMap<String, WeakHashMap<String, Boolean>>> INHERITANCE_CACHE = new ThreadLocal<>();
public static boolean inheritsFrom(ClassNode classNode, String interfaceName) throws IOException {
if (INHERITANCE_CACHE.get() == null) {
INHERITANCE_CACHE.set(new WeakHashMap<>());
}
return inheritsFromInternal(classNode, interfaceName);
}
protected static boolean inheritsFromInternal(ClassNode classNode, String interfaceName) throws IOException {
if (INHERITANCE_CACHE.get().containsKey(classNode.name)) {
if (INHERITANCE_CACHE.get().get(classNode.name).containsKey(interfaceName)) {
return INHERITANCE_CACHE.get().get(classNode.name).get(interfaceName);
}
}
for (String parent : classNode.interfaces) {
if (checkClass(parent, interfaceName)) {
return true;
}
}
if (checkClass(classNode.superName, interfaceName)) {
return true;
}
INHERITANCE_CACHE.get().getOrDefault(classNode.name, new WeakHashMap<>()).put(interfaceName, false);
return false;
}
protected static boolean checkClass(String name, String interfaceName) throws IOException {
if (name.startsWith("java/")) {
return false;
}
boolean isClass = name.equals(interfaceName);
INHERITANCE_CACHE.get().getOrDefault(name, new WeakHashMap<>()).put(interfaceName, isClass);
if (isClass) {
return true;
}
ClassNode node = new ClassNode();
new ClassReader(name).accept(node, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
return inheritsFromInternal(node, interfaceName);
}
}

View File

@@ -0,0 +1,9 @@
package com.dfsek.terra.lifecycle.util;
public abstract class LoaderUtil {
public static LoaderUtil INSTANCE;
public abstract String mapClassName(String namespace, String className);
public abstract String mapMethodName(String namespace, String owner, String name, String descriptor);
}

View File

@@ -15,6 +15,14 @@ dependencies {
annotationProcessor("net.fabricmc:sponge-mixin:${Versions.Mod.mixin}")
annotationProcessor("dev.architectury:architectury-loom:${Versions.Mod.architecuryLoom}")
modImplementation("com.github.the-glitch-network:minecraft-gudasm:${Versions.Mod.minecraftGudAsm}") {
exclude("net.fabricmc")
exclude("net.fabricmc.fabric-api")
}
include("com.github.the-glitch-network:minecraft-gudasm:${Versions.Mod.minecraftGudAsm}") {
exclude("net.fabricmc")
exclude("net.fabricmc.fabric-api")
}
implementation(project(path = ":platforms:mixin-common", configuration = "namedElements")) { isTransitive = false }
"developmentQuilt"(project(path = ":platforms:mixin-common", configuration = "namedElements")) { isTransitive = false }

View File

@@ -0,0 +1,17 @@
package com.dfsek.terra.quilt;
import net.gudenau.minecraft.asm.api.v1.AsmInitializer;
import net.gudenau.minecraft.asm.api.v1.AsmRegistry;
import com.dfsek.terra.lifecycle.asm.FertilizableASM;
import com.dfsek.terra.lifecycle.util.LoaderUtil;
import com.dfsek.terra.quilt.util.QuiltLoaderUtil;
public class QuiltASMEntryPoint implements AsmInitializer {
@Override
public void onInitializeAsm() {
LoaderUtil.INSTANCE = new QuiltLoaderUtil();
AsmRegistry.getInstance().registerTransformer(new FertilizableASM());
}
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.quilt.util;
import com.dfsek.terra.lifecycle.util.LoaderUtil;
import org.quiltmc.loader.api.QuiltLoader;
public class QuiltLoaderUtil extends LoaderUtil {
@Override
public String mapClassName(String namespace, String className) {
return QuiltLoader.getMappingResolver().mapClassName(namespace, className);
}
@Override
public String mapMethodName(String namespace, String owner, String name, String descriptor) {
return QuiltLoader.getMappingResolver().mapMethodName(namespace, owner, name, descriptor);
}
}

View File

@@ -26,6 +26,9 @@
],
"pre_launch": [
"com.dfsek.terra.quilt.QuiltPreLaunchEntryPoint"
],
"gud_asm": [
"com.dfsek.terra.quilt.QuiltASMEntryPoint"
]
},
"depends": [