Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport @EventBusSubscriber annotation from 1.12.2 #80

Merged
merged 7 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/main/java/com/gtnewhorizon/gtnhlib/ClientProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraftforge.common.MinecraftForge;

import com.gtnewhorizon.gtnhlib.client.model.ModelLoader;
import com.gtnewhorizon.gtnhlib.eventbus.EventBusSubscriber;
import com.gtnewhorizon.gtnhlib.util.AboveHotbarHUD;
import com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler;

import cpw.mods.fml.common.event.*;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.relauncher.Side;

@SuppressWarnings("unused")
@EventBusSubscriber(side = Side.CLIENT)
public class ClientProxy extends CommonProxy {

private static boolean modelsBaked = false;
Expand All @@ -36,7 +37,6 @@ public void init(FMLInitializationEvent event) {
@Override
public void postInit(FMLPostInitializationEvent event) {
super.postInit(event);
MinecraftForge.EVENT_BUS.register(new AnimatedTooltipHandler());

if (shouldLoadModels()) {
Minecraft.getMinecraft().refreshResources();
Expand Down Expand Up @@ -131,7 +131,7 @@ public void printMessageAboveHotbar(String message, int displayDuration, boolean
}

@SubscribeEvent
public void onTick(TickEvent.ClientTickEvent event) {
public static void onTick(TickEvent.ClientTickEvent event) {
if (!modelsBaked) {
ModelLoader.bakeModels();
modelsBaked = true;
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/gtnewhorizon/gtnhlib/CommonProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import net.minecraftforge.common.util.FakePlayer;

import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;
import com.gtnewhorizon.gtnhlib.eventbus.AutoEventBus;
import com.gtnewhorizon.gtnhlib.network.NetworkHandler;
import com.gtnewhorizon.gtnhlib.network.PacketMessageAboveHotbar;

Expand All @@ -20,10 +21,12 @@
public class CommonProxy {

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

public void init(FMLInitializationEvent event) {
AutoEventBus.registerSubscribers();
NetworkHandler.init();
ConfigurationManager.onInit();
}
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) {
Lyfts marked this conversation as resolved.
Show resolved Hide resolved
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,182 @@
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.Opcodes;
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;
}

AnnotationNode subscribe = usableAnnotations.get(SUBSCRIBE_DESC);
boolean condition = usableAnnotations.containsKey(CONDITION_DESC);
if ((mn.access & Opcodes.ACC_STATIC) == 0) {
if (!condition && subscribe != null) {
EventBusUtil.getInvalidMethods().add(
"Encountered unexpected non-static method: " + transformedName + " " + mn.name + mn.desc);
}
continue;
}

if (condition) {
if (mn.desc.equals("()Z")) {
EventBusUtil.getConditionsToCheck().put(transformedName, mn.name + mn.desc);
} else {
EventBusUtil.getInvalidMethods().add(
"Invalid condition method: " + transformedName
+ " "
+ mn.name
+ mn.desc
+ ". Condition method must have no parameters and return a boolean.");
}
continue;
}

if (subscribe == null) {
if (DEBUG_EVENT_BUS) {
LOGGER.info(
"Skipping method {} with annotations {}. No @SubscribeEvent found.",
transformedName,
usableAnnotations.keySet());
}
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));
if (DEBUG_EVENT_BUS) {
LOGGER.info(
"Found optional mod {} for method {}",
methodInfo.getOptionalMod(),
methodInfo.getKey());
}
}

EventBusUtil.getMethodsToSubscribe().computeIfAbsent(transformedName, k -> new ObjectOpenHashSet<>())
.add(methodInfo);
if (DEBUG_EVENT_BUS) {
LOGGER.info("Found subscribed method {}", methodInfo.getKey());
}
}

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