aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/events
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/events')
-rw-r--r--src/main/kotlin/events/AllowChatEvent.kt16
-rw-r--r--src/main/kotlin/events/AttackBlockEvent.kt18
-rw-r--r--src/main/kotlin/events/BakeExtraModelsEvent.kt21
-rw-r--r--src/main/kotlin/events/ClientStartedEvent.kt6
-rw-r--r--src/main/kotlin/events/CommandEvent.kt45
-rw-r--r--src/main/kotlin/events/CustomItemModelEvent.kt43
-rw-r--r--src/main/kotlin/events/EarlyResourceReloadEvent.kt10
-rw-r--r--src/main/kotlin/events/EntityDespawnEvent.kt11
-rw-r--r--src/main/kotlin/events/EntityInteractionEvent.kt29
-rw-r--r--src/main/kotlin/events/EntityUpdateEvent.kt31
-rw-r--r--src/main/kotlin/events/FeaturesInitializedEvent.kt8
-rw-r--r--src/main/kotlin/events/FinalizeResourceManagerEvent.kt10
-rw-r--r--src/main/kotlin/events/FirmamentEvent.kt38
-rw-r--r--src/main/kotlin/events/FirmamentEventBus.kt52
-rw-r--r--src/main/kotlin/events/HandledScreenClickEvent.kt10
-rw-r--r--src/main/kotlin/events/HandledScreenForegroundEvent.kt16
-rw-r--r--src/main/kotlin/events/HandledScreenKeyPressedEvent.kt24
-rw-r--r--src/main/kotlin/events/HandledScreenPushREIEvent.kt18
-rw-r--r--src/main/kotlin/events/HotbarItemRenderEvent.kt17
-rw-r--r--src/main/kotlin/events/HudRenderEvent.kt13
-rw-r--r--src/main/kotlin/events/IsSlotProtectedEvent.kt46
-rw-r--r--src/main/kotlin/events/ItemTooltipEvent.kt14
-rw-r--r--src/main/kotlin/events/MaskCommands.kt13
-rw-r--r--src/main/kotlin/events/ModifyChatEvent.kt21
-rw-r--r--src/main/kotlin/events/OutgoingPacketEvent.kt9
-rw-r--r--src/main/kotlin/events/ParticleSpawnEvent.kt18
-rw-r--r--src/main/kotlin/events/PlayerInventoryUpdate.kt11
-rw-r--r--src/main/kotlin/events/ProcessChatEvent.kt28
-rw-r--r--src/main/kotlin/events/ReloadRegistrationEvent.kt7
-rw-r--r--src/main/kotlin/events/ScreenChangeEvent.kt10
-rw-r--r--src/main/kotlin/events/ScreenRenderPostEvent.kt16
-rw-r--r--src/main/kotlin/events/ServerConnectedEvent.kt18
-rw-r--r--src/main/kotlin/events/SkyblockServerUpdateEvent.kt15
-rw-r--r--src/main/kotlin/events/SlotClickEvent.kt15
-rw-r--r--src/main/kotlin/events/SlotRenderEvents.kt34
-rw-r--r--src/main/kotlin/events/SoundReceiveEvent.kt18
-rw-r--r--src/main/kotlin/events/TickEvent.kt7
-rw-r--r--src/main/kotlin/events/TooltipEvent.kt17
-rw-r--r--src/main/kotlin/events/UseBlockEvent.kt11
-rw-r--r--src/main/kotlin/events/WorldKeyboardEvent.kt18
-rw-r--r--src/main/kotlin/events/WorldReadyEvent.kt7
-rw-r--r--src/main/kotlin/events/WorldRenderLastEvent.kt27
-rw-r--r--src/main/kotlin/events/registration/ChatEvents.kt54
-rw-r--r--src/main/kotlin/events/subscription/Subscription.kt16
44 files changed, 886 insertions, 0 deletions
diff --git a/src/main/kotlin/events/AllowChatEvent.kt b/src/main/kotlin/events/AllowChatEvent.kt
new file mode 100644
index 0000000..3069843
--- /dev/null
+++ b/src/main/kotlin/events/AllowChatEvent.kt
@@ -0,0 +1,16 @@
+
+
+package moe.nea.firmament.events
+
+import moe.nea.firmament.util.unformattedString
+import net.minecraft.text.Text
+
+/**
+ * Filter whether the user should see a chat message altogether. May or may not be called for every chat packet sent by
+ * the server. When that quality is desired, consider [ProcessChatEvent] instead.
+ */
+data class AllowChatEvent(val text: Text) : FirmamentEvent.Cancellable() {
+ val unformattedString = text.unformattedString
+
+ companion object : FirmamentEventBus<AllowChatEvent>()
+}
diff --git a/src/main/kotlin/events/AttackBlockEvent.kt b/src/main/kotlin/events/AttackBlockEvent.kt
new file mode 100644
index 0000000..bbaa81d
--- /dev/null
+++ b/src/main/kotlin/events/AttackBlockEvent.kt
@@ -0,0 +1,18 @@
+
+package moe.nea.firmament.events
+
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.util.Hand
+import net.minecraft.util.math.BlockPos
+import net.minecraft.util.math.Direction
+import net.minecraft.world.World
+
+data class AttackBlockEvent(
+ val player: PlayerEntity,
+ val world: World,
+ val hand: Hand,
+ val blockPos: BlockPos,
+ val direction: Direction
+) : FirmamentEvent.Cancellable() {
+ companion object : FirmamentEventBus<AttackBlockEvent>()
+}
diff --git a/src/main/kotlin/events/BakeExtraModelsEvent.kt b/src/main/kotlin/events/BakeExtraModelsEvent.kt
new file mode 100644
index 0000000..f75bedc
--- /dev/null
+++ b/src/main/kotlin/events/BakeExtraModelsEvent.kt
@@ -0,0 +1,21 @@
+
+package moe.nea.firmament.events
+
+import java.util.function.Consumer
+import net.minecraft.client.util.ModelIdentifier
+
+class BakeExtraModelsEvent(
+ private val addItemModel: Consumer<ModelIdentifier>,
+ private val addAnyModel: Consumer<ModelIdentifier>,
+) : FirmamentEvent() {
+
+ fun addNonItemModel(modelIdentifier: ModelIdentifier) {
+ this.addAnyModel.accept(modelIdentifier)
+ }
+
+ fun addItemModel(modelIdentifier: ModelIdentifier) {
+ this.addItemModel.accept(modelIdentifier)
+ }
+
+ companion object : FirmamentEventBus<BakeExtraModelsEvent>()
+}
diff --git a/src/main/kotlin/events/ClientStartedEvent.kt b/src/main/kotlin/events/ClientStartedEvent.kt
new file mode 100644
index 0000000..637916d
--- /dev/null
+++ b/src/main/kotlin/events/ClientStartedEvent.kt
@@ -0,0 +1,6 @@
+
+package moe.nea.firmament.events
+
+class ClientStartedEvent : FirmamentEvent() {
+ companion object : FirmamentEventBus<ClientStartedEvent>()
+}
diff --git a/src/main/kotlin/events/CommandEvent.kt b/src/main/kotlin/events/CommandEvent.kt
new file mode 100644
index 0000000..cc9cf45
--- /dev/null
+++ b/src/main/kotlin/events/CommandEvent.kt
@@ -0,0 +1,45 @@
+
+
+package moe.nea.firmament.events
+
+import com.mojang.brigadier.CommandDispatcher
+import com.mojang.brigadier.tree.LiteralCommandNode
+import net.minecraft.command.CommandRegistryAccess
+import moe.nea.firmament.commands.CaseInsensitiveLiteralCommandNode
+import moe.nea.firmament.commands.DefaultSource
+import moe.nea.firmament.commands.literal
+import moe.nea.firmament.commands.thenLiteral
+
+data class CommandEvent(
+ val dispatcher: CommandDispatcher<DefaultSource>,
+ val ctx: CommandRegistryAccess,
+ val serverCommands: CommandDispatcher<*>?,
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<CommandEvent>()
+
+ /**
+ * Register subcommands to `/firm`. For new top level commands use [CommandEvent]. Cannot be used to register
+ * subcommands to other commands.
+ */
+ data class SubCommand(
+ val builder: CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>,
+ ) : FirmamentEvent() {
+ companion object : FirmamentEventBus<SubCommand>()
+
+ fun subcommand(name: String, block: CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>.() -> Unit) {
+ builder.thenLiteral(name, block)
+ }
+ }
+
+ fun deleteCommand(name: String) {
+ dispatcher.root.children.removeIf { it.name.equals(name, ignoreCase = false) }
+ serverCommands?.root?.children?.removeIf { it.name.equals(name, ignoreCase = false) }
+ }
+
+ fun register(
+ name: String,
+ block: CaseInsensitiveLiteralCommandNode.Builder<DefaultSource>.() -> Unit
+ ): LiteralCommandNode<DefaultSource> {
+ return dispatcher.register(literal(name, block))
+ }
+}
diff --git a/src/main/kotlin/events/CustomItemModelEvent.kt b/src/main/kotlin/events/CustomItemModelEvent.kt
new file mode 100644
index 0000000..27524a9
--- /dev/null
+++ b/src/main/kotlin/events/CustomItemModelEvent.kt
@@ -0,0 +1,43 @@
+
+
+package moe.nea.firmament.events
+
+import java.util.*
+import net.minecraft.client.render.model.BakedModel
+import net.minecraft.client.render.model.BakedModelManager
+import net.minecraft.client.util.ModelIdentifier
+import net.minecraft.item.ItemStack
+
+data class CustomItemModelEvent(
+ val itemStack: ItemStack,
+ var overrideModel: ModelIdentifier? = null,
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<CustomItemModelEvent>() {
+ private val cache = IdentityHashMap<ItemStack?, Any>()
+ private val sentinelNull = Object()
+
+ fun clearCache() {
+ cache.clear()
+ }
+
+ @JvmStatic
+ fun getModelIdentifier(itemStack: ItemStack?): ModelIdentifier? {
+ if (itemStack == null) return null
+ return publish(CustomItemModelEvent(itemStack)).overrideModel
+ }
+
+ @JvmStatic
+ fun getModel(itemStack: ItemStack?, thing: BakedModelManager): BakedModel? {
+ if (itemStack == null) return null
+ val cachedValue = cache.getOrPut(itemStack) {
+ val modelId = getModelIdentifier(itemStack) ?: return@getOrPut sentinelNull
+ val bakedModel = thing.getModel(modelId)
+ if (bakedModel === thing.missingModel) return@getOrPut sentinelNull
+ bakedModel
+ }
+ if (cachedValue === sentinelNull)
+ return null
+ return cachedValue as BakedModel
+ }
+ }
+}
diff --git a/src/main/kotlin/events/EarlyResourceReloadEvent.kt b/src/main/kotlin/events/EarlyResourceReloadEvent.kt
new file mode 100644
index 0000000..ec8377a
--- /dev/null
+++ b/src/main/kotlin/events/EarlyResourceReloadEvent.kt
@@ -0,0 +1,10 @@
+
+package moe.nea.firmament.events
+
+import java.util.concurrent.Executor
+import net.minecraft.resource.ResourceManager
+
+data class EarlyResourceReloadEvent(val resourceManager: ResourceManager, val preparationExecutor: Executor) :
+ FirmamentEvent() {
+ companion object : FirmamentEventBus<EarlyResourceReloadEvent>()
+}
diff --git a/src/main/kotlin/events/EntityDespawnEvent.kt b/src/main/kotlin/events/EntityDespawnEvent.kt
new file mode 100644
index 0000000..93dc477
--- /dev/null
+++ b/src/main/kotlin/events/EntityDespawnEvent.kt
@@ -0,0 +1,11 @@
+
+package moe.nea.firmament.events
+
+import net.minecraft.entity.Entity
+
+data class EntityDespawnEvent(
+ val entity: Entity?, val entityId: Int,
+ val reason: Entity.RemovalReason,
+) : FirmamentEvent() {
+ companion object: FirmamentEventBus<EntityDespawnEvent>()
+}
diff --git a/src/main/kotlin/events/EntityInteractionEvent.kt b/src/main/kotlin/events/EntityInteractionEvent.kt
new file mode 100644
index 0000000..123ea39
--- /dev/null
+++ b/src/main/kotlin/events/EntityInteractionEvent.kt
@@ -0,0 +1,29 @@
+
+package moe.nea.firmament.events
+
+import net.minecraft.entity.Entity
+import net.minecraft.util.Hand
+
+data class EntityInteractionEvent(
+ val kind: InteractionKind,
+ val entity: Entity,
+ val hand: Hand,
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<EntityInteractionEvent>()
+ enum class InteractionKind {
+ /**
+ * Is sent when left-clicking an entity
+ */
+ ATTACK,
+
+ /**
+ * Is a fallback when [INTERACT_AT_LOCATION] fails
+ */
+ INTERACT,
+
+ /**
+ * Is tried first on right click
+ */
+ INTERACT_AT_LOCATION,
+ }
+}
diff --git a/src/main/kotlin/events/EntityUpdateEvent.kt b/src/main/kotlin/events/EntityUpdateEvent.kt
new file mode 100644
index 0000000..d091984
--- /dev/null
+++ b/src/main/kotlin/events/EntityUpdateEvent.kt
@@ -0,0 +1,31 @@
+
+package moe.nea.firmament.events
+
+import net.minecraft.entity.Entity
+import net.minecraft.entity.LivingEntity
+import net.minecraft.entity.data.DataTracker
+import net.minecraft.network.packet.s2c.play.EntityAttributesS2CPacket
+
+/**
+ * This event is fired when some entity properties are updated.
+ * It is not fired for common changes like position, but is for less common ones,
+ * like health, tracked data, names, equipment. It is always fired
+ * *after* the values have been applied to the entity.
+ */
+sealed class EntityUpdateEvent : FirmamentEvent() {
+ companion object : FirmamentEventBus<EntityUpdateEvent>()
+
+ abstract val entity: Entity
+
+ data class AttributeUpdate(
+ override val entity: LivingEntity,
+ val attributes: List<EntityAttributesS2CPacket.Entry>,
+ ) : EntityUpdateEvent()
+
+ data class TrackedDataUpdate(
+ override val entity: Entity,
+ val trackedValues: List<DataTracker.SerializedEntry<*>>,
+ ) : EntityUpdateEvent()
+
+// TODO: onEntityPassengersSet, onEntityAttach?, onEntityEquipmentUpdate, onEntityStatusEffect
+}
diff --git a/src/main/kotlin/events/FeaturesInitializedEvent.kt b/src/main/kotlin/events/FeaturesInitializedEvent.kt
new file mode 100644
index 0000000..ad2ad8a
--- /dev/null
+++ b/src/main/kotlin/events/FeaturesInitializedEvent.kt
@@ -0,0 +1,8 @@
+
+package moe.nea.firmament.events
+
+import moe.nea.firmament.features.FirmamentFeature
+
+data class FeaturesInitializedEvent(val features: List<FirmamentFeature>) : FirmamentEvent() {
+ companion object : FirmamentEventBus<FeaturesInitializedEvent>()
+}
diff --git a/src/main/kotlin/events/FinalizeResourceManagerEvent.kt b/src/main/kotlin/events/FinalizeResourceManagerEvent.kt
new file mode 100644
index 0000000..c43ad3b
--- /dev/null
+++ b/src/main/kotlin/events/FinalizeResourceManagerEvent.kt
@@ -0,0 +1,10 @@
+
+package moe.nea.firmament.events
+
+import net.minecraft.resource.ReloadableResourceManagerImpl
+
+data class FinalizeResourceManagerEvent(
+ val resourceManager: ReloadableResourceManagerImpl,
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<FinalizeResourceManagerEvent>()
+}
diff --git a/src/main/kotlin/events/FirmamentEvent.kt b/src/main/kotlin/events/FirmamentEvent.kt
new file mode 100644
index 0000000..1a93ef5
--- /dev/null
+++ b/src/main/kotlin/events/FirmamentEvent.kt
@@ -0,0 +1,38 @@
+
+
+package moe.nea.firmament.events
+
+/**
+ * An event that can be fired by a [FirmamentEventBus].
+ *
+ * Typically, that event bus is implemented as a companion object
+ *
+ * ```
+ * class SomeEvent : FirmamentEvent() {
+ * companion object : FirmamentEventBus<SomeEvent>()
+ * }
+ * ```
+ */
+abstract class FirmamentEvent {
+ /**
+ * A [FirmamentEvent] that can be [cancelled]
+ */
+ abstract class Cancellable : FirmamentEvent() {
+ /**
+ * Cancels this is event.
+ *
+ * @see cancelled
+ */
+ fun cancel() {
+ cancelled = true
+ }
+
+ /**
+ * Whether this event is cancelled.
+ *
+ * Cancelled events will bypass handlers unless otherwise specified and will prevent the action that this
+ * event was originally fired for.
+ */
+ var cancelled: Boolean = false
+ }
+}
diff --git a/src/main/kotlin/events/FirmamentEventBus.kt b/src/main/kotlin/events/FirmamentEventBus.kt
new file mode 100644
index 0000000..ee9e6c8
--- /dev/null
+++ b/src/main/kotlin/events/FirmamentEventBus.kt
@@ -0,0 +1,52 @@
+
+
+package moe.nea.firmament.events
+
+import java.util.concurrent.CopyOnWriteArrayList
+import moe.nea.firmament.Firmament
+import moe.nea.firmament.util.MC
+
+/**
+ * A pubsub event bus.
+ *
+ * [subscribe] to events [publish]ed on this event bus.
+ * Subscriptions may not necessarily be delivered in the order of registering.
+ */
+open class FirmamentEventBus<T : FirmamentEvent> {
+ data class Handler<T>(
+ val invocation: (T) -> Unit, val receivesCancelled: Boolean,
+ var knownErrors: MutableSet<Class<*>> = mutableSetOf(),
+ )
+
+ private val toHandle: MutableList<Handler<T>> = CopyOnWriteArrayList()
+ fun subscribe(handle: (T) -> Unit) {
+ subscribe(false, handle)
+ }
+
+ fun subscribe(receivesCancelled: Boolean, handle: (T) -> Unit) {
+ toHandle.add(Handler(handle, receivesCancelled))
+ }
+
+ fun publish(event: T): T {
+ for (function in toHandle) {
+ if (function.receivesCancelled || event !is FirmamentEvent.Cancellable || !event.cancelled) {
+ try {
+ function.invocation(event)
+ } catch (e: Exception) {
+ val klass = e.javaClass
+ if (!function.knownErrors.contains(klass) || Firmament.DEBUG) {
+ function.knownErrors.add(klass)
+ Firmament.logger.error("Caught exception during processing event $event by $function", e)
+ }
+ }
+ }
+ }
+ return event
+ }
+
+ fun publishSync(event: T) {
+ MC.onMainThread {
+ publish(event)
+ }
+ }
+}
diff --git a/src/main/kotlin/events/HandledScreenClickEvent.kt b/src/main/kotlin/events/HandledScreenClickEvent.kt
new file mode 100644
index 0000000..4c3003c
--- /dev/null
+++ b/src/main/kotlin/events/HandledScreenClickEvent.kt
@@ -0,0 +1,10 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.gui.screen.ingame.HandledScreen
+
+data class HandledScreenClickEvent(val screen: HandledScreen<*>, val mouseX: Double, val mouseY: Double, val button: Int) :
+ FirmamentEvent.Cancellable() {
+ companion object : FirmamentEventBus<HandledScreenClickEvent>()
+}
diff --git a/src/main/kotlin/events/HandledScreenForegroundEvent.kt b/src/main/kotlin/events/HandledScreenForegroundEvent.kt
new file mode 100644
index 0000000..f16d30e
--- /dev/null
+++ b/src/main/kotlin/events/HandledScreenForegroundEvent.kt
@@ -0,0 +1,16 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.gui.DrawContext
+import net.minecraft.client.gui.screen.ingame.HandledScreen
+
+data class HandledScreenForegroundEvent(
+ val screen: HandledScreen<*>,
+ val context: DrawContext,
+ val mouseX: Int,
+ val mouseY: Int,
+ val delta: Float
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<HandledScreenForegroundEvent>()
+}
diff --git a/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt b/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt
new file mode 100644
index 0000000..7ec2abb
--- /dev/null
+++ b/src/main/kotlin/events/HandledScreenKeyPressedEvent.kt
@@ -0,0 +1,24 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.gui.screen.ingame.HandledScreen
+import net.minecraft.client.option.KeyBinding
+import moe.nea.firmament.keybindings.IKeyBinding
+
+data class HandledScreenKeyPressedEvent(
+ val screen: HandledScreen<*>,
+ val keyCode: Int,
+ val scanCode: Int,
+ val modifiers: Int
+) : FirmamentEvent.Cancellable() {
+ companion object : FirmamentEventBus<HandledScreenKeyPressedEvent>()
+
+ fun matches(keyBinding: KeyBinding): Boolean {
+ return matches(IKeyBinding.minecraft(keyBinding))
+ }
+
+ fun matches(keyBinding: IKeyBinding): Boolean {
+ return keyBinding.matches(keyCode, scanCode, modifiers)
+ }
+}
diff --git a/src/main/kotlin/events/HandledScreenPushREIEvent.kt b/src/main/kotlin/events/HandledScreenPushREIEvent.kt
new file mode 100644
index 0000000..1bb495a
--- /dev/null
+++ b/src/main/kotlin/events/HandledScreenPushREIEvent.kt
@@ -0,0 +1,18 @@
+
+
+package moe.nea.firmament.events
+
+import me.shedaniel.math.Rectangle
+import net.minecraft.client.gui.screen.ingame.HandledScreen
+
+data class HandledScreenPushREIEvent(
+ val screen: HandledScreen<*>,
+ val rectangles: MutableList<Rectangle> = mutableListOf()
+) : FirmamentEvent() {
+
+ fun block(rectangle: Rectangle) {
+ rectangles.add(rectangle)
+ }
+
+ companion object : FirmamentEventBus<HandledScreenPushREIEvent>()
+}
diff --git a/src/main/kotlin/events/HotbarItemRenderEvent.kt b/src/main/kotlin/events/HotbarItemRenderEvent.kt
new file mode 100644
index 0000000..a1940e6
--- /dev/null
+++ b/src/main/kotlin/events/HotbarItemRenderEvent.kt
@@ -0,0 +1,17 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.gui.DrawContext
+import net.minecraft.client.render.RenderTickCounter
+import net.minecraft.item.ItemStack
+
+data class HotbarItemRenderEvent(
+ val item: ItemStack,
+ val context: DrawContext,
+ val x: Int,
+ val y: Int,
+ val tickDelta: RenderTickCounter,
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<HotbarItemRenderEvent>()
+}
diff --git a/src/main/kotlin/events/HudRenderEvent.kt b/src/main/kotlin/events/HudRenderEvent.kt
new file mode 100644
index 0000000..555b3c8
--- /dev/null
+++ b/src/main/kotlin/events/HudRenderEvent.kt
@@ -0,0 +1,13 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.gui.DrawContext
+import net.minecraft.client.render.RenderTickCounter
+
+/**
+ * Called when hud elements should be rendered, before the screen, but after the world.
+ */
+data class HudRenderEvent(val context: DrawContext, val tickDelta: RenderTickCounter) : FirmamentEvent() {
+ companion object : FirmamentEventBus<HudRenderEvent>()
+}
diff --git a/src/main/kotlin/events/IsSlotProtectedEvent.kt b/src/main/kotlin/events/IsSlotProtectedEvent.kt
new file mode 100644
index 0000000..cd431f7
--- /dev/null
+++ b/src/main/kotlin/events/IsSlotProtectedEvent.kt
@@ -0,0 +1,46 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.item.ItemStack
+import net.minecraft.screen.slot.Slot
+import net.minecraft.screen.slot.SlotActionType
+import net.minecraft.text.Text
+import moe.nea.firmament.util.CommonSoundEffects
+import moe.nea.firmament.util.MC
+
+data class IsSlotProtectedEvent(
+ val slot: Slot?,
+ val actionType: SlotActionType,
+ var isProtected: Boolean,
+ val itemStackOverride: ItemStack?,
+ var silent: Boolean = false,
+) : FirmamentEvent() {
+ val itemStack get() = itemStackOverride ?: slot!!.stack
+
+ fun protect() {
+ isProtected = true
+ }
+
+ fun protectSilent() {
+ if (!isProtected) {
+ silent = true
+ }
+ isProtected = true
+ }
+
+ companion object : FirmamentEventBus<IsSlotProtectedEvent>() {
+ @JvmStatic
+ @JvmOverloads
+ fun shouldBlockInteraction(slot: Slot?, action: SlotActionType, itemStackOverride: ItemStack? = null): Boolean {
+ if (slot == null && itemStackOverride == null) return false
+ val event = IsSlotProtectedEvent(slot, action, false, itemStackOverride)
+ publish(event)
+ if (event.isProtected && !event.silent) {
+ MC.player?.sendMessage(Text.translatable("firmament.protectitem").append(event.itemStack.name))
+ CommonSoundEffects.playFailure()
+ }
+ return event.isProtected
+ }
+ }
+}
diff --git a/src/main/kotlin/events/ItemTooltipEvent.kt b/src/main/kotlin/events/ItemTooltipEvent.kt
new file mode 100644
index 0000000..d86e06f
--- /dev/null
+++ b/src/main/kotlin/events/ItemTooltipEvent.kt
@@ -0,0 +1,14 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.item.Item.TooltipContext
+import net.minecraft.item.ItemStack
+import net.minecraft.item.tooltip.TooltipType
+import net.minecraft.text.Text
+
+data class ItemTooltipEvent(
+ val stack: ItemStack, val context: TooltipContext, val type: TooltipType, val lines: MutableList<Text>
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<ItemTooltipEvent>()
+}
diff --git a/src/main/kotlin/events/MaskCommands.kt b/src/main/kotlin/events/MaskCommands.kt
new file mode 100644
index 0000000..35aade0
--- /dev/null
+++ b/src/main/kotlin/events/MaskCommands.kt
@@ -0,0 +1,13 @@
+
+
+package moe.nea.firmament.events
+
+import com.mojang.brigadier.CommandDispatcher
+
+data class MaskCommands(val dispatcher: CommandDispatcher<*>) : FirmamentEvent() {
+ companion object : FirmamentEventBus<MaskCommands>()
+
+ fun mask(name: String) {
+ dispatcher.root.children.removeIf { it.name.equals(name, ignoreCase = true) }
+ }
+}
diff --git a/src/main/kotlin/events/ModifyChatEvent.kt b/src/main/kotlin/events/ModifyChatEvent.kt
new file mode 100644
index 0000000..a5868e8
--- /dev/null
+++ b/src/main/kotlin/events/ModifyChatEvent.kt
@@ -0,0 +1,21 @@
+
+
+package moe.nea.firmament.events
+
+import moe.nea.firmament.util.unformattedString
+import net.minecraft.text.Text
+
+/**
+ * Allow modification of a chat message before it is sent off to the user. Intended for display purposes.
+ */
+data class ModifyChatEvent(val originalText: Text) : FirmamentEvent() {
+ var unformattedString = originalText.unformattedString
+ private set
+ var replaceWith: Text = originalText
+ set(value) {
+ field = value
+ unformattedString = value.unformattedString
+ }
+
+ companion object : FirmamentEventBus<ModifyChatEvent>()
+}
diff --git a/src/main/kotlin/events/OutgoingPacketEvent.kt b/src/main/kotlin/events/OutgoingPacketEvent.kt
new file mode 100644
index 0000000..93890ea
--- /dev/null
+++ b/src/main/kotlin/events/OutgoingPacketEvent.kt
@@ -0,0 +1,9 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.network.packet.Packet
+
+data class OutgoingPacketEvent(val packet: Packet<*>) : FirmamentEvent.Cancellable() {
+ companion object : FirmamentEventBus<OutgoingPacketEvent>()
+}
diff --git a/src/main/kotlin/events/ParticleSpawnEvent.kt b/src/main/kotlin/events/ParticleSpawnEvent.kt
new file mode 100644
index 0000000..9359e4b
--- /dev/null
+++ b/src/main/kotlin/events/ParticleSpawnEvent.kt
@@ -0,0 +1,18 @@
+
+
+package moe.nea.firmament.events
+
+import org.joml.Vector3f
+import net.minecraft.particle.ParticleEffect
+import net.minecraft.util.math.Vec3d
+
+data class ParticleSpawnEvent(
+ val particleEffect: ParticleEffect,
+ val position: Vec3d,
+ val offset: Vector3f,
+ val longDistance: Boolean,
+ val count: Int,
+ val speed: Float,
+) : FirmamentEvent.Cancellable() {
+ companion object : FirmamentEventBus<ParticleSpawnEvent>()
+}
diff --git a/src/main/kotlin/events/PlayerInventoryUpdate.kt b/src/main/kotlin/events/PlayerInventoryUpdate.kt
new file mode 100644
index 0000000..6e8203a
--- /dev/null
+++ b/src/main/kotlin/events/PlayerInventoryUpdate.kt
@@ -0,0 +1,11 @@
+
+package moe.nea.firmament.events
+
+import net.minecraft.item.ItemStack
+
+sealed class PlayerInventoryUpdate : FirmamentEvent() {
+ companion object : FirmamentEventBus<PlayerInventoryUpdate>()
+ data class Single(val slot: Int, val stack: ItemStack) : PlayerInventoryUpdate()
+ data class Multi(val contents: List<ItemStack>) : PlayerInventoryUpdate()
+
+}
diff --git a/src/main/kotlin/events/ProcessChatEvent.kt b/src/main/kotlin/events/ProcessChatEvent.kt
new file mode 100644
index 0000000..76c0b27
--- /dev/null
+++ b/src/main/kotlin/events/ProcessChatEvent.kt
@@ -0,0 +1,28 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.text.Text
+import moe.nea.firmament.util.unformattedString
+
+/**
+ * Behaves like [AllowChatEvent], but is triggered even when cancelled by other mods. Intended for data collection.
+ * Make sure to subscribe to cancellable events as well when using.
+ */
+data class ProcessChatEvent(val text: Text, val wasExternallyCancelled: Boolean) : FirmamentEvent.Cancellable() {
+ val unformattedString = text.unformattedString
+
+ val nameHeuristic: String? = run {
+ val firstColon = unformattedString.indexOf(':')
+ if (firstColon < 0) return@run null
+ val firstSpace = unformattedString.lastIndexOf(' ', firstColon)
+ unformattedString.substring(firstSpace + 1 until firstColon).takeIf { it.isNotEmpty() }
+ }
+
+ init {
+ if (wasExternallyCancelled)
+ cancelled = true
+ }
+
+ companion object : FirmamentEventBus<ProcessChatEvent>()
+}
diff --git a/src/main/kotlin/events/ReloadRegistrationEvent.kt b/src/main/kotlin/events/ReloadRegistrationEvent.kt
new file mode 100644
index 0000000..4c3083e
--- /dev/null
+++ b/src/main/kotlin/events/ReloadRegistrationEvent.kt
@@ -0,0 +1,7 @@
+package moe.nea.firmament.events
+
+import io.github.moulberry.repo.NEURepository
+
+data class ReloadRegistrationEvent(val repo: NEURepository) : FirmamentEvent() {
+ companion object : FirmamentEventBus<ReloadRegistrationEvent>()
+}
diff --git a/src/main/kotlin/events/ScreenChangeEvent.kt b/src/main/kotlin/events/ScreenChangeEvent.kt
new file mode 100644
index 0000000..489e487
--- /dev/null
+++ b/src/main/kotlin/events/ScreenChangeEvent.kt
@@ -0,0 +1,10 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.gui.screen.Screen
+
+data class ScreenChangeEvent(val old: Screen?, val new: Screen?) : FirmamentEvent.Cancellable() {
+ var overrideScreen: Screen? = null
+ companion object : FirmamentEventBus<ScreenChangeEvent>()
+}
diff --git a/src/main/kotlin/events/ScreenRenderPostEvent.kt b/src/main/kotlin/events/ScreenRenderPostEvent.kt
new file mode 100644
index 0000000..79f4913
--- /dev/null
+++ b/src/main/kotlin/events/ScreenRenderPostEvent.kt
@@ -0,0 +1,16 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.gui.DrawContext
+import net.minecraft.client.gui.screen.Screen
+
+data class ScreenRenderPostEvent(
+ val screen: Screen,
+ val mouseX: Int,
+ val mouseY: Int,
+ val tickDelta: Float,
+ val drawContext: DrawContext
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<ScreenRenderPostEvent>()
+}
diff --git a/src/main/kotlin/events/ServerConnectedEvent.kt b/src/main/kotlin/events/ServerConnectedEvent.kt
new file mode 100644
index 0000000..26897f2
--- /dev/null
+++ b/src/main/kotlin/events/ServerConnectedEvent.kt
@@ -0,0 +1,18 @@
+package moe.nea.firmament.events
+
+import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents
+import net.minecraft.client.MinecraftClient
+import net.minecraft.client.network.ClientPlayNetworkHandler
+import net.minecraft.network.ClientConnection
+
+data class ServerConnectedEvent(
+ val connection: ClientConnection
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<ServerConnectedEvent>() {
+ init {
+ ClientPlayConnectionEvents.INIT.register(ClientPlayConnectionEvents.Init { clientPlayNetworkHandler: ClientPlayNetworkHandler, minecraftClient: MinecraftClient ->
+ publishSync(ServerConnectedEvent(clientPlayNetworkHandler.connection))
+ })
+ }
+ }
+}
diff --git a/src/main/kotlin/events/SkyblockServerUpdateEvent.kt b/src/main/kotlin/events/SkyblockServerUpdateEvent.kt
new file mode 100644
index 0000000..0bc5143
--- /dev/null
+++ b/src/main/kotlin/events/SkyblockServerUpdateEvent.kt
@@ -0,0 +1,15 @@
+
+
+package moe.nea.firmament.events
+
+import moe.nea.firmament.util.Locraw
+
+/**
+ * This event gets published whenever `/locraw` is queried and HyPixel returns a location different to the old one.
+ *
+ * **N.B.:** This event may get fired multiple times while on the server (for example, first to null, then to the
+ * correct location).
+ */
+data class SkyblockServerUpdateEvent(val oldLocraw: Locraw?, val newLocraw: Locraw?) : FirmamentEvent() {
+ companion object : FirmamentEventBus<SkyblockServerUpdateEvent>()
+}
diff --git a/src/main/kotlin/events/SlotClickEvent.kt b/src/main/kotlin/events/SlotClickEvent.kt
new file mode 100644
index 0000000..d4abfb0
--- /dev/null
+++ b/src/main/kotlin/events/SlotClickEvent.kt
@@ -0,0 +1,15 @@
+
+package moe.nea.firmament.events
+
+import net.minecraft.item.ItemStack
+import net.minecraft.screen.slot.Slot
+import net.minecraft.screen.slot.SlotActionType
+
+data class SlotClickEvent(
+ val slot: Slot,
+ val stack: ItemStack,
+ val button: Int,
+ val actionType: SlotActionType,
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<SlotClickEvent>()
+}
diff --git a/src/main/kotlin/events/SlotRenderEvents.kt b/src/main/kotlin/events/SlotRenderEvents.kt
new file mode 100644
index 0000000..8352581
--- /dev/null
+++ b/src/main/kotlin/events/SlotRenderEvents.kt
@@ -0,0 +1,34 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.gui.DrawContext
+import net.minecraft.screen.slot.Slot
+
+interface SlotRenderEvents {
+ val context: DrawContext
+ val slot: Slot
+ val mouseX: Int
+ val mouseY: Int
+ val delta: Float
+
+ data class Before(
+ override val context: DrawContext, override val slot: Slot,
+ override val mouseX: Int,
+ override val mouseY: Int,
+ override val delta: Float
+ ) : FirmamentEvent(),
+ SlotRenderEvents {
+ companion object : FirmamentEventBus<Before>()
+ }
+
+ data class After(
+ override val context: DrawContext, override val slot: Slot,
+ override val mouseX: Int,
+ override val mouseY: Int,
+ override val delta: Float
+ ) : FirmamentEvent(),
+ SlotRenderEvents {
+ companion object : FirmamentEventBus<After>()
+ }
+}
diff --git a/src/main/kotlin/events/SoundReceiveEvent.kt b/src/main/kotlin/events/SoundReceiveEvent.kt
new file mode 100644
index 0000000..d1b85b6
--- /dev/null
+++ b/src/main/kotlin/events/SoundReceiveEvent.kt
@@ -0,0 +1,18 @@
+
+package moe.nea.firmament.events
+
+import net.minecraft.registry.entry.RegistryEntry
+import net.minecraft.sound.SoundCategory
+import net.minecraft.sound.SoundEvent
+import net.minecraft.util.math.Vec3d
+
+data class SoundReceiveEvent(
+ val sound: RegistryEntry<SoundEvent>,
+ val category: SoundCategory,
+ val position: Vec3d,
+ val pitch: Float,
+ val volume: Float,
+ val seed: Long
+) : FirmamentEvent.Cancellable() {
+ companion object : FirmamentEventBus<SoundReceiveEvent>()
+}
diff --git a/src/main/kotlin/events/TickEvent.kt b/src/main/kotlin/events/TickEvent.kt
new file mode 100644
index 0000000..18007f8
--- /dev/null
+++ b/src/main/kotlin/events/TickEvent.kt
@@ -0,0 +1,7 @@
+
+
+package moe.nea.firmament.events
+
+data class TickEvent(val tickCount: Int) : FirmamentEvent() {
+ companion object : FirmamentEventBus<TickEvent>()
+}
diff --git a/src/main/kotlin/events/TooltipEvent.kt b/src/main/kotlin/events/TooltipEvent.kt
new file mode 100644
index 0000000..630ba56
--- /dev/null
+++ b/src/main/kotlin/events/TooltipEvent.kt
@@ -0,0 +1,17 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.gui.tooltip.Tooltip
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.item.Item
+import net.minecraft.item.ItemStack
+
+data class TooltipEvent(
+ val itemStack: ItemStack,
+ val tooltip: Tooltip,
+ val tooltipContext: Item.TooltipContext,
+ val player: PlayerEntity?
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<TooltipEvent>()
+}
diff --git a/src/main/kotlin/events/UseBlockEvent.kt b/src/main/kotlin/events/UseBlockEvent.kt
new file mode 100644
index 0000000..8bbe0de
--- /dev/null
+++ b/src/main/kotlin/events/UseBlockEvent.kt
@@ -0,0 +1,11 @@
+
+package moe.nea.firmament.events
+
+import net.minecraft.entity.player.PlayerEntity
+import net.minecraft.util.Hand
+import net.minecraft.util.hit.BlockHitResult
+import net.minecraft.world.World
+
+data class UseBlockEvent(val player: PlayerEntity, val world: World, val hand: Hand, val hitResult: BlockHitResult) : FirmamentEvent.Cancellable() {
+ companion object : FirmamentEventBus<UseBlockEvent>()
+}
diff --git a/src/main/kotlin/events/WorldKeyboardEvent.kt b/src/main/kotlin/events/WorldKeyboardEvent.kt
new file mode 100644
index 0000000..e8566fd
--- /dev/null
+++ b/src/main/kotlin/events/WorldKeyboardEvent.kt
@@ -0,0 +1,18 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.option.KeyBinding
+import moe.nea.firmament.keybindings.IKeyBinding
+
+data class WorldKeyboardEvent(val keyCode: Int, val scanCode: Int, val modifiers: Int) : FirmamentEvent.Cancellable() {
+ companion object : FirmamentEventBus<WorldKeyboardEvent>()
+
+ fun matches(keyBinding: KeyBinding): Boolean {
+ return matches(IKeyBinding.minecraft(keyBinding))
+ }
+
+ fun matches(keyBinding: IKeyBinding): Boolean {
+ return keyBinding.matches(keyCode, scanCode, modifiers)
+ }
+}
diff --git a/src/main/kotlin/events/WorldReadyEvent.kt b/src/main/kotlin/events/WorldReadyEvent.kt
new file mode 100644
index 0000000..2c76c44
--- /dev/null
+++ b/src/main/kotlin/events/WorldReadyEvent.kt
@@ -0,0 +1,7 @@
+
+
+package moe.nea.firmament.events
+
+class WorldReadyEvent : FirmamentEvent() {
+ companion object : FirmamentEventBus<WorldReadyEvent>()
+}
diff --git a/src/main/kotlin/events/WorldRenderLastEvent.kt b/src/main/kotlin/events/WorldRenderLastEvent.kt
new file mode 100644
index 0000000..21a670d
--- /dev/null
+++ b/src/main/kotlin/events/WorldRenderLastEvent.kt
@@ -0,0 +1,27 @@
+
+
+package moe.nea.firmament.events
+
+import net.minecraft.client.render.Camera
+import net.minecraft.client.render.GameRenderer
+import net.minecraft.client.render.LightmapTextureManager
+import net.minecraft.client.render.RenderTickCounter
+import net.minecraft.client.render.VertexConsumerProvider
+import net.minecraft.client.util.math.MatrixStack
+import net.minecraft.util.math.Position
+import net.minecraft.util.math.Vec3d
+
+/**
+ * This event is called after all world rendering is done, but before any GUI rendering (including hand) has been done.
+ */
+data class WorldRenderLastEvent(
+ val matrices: MatrixStack,
+ val tickCounter: RenderTickCounter,
+ val renderBlockOutline: Boolean,
+ val camera: Camera,
+ val gameRenderer: GameRenderer,
+ val lightmapTextureManager: LightmapTextureManager,
+ val vertexConsumers: VertexConsumerProvider.Immediate,
+) : FirmamentEvent() {
+ companion object : FirmamentEventBus<WorldRenderLastEvent>()
+}
diff --git a/src/main/kotlin/events/registration/ChatEvents.kt b/src/main/kotlin/events/registration/ChatEvents.kt
new file mode 100644
index 0000000..4c1c63f
--- /dev/null
+++ b/src/main/kotlin/events/registration/ChatEvents.kt
@@ -0,0 +1,54 @@
+
+
+package moe.nea.firmament.events.registration
+
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents
+import net.fabricmc.fabric.api.event.player.AttackBlockCallback
+import net.fabricmc.fabric.api.event.player.UseBlockCallback
+import net.minecraft.text.Text
+import net.minecraft.util.ActionResult
+import moe.nea.firmament.events.AllowChatEvent
+import moe.nea.firmament.events.AttackBlockEvent
+import moe.nea.firmament.events.ModifyChatEvent
+import moe.nea.firmament.events.ProcessChatEvent
+import moe.nea.firmament.events.UseBlockEvent
+
+private var lastReceivedMessage: Text? = null
+
+fun registerFirmamentEvents() {
+ ClientReceiveMessageEvents.ALLOW_CHAT.register(ClientReceiveMessageEvents.AllowChat { message, signedMessage, sender, params, receptionTimestamp ->
+ lastReceivedMessage = message
+ !ProcessChatEvent.publish(ProcessChatEvent(message, false)).cancelled
+ && !AllowChatEvent.publish(AllowChatEvent(message)).cancelled
+ })
+ ClientReceiveMessageEvents.ALLOW_GAME.register(ClientReceiveMessageEvents.AllowGame { message, overlay ->
+ lastReceivedMessage = message
+ overlay || (!ProcessChatEvent.publish(ProcessChatEvent(message, false)).cancelled &&
+ !AllowChatEvent.publish(AllowChatEvent(message)).cancelled)
+ })
+ ClientReceiveMessageEvents.MODIFY_GAME.register(ClientReceiveMessageEvents.ModifyGame { message, overlay ->
+ if (overlay) message
+ else ModifyChatEvent.publish(ModifyChatEvent(message)).replaceWith
+ })
+ ClientReceiveMessageEvents.GAME_CANCELED.register(ClientReceiveMessageEvents.GameCanceled { message, overlay ->
+ if (!overlay && lastReceivedMessage !== message) {
+ ProcessChatEvent.publish(ProcessChatEvent(message, true))
+ }
+ })
+ ClientReceiveMessageEvents.CHAT_CANCELED.register(ClientReceiveMessageEvents.ChatCanceled { message, signedMessage, sender, params, receptionTimestamp ->
+ if (lastReceivedMessage !== message) {
+ ProcessChatEvent.publish(ProcessChatEvent(message, true))
+ }
+ })
+
+ AttackBlockCallback.EVENT.register(AttackBlockCallback { player, world, hand, pos, direction ->
+ if (AttackBlockEvent.publish(AttackBlockEvent(player, world, hand, pos, direction)).cancelled)
+ ActionResult.CONSUME
+ else ActionResult.PASS
+ })
+ UseBlockCallback.EVENT.register(UseBlockCallback { player, world, hand, hitResult ->
+ if (UseBlockEvent.publish(UseBlockEvent(player, world, hand, hitResult)).cancelled)
+ ActionResult.CONSUME
+ else ActionResult.PASS
+ })
+}
diff --git a/src/main/kotlin/events/subscription/Subscription.kt b/src/main/kotlin/events/subscription/Subscription.kt
new file mode 100644
index 0000000..83b91c9
--- /dev/null
+++ b/src/main/kotlin/events/subscription/Subscription.kt
@@ -0,0 +1,16 @@
+
+package moe.nea.firmament.events.subscription
+
+import moe.nea.firmament.events.FirmamentEvent
+import moe.nea.firmament.events.FirmamentEventBus
+import moe.nea.firmament.features.FirmamentFeature
+
+interface SubscriptionOwner {
+ val delegateFeature: FirmamentFeature
+}
+
+data class Subscription<T : FirmamentEvent>(
+ val owner: Any,
+ val invoke: (T) -> Unit,
+ val eventBus: FirmamentEventBus<T>,
+)