Skip to content

Commit

Permalink
Test
Browse files Browse the repository at this point in the history
  • Loading branch information
shartte committed Sep 1, 2024
1 parent 93b8048 commit de0a00b
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 148 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package net.neoforged.neoforge.coremods;

import cpw.mods.modlauncher.api.ITransformer;
import cpw.mods.modlauncher.api.ITransformerVotingContext;
import cpw.mods.modlauncher.api.TargetType;
import cpw.mods.modlauncher.api.TransformerVoteResult;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

/**
* Redirect calls to one method to another.
*/
public class MethodRedirector implements ITransformer<ClassNode> {
private static final Logger LOG = LoggerFactory.getLogger(MethodRedirector.class);
private final Map<String, List<MethodRedirection>> redirectionsByClass = new HashMap<>();
private final Set<Target<ClassNode>> targets = new HashSet<>();

private static final List<MethodRedirection> REDIRECTIONS = List.of(
new MethodRedirection(
Opcodes.INVOKEVIRTUAL,
"finalizeSpawn",
"(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/MobSpawnType;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;",
"finalize_spawn_targets.json",
methodInsnNode -> new MethodInsnNode(
Opcodes.INVOKESTATIC,
"net/neoforged/neoforge/event/EventHooks",
"finalizeMobSpawn",
"(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/MobSpawnType;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;",
false)
)
);

public MethodRedirector() {
for (var redirection : REDIRECTIONS) {
var targetClassNames = CoremodUtils.loadResource(redirection.targetClassListFile, String[].class);
for (var targetClassName : targetClassNames) {
targets.add(Target.targetClass(targetClassName));
var redirections = redirectionsByClass.computeIfAbsent(targetClassName, s -> new ArrayList<>());
redirections.add(redirection);
}
}
}

@Override
public TargetType<ClassNode> getTargetType() {
return TargetType.CLASS;
}

@Override
public Set<Target<ClassNode>> targets() {
return targets;
}

@Override
public ClassNode transform(ClassNode classNode, ITransformerVotingContext votingContext) {
var redirections = redirectionsByClass.getOrDefault(classNode.name, Collections.emptyList());

var methods = classNode.methods;
for (var i = 0; i < methods.size(); i++) {
var instr = methods.get(i).instructions;
for (var ix = 0; ix < instr.size(); ix++) {
var node = instr.get(ix);
if (node instanceof MethodInsnNode methodInsnNode) {
for (var redirection : redirections) {
if (redirection.invokeOpCode == methodInsnNode.getOpcode()
&& redirection.methodName.equals(methodInsnNode.name)
&& redirection.methodDescriptor.equals(methodInsnNode.desc)) {
// Found a match for the target method
instr.set(
methodInsnNode,
redirection.redirector.apply(methodInsnNode)
);
}
}
}
}
}
return classNode;
}

@Override
public TransformerVoteResult castVote(ITransformerVotingContext context) {
return TransformerVoteResult.YES;
}

private record MethodRedirection(
int invokeOpCode,
String methodName,
String methodDescriptor,
String targetClassListFile,
Function<MethodInsnNode, MethodInsnNode> redirector
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,28 @@
import cpw.mods.modlauncher.api.ITransformer;
import net.neoforged.neoforgespi.coremod.ICoreMod;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class NeoForgeCoreMod implements ICoreMod {
@Override
public Iterable<? extends ITransformer<?>> getTransformers() {
List<FieldToMethod> fieldToMethods = new java.util.ArrayList<>();
fieldToMethods.add(new FieldToMethod("net.minecraft.world.level.biome.Biome", Map.of(
List<ITransformer<?>> transformers = new ArrayList<>();
transformers.add(new ReplaceFieldWithGetterAccess("net.minecraft.world.level.biome.Biome", Map.of(
"climateSettings", "getModifiedClimateSettings",
"specialEffects", "getModifiedSpecialEffects"
)));
fieldToMethods.add(new FieldToMethod("net.minecraft.world.level.levelgen.structure.Structure", Map.of(
transformers.add(new ReplaceFieldWithGetterAccess("net.minecraft.world.level.levelgen.structure.Structure", Map.of(
"settings", "getModifiedStructureSettings"
)));
fieldToMethods.add(new FieldToMethod("net.minecraft.world.level.block.FlowerPotBlock", Map.of(
transformers.add(new ReplaceFieldWithGetterAccess("net.minecraft.world.level.block.FlowerPotBlock", Map.of(
"potted", "getPotted"
)));

return fieldToMethods;
transformers.add(new MethodRedirector());
transformers.addAll(ReplaceFieldComparisonWithInstanceOf.loadAll());

return transformers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
* as {@code itemstack.getItem() instanceof CrossbowItem}.
* This transformer targets a set of methods to replace the occurrence of a single field-comparison.
*/
public class FieldToInstanceof implements ITransformer<MethodNode> {
private static final Logger LOG = LoggerFactory.getLogger(FieldToInstanceof.class);
public class ReplaceFieldComparisonWithInstanceOf implements ITransformer<MethodNode> {
private static final Logger LOG = LoggerFactory.getLogger(ReplaceFieldComparisonWithInstanceOf.class);

private final Set<Target<MethodNode>> targets;
private final String fieldOwner;
Expand All @@ -38,19 +38,19 @@ public class FieldToInstanceof implements ITransformer<MethodNode> {
* @param replacementClassName Reference comparisons against {@code fieldName} in {@code fieldOwner} are replaced
* by instanceof checks against this class.
*/
public FieldToInstanceof(Set<Target<MethodNode>> methods, String fieldOwner, String fieldName, String replacementClassName) {
public ReplaceFieldComparisonWithInstanceOf(Set<Target<MethodNode>> methods, String fieldOwner, String fieldName, String replacementClassName) {
this.targets = Set.copyOf(methods);

this.fieldOwner = fieldOwner;
this.fieldName = fieldName;
this.replacementClassName = replacementClassName;
}

public static List<ITransformer<?>> load() {
public static List<ITransformer<?>> loadAll() {
@SuppressWarnings("unchecked") var dataMap = (Map<String, FieldToInstanceofData>) CoremodUtils.loadResource("field_to_instanceof.json", TypeToken.getParameterized(Map.class, String.class, FieldToInstanceofData.class));

return dataMap.values().stream()
.<ITransformer<?>>map(data -> new FieldToInstanceof(
.<ITransformer<?>>map(data -> new ReplaceFieldComparisonWithInstanceOf(
data.targets.stream().map(
targetData -> Target.targetMethod(targetData.owner, targetData.name, targetData.desc)
).collect(Collectors.toSet()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
* same type as the field.
* If no methodName is passed, any method matching the described signature will be used as callable method.
*/
public class FieldToMethod implements ITransformer<ClassNode> {
public class ReplaceFieldWithGetterAccess implements ITransformer<ClassNode> {
private final Map<String, String> fieldToMethod;
private final Set<Target<ClassNode>> targets;

public FieldToMethod(String className, Map<String, String> fieldToMethod) {
public ReplaceFieldWithGetterAccess(String className, Map<String, String> fieldToMethod) {
this.targets = Set.of(Target.targetClass(className));
this.fieldToMethod = fieldToMethod;
}
Expand Down

This file was deleted.

This file was deleted.

4 changes: 3 additions & 1 deletion projects/neoforge/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ dependencies {
}

userdevTestImplementation("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}")
jarJar(project(":neoforge-coremods"))
implementation(jarJar(project(":neoforge-coremods")))
}

runTypes {
Expand Down Expand Up @@ -208,6 +208,8 @@ runs {
}

runs.configureEach { it ->
modSources.add project(":neoforge-coremods").sourceSets.main

final File gameDir = project.file("run/${it.name}") as File
gameDir.mkdirs();

Expand Down
2 changes: 2 additions & 0 deletions tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugins {

def neoforgeProject = project(':neoforge')
def testframeworkProject = project(':testframework')
def coremodsProject = project(':neoforge-coremods')

repositories {
mavenLocal()
Expand Down Expand Up @@ -98,6 +99,7 @@ runs {
runs.configureEach {
dependsOn(neoforgeProject.runtime.assets, neoforgeProject.runtime.natives)
modSource neoforgeProject.sourceSets.main
modSource coremodsProject.sourceSets.main
modSource testframeworkProject.sourceSets.main
}

Expand Down
Loading

0 comments on commit de0a00b

Please sign in to comment.