Skip to content

Commit

Permalink
actual completely safe event registration this time with less reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
Lyfts committed Oct 9, 2024
1 parent c0e4797 commit 32f24a7
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 262 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/gtnewhorizon/gtnhlib/CommonProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
public class CommonProxy {

public void preInit(FMLPreInitializationEvent event) {
AutoEventBus.setDataTable(event.getAsmData());
AutoEventBus.init(event.getAsmData());
GTNHLib.info("GTNHLib version " + Tags.VERSION + " loaded.");
}

Expand Down
39 changes: 35 additions & 4 deletions src/main/java/com/gtnewhorizon/gtnhlib/core/GTNHLibCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@
import org.spongepowered.asm.launch.GlobalProperties;
import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper;

import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.gtnewhorizon.gtnhlib.Tags;
import com.gtnewhorizon.gtnhlib.core.transformer.EventBusSubTransformer;
import com.gtnewhorizon.gtnhlib.mixins.Mixins;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;

import cpw.mods.fml.common.DummyModContainer;
import cpw.mods.fml.common.LoadController;
import cpw.mods.fml.common.ModMetadata;
import cpw.mods.fml.common.event.FMLConstructionEvent;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;

Expand All @@ -20,26 +28,38 @@
"com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager",
"com.gtnewhorizon.gtnhlib.client.renderer.CapturingTessellator" })
@IFMLLoadingPlugin.SortingIndex(-1000)
public class GTNHLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
public class GTNHLibCore extends DummyModContainer implements IFMLLoadingPlugin, IEarlyMixinLoader {

public static final String[] DEFAULT_TRANSFORMERS = new String[] {
"com.gtnewhorizon.gtnhlib.core.transformer.EventBusSubTransformer" };

public GTNHLibCore() {
super(new ModMetadata());
ModMetadata md = getMetadata();
md.autogenerated = true;
md.modId = md.name = "GTNHLib Core";
md.parent = "gtnhlib";
md.version = Tags.VERSION;
}

@Override
public String[] getASMTransformerClass() {
if (!FMLLaunchHandler.side().isClient()
|| Launch.blackboard.getOrDefault("gtnhlib.rfbPluginLoaded", Boolean.FALSE) == Boolean.TRUE) {
// Don't need any transformers if we're not on the client, or the RFB Plugin was loaded
return new String[0];
return DEFAULT_TRANSFORMERS;
}
// Directly add this to the MixinServiceLaunchWrapper tweaker's list of Tweak Classes
List<String> mixinTweakClasses = GlobalProperties.get(MixinServiceLaunchWrapper.BLACKBOARD_KEY_TWEAKCLASSES);
if (mixinTweakClasses != null) {
mixinTweakClasses.add(MixinCompatHackTweaker.class.getName());
}
return new String[0];
return DEFAULT_TRANSFORMERS;
}

@Override
public String getModContainerClass() {
return null;
return "com.gtnewhorizon.gtnhlib.core.GTNHLibCore";
}

@Override
Expand All @@ -64,4 +84,15 @@ public String getMixinConfig() {
public List<String> getMixins(Set<String> loadedCoreMods) {
return Mixins.getEarlyMixins(loadedCoreMods);
}

@Override
public boolean registerBus(EventBus bus, LoadController controller) {
bus.register(this);
return true;
}

@Subscribe
public void construct(FMLConstructionEvent event) {
EventBusSubTransformer.harvestData(event.getASMHarvestedData());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package com.gtnewhorizon.gtnhlib.core.transformer;

import static com.gtnewhorizon.gtnhlib.eventbus.EventBusUtil.DEBUG_EVENT_BUS;

import java.util.Arrays;
import java.util.List;

import net.minecraft.launchwrapper.IClassTransformer;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

import com.gtnewhorizon.gtnhlib.eventbus.EventBusSubscriber;
import com.gtnewhorizon.gtnhlib.eventbus.EventBusUtil;
import com.gtnewhorizon.gtnhlib.eventbus.MethodInfo;

import cpw.mods.fml.common.Optional;
import cpw.mods.fml.common.discovery.ASMDataTable;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.SideOnly;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;

public class EventBusSubTransformer implements IClassTransformer {

private static final Logger LOGGER = LogManager.getLogger("GTNHLib|EventBusSubTransformer");
private static final String OPTIONAL_DESC = Type.getDescriptor(Optional.Method.class);
private static final String SIDEONLY_DESC = Type.getDescriptor(SideOnly.class);
private static final String SUBSCRIBE_DESC = Type.getDescriptor(SubscribeEvent.class);
private static final String CONDITION_DESC = Type.getDescriptor(EventBusSubscriber.Condition.class);
private static final List<String> ANNOTATIONS = Arrays
.asList(OPTIONAL_DESC, SIDEONLY_DESC, SUBSCRIBE_DESC, CONDITION_DESC);
private static final String CURRENT_SIDE = FMLLaunchHandler.side().name();
private static ObjectSet<String> classesToVisit;

public static void harvestData(ASMDataTable table) {
classesToVisit = EventBusUtil.getClassesToVisit();
for (ASMDataTable.ASMData data : table.getAll(EventBusSubscriber.class.getName())) {
classesToVisit.add(data.getClassName());
}
}

@Override
public byte[] transform(String name, String transformedName, byte[] basicClass) {
if (basicClass == null) return null;

// It's either too early or this class isn't an @EventBusSubscriber
if (classesToVisit == null || !classesToVisit.contains(transformedName)) {
return basicClass;
}

final ClassReader cr = new ClassReader(basicClass);
final ClassNode cn = new ClassNode();
cr.accept(cn, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE);

// Processing all of this from the ASMDataTable is way too slow
for (MethodNode mn : cn.methods) {
Object2ObjectMap<String, AnnotationNode> usableAnnotations = getUsableAnnotations(mn.visibleAnnotations);
if (usableAnnotations.isEmpty()) continue;

if (!matchesSide(usableAnnotations.get(SIDEONLY_DESC))) {
if (DEBUG_EVENT_BUS) {
LOGGER.info("Skipping method {} due to side mismatch", transformedName);
}
continue;
}

if (usableAnnotations.containsKey(CONDITION_DESC)) {
if (mn.desc.equals("()Z")) {
EventBusUtil.getConditionsToCheck().put(transformedName, mn.name + mn.desc);
}
continue;
}

AnnotationNode subscribe = usableAnnotations.get(SUBSCRIBE_DESC);
if (subscribe == null) continue;
Object[] subscribeInfo = getSubscribeInfo(subscribe);
MethodInfo methodInfo = new MethodInfo(
transformedName,
mn.name,
mn.desc,
(Boolean) subscribeInfo[0],
(EventPriority) subscribeInfo[1]);
AnnotationNode optional = usableAnnotations.get(OPTIONAL_DESC);
if (optional != null) {
List<Object> values = optional.values;
methodInfo.setOptionalMod((String) values.get(1));
}

EventBusUtil.getMethodsToSubscribe().computeIfAbsent(transformedName, k -> new ObjectOpenHashSet<>())
.add(methodInfo);
}

return basicClass;
}

private static Object2ObjectMap<String, AnnotationNode> getUsableAnnotations(List<AnnotationNode> annotations) {
if (annotations == null) return Object2ObjectMaps.emptyMap();
Object2ObjectMap<String, AnnotationNode> usable = new Object2ObjectOpenHashMap<>();
for (AnnotationNode ann : annotations) {
if (ANNOTATIONS.contains(ann.desc)) {
usable.put(ann.desc, ann);
}
}
return usable;
}

@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean matchesSide(AnnotationNode side) {
if (side == null) return true;
for (int x = 0; x < side.values.size() - 1; x += 2) {
Object key = side.values.get(x);
Object value = side.values.get(x + 1);
if (!(key instanceof String) || !key.equals("value")) continue;
if (!(value instanceof String[]array)) continue;
if (!array[1].equals(CURRENT_SIDE)) {
return false;
}
}
return true;
}

private static Object[] getSubscribeInfo(AnnotationNode annotation) {
Object[] info = { false, EventPriority.NORMAL };
if (annotation.values == null) return info;
for (int i = 0; i < annotation.values.size() - 1; i += 2) {
Object key = annotation.values.get(i);
Object value = annotation.values.get(i + 1);
if (!(key instanceof String)) continue;
if (key.equals("receiveCanceled")) {
info[0] = value;
} else if (key.equals("priority") && value instanceof String[]array) {
info[1] = EventPriority.valueOf(array[1]);
}
}
return info;
}
}
Loading

0 comments on commit 32f24a7

Please sign in to comment.