diff options
Diffstat (limited to 'src/main/java')
17 files changed, 301 insertions, 33 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index 1d0f838db..4503a63b5 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -1,5 +1,6 @@ package at.hannibal2.skyhanni +import at.hannibal2.skyhanni.api.event.SkyHanniEvents import at.hannibal2.skyhanni.config.ConfigFileType import at.hannibal2.skyhanni.config.ConfigManager import at.hannibal2.skyhanni.config.Features @@ -134,6 +135,8 @@ class SkyHanniMod { // test stuff loadModule(SkyHanniDebugsAndTests()) + SkyHanniEvents.init(modules) + Commands.init() PreInitFinishedEvent().postAndCatch() diff --git a/src/main/java/at/hannibal2/skyhanni/api/FmlEventApi.kt b/src/main/java/at/hannibal2/skyhanni/api/FmlEventApi.kt index 7af401401..784409637 100644 --- a/src/main/java/at/hannibal2/skyhanni/api/FmlEventApi.kt +++ b/src/main/java/at/hannibal2/skyhanni/api/FmlEventApi.kt @@ -15,9 +15,8 @@ object FmlEventApi { ClientDisconnectEvent().postAndCatch() } - // TODO when we have generic events, make this support generics @SubscribeEvent fun onEntityJoinWorld(event: EntityJoinWorldEvent) { - EntityEnterWorldEvent(event.entity).postAndCatch() + EntityEnterWorldEvent(event.entity).post() } } diff --git a/src/main/java/at/hannibal2/skyhanni/api/event/CancellableSkyHanniEvent.kt b/src/main/java/at/hannibal2/skyhanni/api/event/CancellableSkyHanniEvent.kt new file mode 100644 index 000000000..048630b84 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/api/event/CancellableSkyHanniEvent.kt @@ -0,0 +1,3 @@ +package at.hannibal2.skyhanni.api.event + +abstract class CancellableSkyHanniEvent : SkyHanniEvent(), SkyHanniEvent.Cancellable diff --git a/src/main/java/at/hannibal2/skyhanni/api/event/EventHandler.kt b/src/main/java/at/hannibal2/skyhanni/api/event/EventHandler.kt new file mode 100644 index 000000000..cfc8e63b8 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/api/event/EventHandler.kt @@ -0,0 +1,134 @@ +package at.hannibal2.skyhanni.api.event + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.test.command.ErrorManager +import at.hannibal2.skyhanni.utils.ChatUtils +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.chat.Text +import java.lang.invoke.LambdaMetafactory +import java.lang.invoke.MethodHandles +import java.lang.invoke.MethodType +import java.lang.reflect.Method +import java.lang.reflect.ParameterizedType +import java.util.function.Consumer + +class EventHandler<T : SkyHanniEvent> private constructor(val name: String, private val isGeneric: Boolean) { + + private val listeners: MutableList<Listener> = mutableListOf() + + private var isFrozen = false + private var canReceiveCancelled = false + + var invokeCount: Long = 0L + private set + + constructor(event: Class<T>) : this( + (event.name.split(".").lastOrNull() ?: event.name).replace("$", "."), + GenericSkyHanniEvent::class.java.isAssignableFrom(event) + ) + + fun addListener(method: Method, instance: Any, options: HandleEvent) { + if (isFrozen) throw IllegalStateException("Cannot add listener to frozen event handler") + val generic: Class<*>? = if (isGeneric) { + method.genericParameterTypes + .firstNotNullOfOrNull { it as? ParameterizedType } + ?.let { it.actualTypeArguments.firstOrNull() as? Class<*> } + ?: throw IllegalArgumentException("Generic event handler must have a generic type") + } else { + null + } + val name = "${method.declaringClass.name}.${method.name}${ + method.parameterTypes.joinTo( + StringBuilder(), + prefix = "(", + postfix = ")", + separator = ", ", + transform = Class<*>::getTypeName + ) + }" + listeners.add(Listener(name, createEventConsumer(name, instance, method), options, generic)) + } + + @Suppress("UNCHECKED_CAST") + private fun createEventConsumer(name: String, instance: Any, method: Method): Consumer<Any> { + try { + val handle = MethodHandles.lookup().unreflect(method) + return LambdaMetafactory.metafactory( + MethodHandles.lookup(), + "accept", + MethodType.methodType(Consumer::class.java, instance::class.java), + MethodType.methodType(Nothing::class.javaPrimitiveType, Object::class.java), + handle, + MethodType.methodType(Nothing::class.javaPrimitiveType, method.parameterTypes[0]) + ).target.bindTo(instance).invokeExact() as Consumer<Any> + } catch (e: Throwable) { + throw IllegalArgumentException("Method $name is not a valid consumer", e) + } + } + + fun freeze() { + isFrozen = true + listeners.sortBy { it.options.priority } + canReceiveCancelled = listeners.any { it.options.receiveCancelled } + } + + fun post(event: T, onError: ((Throwable) -> Unit)? = null): Boolean { + invokeCount++ + if (this.listeners.isEmpty()) return false + if (!isFrozen) error("Cannot invoke event on unfrozen event handler") + + if (SkyHanniEvents.isDisabledHandler(name)) return false + + var errors = 0 + + for (listener in listeners) { + if (!shouldInvoke(event, listener)) continue + try { + listener.invoker.accept(event) + } catch (throwable: Throwable) { + errors++ + if (errors <= 3) { + val errorName = throwable::class.simpleName ?: "error" + val message = "Caught an $errorName in ${listener.name} at $name: ${throwable.message}" + ErrorManager.logErrorWithData(throwable, message, ignoreErrorCache = onError != null) + } + onError?.invoke(throwable) + } + if (event.isCancelled && !canReceiveCancelled) break + } + + if (errors > 3) { + val hiddenErrors = errors - 3 + ChatUtils.chat( + Text.text( + "§c[SkyHanni/${SkyHanniMod.version}] $hiddenErrors more errors in $name are hidden!" + ) + ) + } + return event.isCancelled + } + + private fun shouldInvoke(event: SkyHanniEvent, listener: Listener): Boolean { + if (SkyHanniEvents.isDisabledInvoker(listener.name)) return false + if (listener.options.onlyOnSkyblock && !LorenzUtils.inSkyBlock) return false + if (listener.options.onlyOnIsland != IslandType.ANY && !listener.options.onlyOnIsland.isInIsland()) return false + if (event.isCancelled && !listener.options.receiveCancelled) return false + if ( + event is GenericSkyHanniEvent<*> && + listener.generic != null && + !listener.generic.isAssignableFrom(event.type) + ) { + return false + } + return true + } + + private class Listener( + val name: String, + val invoker: Consumer<Any>, + val options: HandleEvent, + val generic: Class<*>? + ) +} diff --git a/src/main/java/at/hannibal2/skyhanni/api/event/GenericSkyHanniEvent.kt b/src/main/java/at/hannibal2/skyhanni/api/event/GenericSkyHanniEvent.kt new file mode 100644 index 000000000..1b761e1ae --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/api/event/GenericSkyHanniEvent.kt @@ -0,0 +1,3 @@ +package at.hannibal2.skyhanni.api.event + +abstract class GenericSkyHanniEvent<T>(val type: Class<T>) : SkyHanniEvent(), SkyHanniEvent.Cancellable diff --git a/src/main/java/at/hannibal2/skyhanni/api/event/HandleEvent.kt b/src/main/java/at/hannibal2/skyhanni/api/event/HandleEvent.kt new file mode 100644 index 000000000..39b047a6e --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/api/event/HandleEvent.kt @@ -0,0 +1,35 @@ +package at.hannibal2.skyhanni.api.event + +import at.hannibal2.skyhanni.data.IslandType + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.FUNCTION) +annotation class HandleEvent( + /** + * If the event should only be received while on SkyBlock. + */ + val onlyOnSkyblock: Boolean = false, + + /** + * If the event should only be received while on a specific skyblock island. + */ + val onlyOnIsland: IslandType = IslandType.ANY, + + /** + * The priority of when the event will be called, lower priority will be called first, see the companion object. + */ + val priority: Int = 0, + + /** + * If the event is cancelled & receiveCancelled is true, then the method will still invoke. + */ + val receiveCancelled: Boolean = false, +) { + + companion object { + const val HIGHEST = -2 + const val HIGH = -1 + const val LOW = 1 + const val LOWEST = 2 + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvent.kt b/src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvent.kt new file mode 100644 index 000000000..4f3685c8b --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvent.kt @@ -0,0 +1,19 @@ +package at.hannibal2.skyhanni.api.event + +abstract class SkyHanniEvent protected constructor() { + + var isCancelled: Boolean = false + private set + + fun post() = SkyHanniEvents.getEventHandler(javaClass).post(this) + + fun post(onError: (Throwable) -> Unit = {}) = SkyHanniEvents.getEventHandler(javaClass).post(this, onError) + + interface Cancellable { + + fun cancel() { + val event = this as SkyHanniEvent + event.isCancelled = true + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvents.kt b/src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvents.kt new file mode 100644 index 000000000..e3dff2014 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvents.kt @@ -0,0 +1,64 @@ +package at.hannibal2.skyhanni.api.event + +import at.hannibal2.skyhanni.data.MinecraftData +import at.hannibal2.skyhanni.data.jsonobjects.repo.DisabledEventsJson +import at.hannibal2.skyhanni.events.DebugDataCollectEvent +import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.lang.reflect.Method + +@SkyHanniModule +object SkyHanniEvents { + + private val handlers: MutableMap<Class<*>, EventHandler<*>> = mutableMapOf() + private var disabledHandlers = emptySet<String>() + private var disabledHandlerInvokers = emptySet<String>() + + fun init(instances: List<Any>) { + instances.forEach { instance -> + instance.javaClass.declaredMethods.forEach { + registerMethod(it, instance) + } + } + handlers.values.forEach { it.freeze() } + } + + @Suppress("UNCHECKED_CAST") + fun <T : SkyHanniEvent> getEventHandler(event: Class<T>): EventHandler<T> = handlers.getOrPut(event) { + EventHandler(event) + } as EventHandler<T> + + fun isDisabledHandler(handler: String): Boolean = handler in disabledHandlers + fun isDisabledInvoker(invoker: String): Boolean = invoker in disabledHandlerInvokers + + @Suppress("UNCHECKED_CAST") + private fun registerMethod(method: Method, instance: Any) { + if (method.parameterCount != 1) return + val options = method.getAnnotation(HandleEvent::class.java) ?: return + val event = method.parameterTypes[0] + if (!SkyHanniEvent::class.java.isAssignableFrom(event)) return + val handler = getEventHandler(event as Class<SkyHanniEvent>) + handler.addListener(method, instance, options) + } + + @SubscribeEvent + fun onRepoLoad(event: RepositoryReloadEvent) { + val data = event.getConstant<DisabledEventsJson>("DisabledEvents") + disabledHandlers = data.disabledHandlers + disabledHandlerInvokers = data.disabledInvokers + } + + @SubscribeEvent + fun onDebug(event: DebugDataCollectEvent) { + event.title("Events") + event.addIrrelevant { + handlers.values.toMutableList() + .filter { it.invokeCount > 0 } + .sortedWith(compareBy({ -it.invokeCount }, { it.name })) + .forEach { + add("- ${it.name} (${it.invokeCount} ${it.invokeCount / (MinecraftData.totalTicks / 20)}/s)") + } + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/data/IslandType.kt b/src/main/java/at/hannibal2/skyhanni/data/IslandType.kt index ff185d5ae..4d36c1c01 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/IslandType.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/IslandType.kt @@ -26,6 +26,7 @@ enum class IslandType(val displayName: String) { MINESHAFT("Mineshaft"), NONE(""), + ANY(""), UNKNOWN("???"), ; diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/DisabledEventsJson.kt b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/DisabledEventsJson.kt new file mode 100644 index 000000000..131ea5f3c --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/DisabledEventsJson.kt @@ -0,0 +1,8 @@ +package at.hannibal2.skyhanni.data.jsonobjects.repo + +import com.google.gson.annotations.Expose + +data class DisabledEventsJson( + @Expose val disabledHandlers: Set<String> = emptySet(), + @Expose val disabledInvokers: Set<String> = emptySet(), +) diff --git a/src/main/java/at/hannibal2/skyhanni/events/entity/EntityEnterWorldEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/entity/EntityEnterWorldEvent.kt index 14283f2fe..24b887165 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/entity/EntityEnterWorldEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/entity/EntityEnterWorldEvent.kt @@ -1,6 +1,6 @@ package at.hannibal2.skyhanni.events.entity -import at.hannibal2.skyhanni.events.LorenzEvent +import at.hannibal2.skyhanni.api.event.GenericSkyHanniEvent import net.minecraft.entity.Entity -class EntityEnterWorldEvent(val entity: Entity) : LorenzEvent() +class EntityEnterWorldEvent<T : Entity>(val entity: T) : GenericSkyHanniEvent<T>(entity.javaClass) diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt index 58455cee7..6ee7979fa 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.features.combat.damageindicator import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator import at.hannibal2.skyhanni.config.features.combat.damageindicator.DamageIndicatorConfig.BossCategory import at.hannibal2.skyhanni.config.features.combat.damageindicator.DamageIndicatorConfig.NameVisibility @@ -48,6 +49,7 @@ import com.google.gson.JsonArray import net.minecraft.client.Minecraft import net.minecraft.client.entity.EntityOtherPlayerMP import net.minecraft.client.renderer.GlStateManager +import net.minecraft.entity.Entity import net.minecraft.entity.EntityLiving import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.item.EntityArmorStand @@ -849,8 +851,8 @@ class DamageIndicatorManager { return maxHealth.getOrDefault(entity.uniqueID!!, 0L) } - @SubscribeEvent - fun onEntityJoin(event: EntityEnterWorldEvent) { + @HandleEvent + fun onEntityJoin(event: EntityEnterWorldEvent<Entity>) { mobFinder?.handleNewEntity(event.entity) } diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/UniqueGiftingOpportunitiesFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/event/UniqueGiftingOpportunitiesFeatures.kt index 7f312d656..e24a66594 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/UniqueGiftingOpportunitiesFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/UniqueGiftingOpportunitiesFeatures.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.features.event import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.data.ProfileStorageData import at.hannibal2.skyhanni.data.WinterAPI import at.hannibal2.skyhanni.events.EntityCustomNameUpdateEvent @@ -22,6 +23,7 @@ import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.getLorenzVec import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.client.entity.EntityOtherPlayerMP +import net.minecraft.entity.Entity import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.item.EntityArmorStand import net.minecraft.entity.player.EntityPlayer @@ -73,16 +75,16 @@ object UniqueGiftingOpportunitiesFeatures { analyzeArmorStand(entity) } - @SubscribeEvent - fun onEntityJoinWorld(event: EntityEnterWorldEvent) { + @HandleEvent + fun onEntityJoinWorld(event: EntityEnterWorldEvent<Entity>) { playerColor(event) val entity = event.entity as? EntityArmorStand ?: return analyzeArmorStand(entity) } - private fun playerColor(event: EntityEnterWorldEvent) { + private fun playerColor(event: EntityEnterWorldEvent<Entity>) { if (event.entity is EntityOtherPlayerMP) { - val entity = event.entity as EntityOtherPlayerMP + val entity = event.entity if (entity.isNPC() || isIronman(entity) || isBingo(entity)) return RenderLivingEntityHelper.setEntityColor( diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaAPI.kt index 4b1cde2d4..a8c440efe 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaAPI.kt @@ -1,5 +1,6 @@ package at.hannibal2.skyhanni.features.event.diana +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.data.IslandType import at.hannibal2.skyhanni.data.Perk import at.hannibal2.skyhanni.data.PetAPI @@ -8,12 +9,10 @@ import at.hannibal2.skyhanni.events.entity.EntityEnterWorldEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName -import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import net.minecraft.client.entity.EntityOtherPlayerMP import net.minecraft.item.ItemStack -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @SkyHanniModule object DianaAPI { @@ -33,13 +32,10 @@ object DianaAPI { private fun hasSpadeInInventory() = InventoryUtils.getItemsInOwnInventory().any { it.isDianaSpade } - @SubscribeEvent - fun onJoinWorld(event: EntityEnterWorldEvent) { - if (!LorenzUtils.inSkyBlock) return - - val entity = event.entity - if (entity is EntityOtherPlayerMP && entity.name == "Minos Inquisitor") { - InquisitorFoundEvent(entity).postAndCatch() + @HandleEvent(onlyOnSkyblock = true) + fun onJoinWorld(event: EntityEnterWorldEvent<EntityOtherPlayerMP>) { + if (event.entity.name == "Minos Inquisitor") { + InquisitorFoundEvent(event.entity).postAndCatch() } } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt index 89beae6aa..f48309bb0 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt @@ -1,5 +1,6 @@ package at.hannibal2.skyhanni.features.fishing +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.data.jsonobjects.repo.ItemsJson import at.hannibal2.skyhanni.events.FishingBobberCastEvent import at.hannibal2.skyhanni.events.FishingBobberInWaterEvent @@ -55,17 +56,15 @@ object FishingAPI { var wearingTrophyArmor = false - @SubscribeEvent - fun onJoinWorld(event: EntityEnterWorldEvent) { - if (!LorenzUtils.inSkyBlock || !holdingRod) return - val entity = event.entity ?: return - if (entity !is EntityFishHook) return - if (entity.angler != Minecraft.getMinecraft().thePlayer) return + @HandleEvent(onlyOnSkyblock = true) + fun onJoinWorld(event: EntityEnterWorldEvent<EntityFishHook>) { + if (!holdingRod) return + if (event.entity.angler != Minecraft.getMinecraft().thePlayer) return lastCastTime = SimpleTimeMark.now() - bobber = entity + bobber = event.entity bobberHasTouchedWater = false - FishingBobberCastEvent(entity).postAndCatch() + FishingBobberCastEvent(event.entity).postAndCatch() } private fun resetBobber() { diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingHookDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingHookDisplay.kt index 6e2211f58..b4a7e5804 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingHookDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingHookDisplay.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.features.fishing import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.events.CheckRenderEntityEvent import at.hannibal2.skyhanni.events.FishingBobberCastEvent import at.hannibal2.skyhanni.events.GuiRenderEvent @@ -48,13 +49,10 @@ object FishingHookDisplay { armorStand = null } - @SubscribeEvent - fun onJoinWorld(event: EntityEnterWorldEvent) { + @HandleEvent + fun onJoinWorld(event: EntityEnterWorldEvent<EntityArmorStand>) { if (!isEnabled()) return - val entity = event.entity - if (entity !is EntityArmorStand) return - - potentialArmorStands.add(entity) + potentialArmorStands.add(event.entity) } @SubscribeEvent diff --git a/src/main/java/at/hannibal2/skyhanni/test/command/ErrorManager.kt b/src/main/java/at/hannibal2/skyhanni/test/command/ErrorManager.kt index 2100489ce..34b08b82f 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/command/ErrorManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/command/ErrorManager.kt @@ -49,6 +49,8 @@ object ErrorManager { "at at.hannibal2.skyhanni.config.commands.Commands\$createCommand\$1.processCommand", "at at.hannibal2.skyhanni.test.command.ErrorManager.logError", "at at.hannibal2.skyhanni.events.LorenzEvent.postAndCatch", + "at at.hannibal2.skyhanni.api.event.SkyHanniEvent.post", + "at at.hannibal2.skyhanni.api.event.EventHandler.post", "at net.minecraft.launchwrapper.", ) |