mirror of
https://github.com/architectury/architectury-api.git
synced 2026-03-30 05:05:19 -05:00
new event system
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.event;
|
||||
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
|
||||
/**
|
||||
* A result from an event, determines if the event should continue to other listeners,
|
||||
* determines the outcome of the event, and provides extra result for the outcome.
|
||||
*
|
||||
* @param <T> the type of the extra result
|
||||
* @see #pass()
|
||||
* @see #interrupt(Boolean, Object)
|
||||
* @see CompoundEventResult
|
||||
*/
|
||||
public class CompoundEventResult<T> {
|
||||
private static final CompoundEventResult<?> PASS = new CompoundEventResult<>(EventResult.pass(), null);
|
||||
private final EventResult result;
|
||||
private final T object;
|
||||
|
||||
/**
|
||||
* Passes the event to other listeners, and does not set an outcome of the event.
|
||||
*
|
||||
* @param <T> the type of the extra result
|
||||
* @return an event that passes the event to other listeners
|
||||
*/
|
||||
public static <T> CompoundEventResult<T> pass() {
|
||||
return (CompoundEventResult<T>) PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the event and stops it from being passed on to other listeners,
|
||||
* may or may not set an outcome and extra data of the event.
|
||||
*
|
||||
* @param value the outcome of the event, passing {@code null} here means the default outcome,
|
||||
* which often means falling back to vanilla logic
|
||||
* @param object the extra data of the result, this usually is the returning value of the event
|
||||
* @return an event that interrupts the event
|
||||
*/
|
||||
public static <T> CompoundEventResult<T> interrupt(Boolean value, T object) {
|
||||
return new CompoundEventResult<>(EventResult.interrupt(value), object);
|
||||
}
|
||||
|
||||
private CompoundEventResult(EventResult result, T object) {
|
||||
this.result = result;
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this result interrupts the evaluation of other listeners.
|
||||
*
|
||||
* @return whether this result interrupts the evaluation of other listeners
|
||||
*/
|
||||
public boolean interruptsFurtherEvaluation() {
|
||||
return result.interruptsFurtherEvaluation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the outcome of the result, an passing result will never have an outcome.
|
||||
*
|
||||
* @return the outcome of the result, returns {@code null} if fallback
|
||||
*/
|
||||
public Boolean value() {
|
||||
return result.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link EventResult} view of the result, this returns the same values as
|
||||
* {@link #interruptsFurtherEvaluation()} and {@link #value()}.
|
||||
*
|
||||
* @return the {@link EventResult} view of the result.
|
||||
*/
|
||||
public EventResult result() {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extra data of the result, an passing result will never contain any extra data.
|
||||
*
|
||||
* @return the extra data of the result, returns {@code null} if passing
|
||||
*/
|
||||
public T object() {
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Minecraft-facing result, however ignores {@link #interruptsFurtherEvaluation()}.
|
||||
*
|
||||
* @return the Minecraft-facing result
|
||||
*/
|
||||
public InteractionResultHolder<T> asMinecraft() {
|
||||
if (value() != null) {
|
||||
return value() ? InteractionResultHolder.success(object()) : InteractionResultHolder.fail(object());
|
||||
}
|
||||
return InteractionResultHolder.pass(object());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.event;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface EventActor<T> {
|
||||
EventResult act(T t);
|
||||
}
|
||||
@@ -99,6 +99,28 @@ public final class EventFactory {
|
||||
}));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> Event<T> createEventResult(T... typeGetter) {
|
||||
if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!");
|
||||
return createEventResult((Class<T>) typeGetter.getClass().getComponentType());
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public static <T> Event<T> createEventResult(Class<T> clazz) {
|
||||
return of(listeners -> (T) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{clazz}, new AbstractInvocationHandler() {
|
||||
@Override
|
||||
protected Object handleInvocation(@NotNull Object proxy, @NotNull Method method, Object @NotNull [] args) throws Throwable {
|
||||
for (T listener : listeners) {
|
||||
EventResult result = (EventResult) Objects.requireNonNull(method.invoke(listener, args));
|
||||
if (result.interruptsFurtherEvaluation()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return EventResult.pass();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> Event<T> createInteractionResultHolder(T... typeGetter) {
|
||||
if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!");
|
||||
@@ -121,6 +143,28 @@ public final class EventFactory {
|
||||
}));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> Event<T> createCompoundEventResult(T... typeGetter) {
|
||||
if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!");
|
||||
return createCompoundEventResult((Class<T>) typeGetter.getClass().getComponentType());
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public static <T> Event<T> createCompoundEventResult(Class<T> clazz) {
|
||||
return of(listeners -> (T) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{clazz}, new AbstractInvocationHandler() {
|
||||
@Override
|
||||
protected Object handleInvocation(@NotNull Object proxy, @NotNull Method method, Object @NotNull [] args) throws Throwable {
|
||||
for (T listener : listeners) {
|
||||
CompoundEventResult result = (CompoundEventResult) Objects.requireNonNull(method.invoke(listener, args));
|
||||
if (result.interruptsFurtherEvaluation()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return CompoundEventResult.pass();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> Event<Consumer<T>> createConsumerLoop(T... typeGetter) {
|
||||
if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!");
|
||||
@@ -187,21 +231,75 @@ public final class EventFactory {
|
||||
return event;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> Event<EventActor<T>> createEventActorLoop(T... typeGetter) {
|
||||
if (typeGetter.length != 0) throw new IllegalStateException("array must be empty!");
|
||||
return createEventActorLoop((Class<T>) typeGetter.getClass().getComponentType());
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public static <T> Event<EventActor<T>> createEventActorLoop(Class<T> clazz) {
|
||||
Event<EventActor<T>> event = of(listeners -> (EventActor<T>) Proxy.newProxyInstance(EventFactory.class.getClassLoader(), new Class[]{EventActor.class}, new AbstractInvocationHandler() {
|
||||
@Override
|
||||
protected Object handleInvocation(@NotNull Object proxy, @NotNull Method method, Object @NotNull [] args) throws Throwable {
|
||||
for (EventActor<T> listener : listeners) {
|
||||
EventResult result = (EventResult) method.invoke(listener, args);
|
||||
if (result.interruptsFurtherEvaluation()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return EventResult.pass();
|
||||
}
|
||||
}));
|
||||
Class<?> superClass = clazz;
|
||||
do {
|
||||
|
||||
if (superClass.isAnnotationPresent(ForgeEventCancellable.class)) {
|
||||
return attachToForgeEventActorCancellable(event);
|
||||
}
|
||||
superClass = superClass.getSuperclass();
|
||||
} while (superClass != null);
|
||||
superClass = clazz;
|
||||
do {
|
||||
|
||||
if (superClass.isAnnotationPresent(ForgeEvent.class)) {
|
||||
return attachToForgeEventActor(event);
|
||||
}
|
||||
superClass = superClass.getSuperclass();
|
||||
} while (superClass != null);
|
||||
return event;
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
@ApiStatus.Internal
|
||||
public static <T> Event<Consumer<T>> attachToForge(Event<Consumer<T>> event) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
@ApiStatus.Internal
|
||||
public static <T> Event<Actor<T>> attachToForgeActor(Event<Actor<T>> event) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
@ApiStatus.Internal
|
||||
public static <T> Event<Actor<T>> attachToForgeActorCancellable(Event<Actor<T>> event) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
@ApiStatus.Internal
|
||||
public static <T> Event<EventActor<T>> attachToForgeEventActor(Event<EventActor<T>> event) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
@ApiStatus.Internal
|
||||
public static <T> Event<EventActor<T>> attachToForgeEventActorCancellable(Event<EventActor<T>> event) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
private static class EventImpl<T> implements Event<T> {
|
||||
private final Function<List<T>, T> function;
|
||||
private T invoker = null;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of architectury.
|
||||
* Copyright (C) 2020, 2021 architectury
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.event;
|
||||
|
||||
/**
|
||||
* A result from an event, determines if the event should continue to other listeners,
|
||||
* and determines the outcome of the event.
|
||||
*
|
||||
* @see #pass()
|
||||
* @see #interrupt(Boolean)
|
||||
* @see CompoundEventResult
|
||||
*/
|
||||
public final class EventResult {
|
||||
private static final EventResult TRUE = new EventResult(true, true);
|
||||
private static final EventResult STOP = new EventResult(true, null);
|
||||
private static final EventResult PASS = new EventResult(false, null);
|
||||
private static final EventResult FALSE = new EventResult(true, false);
|
||||
|
||||
/**
|
||||
* Passes the event to other listeners, and does not set an outcome of the event.
|
||||
*
|
||||
* @return an event that passes the event to other listeners
|
||||
*/
|
||||
public static EventResult pass() {
|
||||
return PASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupts the event and stops it from being passed on to other listeners,
|
||||
* may or may not set an outcome of the event.
|
||||
*
|
||||
* @param value the outcome of the event, passing {@code null} here means the default outcome,
|
||||
* which often means falling back to vanilla logic
|
||||
* @return an event that interrupts the event
|
||||
*/
|
||||
public static EventResult interrupt(Boolean value) {
|
||||
if (value == null) return STOP;
|
||||
if (value) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
private final boolean interruptsFurtherEvaluation;
|
||||
|
||||
private final Boolean value;
|
||||
|
||||
EventResult(boolean interruptsFurtherEvaluation, Boolean value) {
|
||||
this.interruptsFurtherEvaluation = interruptsFurtherEvaluation;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this result interrupts the evaluation of other listeners.
|
||||
*
|
||||
* @return whether this result interrupts the evaluation of other listeners
|
||||
*/
|
||||
public boolean interruptsFurtherEvaluation() {
|
||||
return interruptsFurtherEvaluation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the outcome of the result, an passing result will never have an outcome.
|
||||
*
|
||||
* @return the outcome of the result, returns {@code null} if fallback
|
||||
*/
|
||||
public Boolean value() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ package me.shedaniel.architectury.event.events;
|
||||
|
||||
import me.shedaniel.architectury.event.Event;
|
||||
import me.shedaniel.architectury.event.EventFactory;
|
||||
import me.shedaniel.architectury.event.EventResult;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
@@ -49,13 +50,13 @@ public interface EntityEvent {
|
||||
*/
|
||||
Event<Add> ADD = EventFactory.createInteractionResult();
|
||||
/**
|
||||
* Invoked when an entity enters a chunk.
|
||||
* Invoked when an entity enters a chunk, equivalent to forge's {@code EnteringChunk}
|
||||
*/
|
||||
Event<EnterChunk> ENTER_CHUNK = EventFactory.createLoop();
|
||||
/**
|
||||
* Invoked when an entity is about to be spawned, equivalent to forge's {@code LivingSpawnEvent.CheckSpawn}
|
||||
*/
|
||||
Event<CheckSpawn> CHECK_SPAWN = EventFactory.createInteractionResult();
|
||||
Event<CheckSpawn> CHECK_SPAWN = EventFactory.createEventResult();
|
||||
|
||||
/**
|
||||
* @deprecated use {@link BlockEvent#PLACE}
|
||||
@@ -85,6 +86,6 @@ public interface EntityEvent {
|
||||
}
|
||||
|
||||
interface CheckSpawn {
|
||||
InteractionResult canSpawn(Entity entity, LevelAccessor world, double x, double y, double z, MobSpawnType type, @Nullable BaseSpawner spawner);
|
||||
EventResult canSpawn(Entity entity, LevelAccessor world, double x, double y, double z, MobSpawnType type, @Nullable BaseSpawner spawner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ package me.shedaniel.architectury.event.events;
|
||||
|
||||
import me.shedaniel.architectury.event.Event;
|
||||
import me.shedaniel.architectury.event.EventFactory;
|
||||
import me.shedaniel.architectury.event.EventResult;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
@@ -42,7 +43,7 @@ public interface InteractionEvent {
|
||||
/**
|
||||
* Invoked before a farmland block is trampled by an entity, equivalent to forge's {@code BlockEvent.FarmlandTrampleEvent}
|
||||
*/
|
||||
Event<FarmlandTrample> FARMLAND_TRAMPLE = EventFactory.createInteractionResult();
|
||||
Event<FarmlandTrample> FARMLAND_TRAMPLE = EventFactory.createEventResult();
|
||||
|
||||
interface RightClickBlock {
|
||||
InteractionResult click(Player player, InteractionHand hand, BlockPos pos, Direction face);
|
||||
@@ -73,6 +74,6 @@ public interface InteractionEvent {
|
||||
}
|
||||
|
||||
interface FarmlandTrample {
|
||||
InteractionResult trample(Level world, BlockPos pos, BlockState state, float distance, Entity entity);
|
||||
EventResult trample(Level world, BlockPos pos, BlockState state, float distance, Entity entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
package me.shedaniel.architectury.event.events;
|
||||
|
||||
import me.shedaniel.architectury.event.CompoundEventResult;
|
||||
import me.shedaniel.architectury.event.Event;
|
||||
import me.shedaniel.architectury.event.EventFactory;
|
||||
import me.shedaniel.architectury.utils.IntValue;
|
||||
@@ -28,14 +29,12 @@ import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -57,11 +56,11 @@ public interface PlayerEvent {
|
||||
/**
|
||||
* Invoked when a player attempts to fill a bucket using right-click.
|
||||
* You can return a non-PASS interaction result to cancel further processing by other mods.
|
||||
*
|
||||
* <p>
|
||||
* On Forge, FAIL cancels the event, and SUCCESS sets the event as handled.
|
||||
* On Fabric, any non-PASS result is returned directly and immediately.
|
||||
*/
|
||||
Event<FillBucket> FILL_BUCKET = EventFactory.createInteractionResultHolder();
|
||||
Event<FillBucket> FILL_BUCKET = EventFactory.createCompoundEventResult();
|
||||
|
||||
/**
|
||||
* @deprecated use {@link BlockEvent#BREAK}
|
||||
@@ -127,6 +126,6 @@ public interface PlayerEvent {
|
||||
}
|
||||
|
||||
interface FillBucket {
|
||||
InteractionResultHolder<ItemStack> fill(Player player, Level level, ItemStack stack, @Nullable HitResult target);
|
||||
CompoundEventResult<ItemStack> fill(Player player, Level level, ItemStack stack, @Nullable HitResult target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ package me.shedaniel.architectury.event.fabric;
|
||||
|
||||
import me.shedaniel.architectury.event.Actor;
|
||||
import me.shedaniel.architectury.event.Event;
|
||||
import me.shedaniel.architectury.event.EventActor;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -36,4 +38,14 @@ public class EventFactoryImpl {
|
||||
public static <T> Event<Actor<T>> attachToForgeActorCancellable(Event<Actor<T>> event) {
|
||||
return event;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static <T> Event<EventActor<T>> attachToForgeEventActor(Event<EventActor<T>> event) {
|
||||
return event;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static <T> Event<EventActor<T>> attachToForgeEventActorCancellable(Event<EventActor<T>> event) {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
package me.shedaniel.architectury.mixin.fabric;
|
||||
|
||||
import me.shedaniel.architectury.event.CompoundEventResult;
|
||||
import me.shedaniel.architectury.event.events.PlayerEvent;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BucketItem;
|
||||
@@ -48,9 +48,9 @@ public class MixinBucketItem {
|
||||
cancellable = true
|
||||
)
|
||||
public void fillBucket(Level level, Player player, InteractionHand hand, CallbackInfoReturnable<InteractionResultHolder<ItemStack>> cir, ItemStack stack, HitResult target) {
|
||||
InteractionResultHolder<ItemStack> event = PlayerEvent.FILL_BUCKET.invoker().fill(player, level, stack, target);
|
||||
if (event.getResult() != InteractionResult.PASS) {
|
||||
cir.setReturnValue(event);
|
||||
CompoundEventResult<ItemStack> result = PlayerEvent.FILL_BUCKET.invoker().fill(player, level, stack, target);
|
||||
if (result.interruptsFurtherEvaluation() && result.value() != null) {
|
||||
cir.setReturnValue(result.asMinecraft());
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.mixin.fabric;
|
||||
|
||||
import me.shedaniel.architectury.event.events.EntityEvent;
|
||||
@@ -38,7 +39,6 @@ import java.util.Random;
|
||||
|
||||
@Mixin(CatSpawner.class)
|
||||
public abstract class MixinCatSpawner {
|
||||
|
||||
@Inject(
|
||||
method = "spawnCat",
|
||||
at = @At(
|
||||
@@ -50,7 +50,7 @@ public abstract class MixinCatSpawner {
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private void checkCatSpawn(BlockPos pos, ServerLevel level, CallbackInfoReturnable<Integer> cir, Cat entity) {
|
||||
if (EntityEvent.CHECK_SPAWN.invoker().canSpawn(entity, level, pos.getX(), pos.getY(), pos.getZ(), MobSpawnType.NATURAL, null) == InteractionResult.FAIL) {
|
||||
if (EntityEvent.CHECK_SPAWN.invoker().canSpawn(entity, level, pos.getX(), pos.getY(), pos.getZ(), MobSpawnType.NATURAL, null).value() == Boolean.FALSE) {
|
||||
cir.setReturnValue(0);
|
||||
cir.cancel();
|
||||
}
|
||||
|
||||
@@ -21,33 +21,45 @@ package me.shedaniel.architectury.mixin.fabric;
|
||||
|
||||
import me.shedaniel.architectury.event.events.InteractionEvent;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.Tuple;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.FarmBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(FarmBlock.class)
|
||||
public abstract class MixinFarmBlock {
|
||||
@Unique
|
||||
private static ThreadLocal<Triple<Long, Float, Entity>> turnToDirtLocal = new ThreadLocal<>();
|
||||
|
||||
@Shadow
|
||||
public static void turnToDirt(BlockState blockState, Level level, BlockPos blockPos) {
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
@Inject(
|
||||
method = "fallOn",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/block/FarmBlock;turnToDirt(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)V"
|
||||
)
|
||||
)
|
||||
private void redirectTurnToDirt(BlockState state, Level level, BlockPos pos, Level l2, BlockPos pos2, Entity entity, float f) {
|
||||
if (InteractionEvent.FARMLAND_TRAMPLE.invoker().trample(level, pos, state, f, entity) == InteractionResult.PASS) {
|
||||
turnToDirt(state, level, pos);
|
||||
private void fallOn(Level level, BlockPos blockPos, Entity entity, float f, CallbackInfo ci) {
|
||||
turnToDirtLocal.set(Triple.of(blockPos.asLong(), f, entity));
|
||||
}
|
||||
|
||||
@Inject(method = "turnToDirt", at = @At("HEAD"), cancellable = true)
|
||||
private static void turnToDirt(BlockState state, Level level, BlockPos pos, CallbackInfo ci) {
|
||||
Triple<Long, Float, Entity> triple = turnToDirtLocal.get();
|
||||
turnToDirtLocal.remove();
|
||||
if (triple != null && triple.getLeft() == pos.asLong()) {
|
||||
if (InteractionEvent.FARMLAND_TRAMPLE.invoker().trample(level, pos, state, triple.getMiddle(), triple.getRight()).value() != null) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.mixin.fabric;
|
||||
|
||||
import me.shedaniel.architectury.event.EventResult;
|
||||
import me.shedaniel.architectury.event.events.EntityEvent;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -49,38 +51,11 @@ import java.util.Random;
|
||||
|
||||
@Mixin(NaturalSpawner.class)
|
||||
public abstract class MixinNaturalSpawner {
|
||||
|
||||
@Unique
|
||||
private static int arch$naturalSpawnOverride = 0;
|
||||
@Unique
|
||||
private static boolean arch$skipChunkGenSpawn = false;
|
||||
|
||||
@Shadow
|
||||
private static boolean isValidPositionForMob(ServerLevel serverLevel, Mob mob, double d) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "spawnCategoryForPosition",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/level/NaturalSpawner;isValidPositionForMob(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/Mob;D)Z",
|
||||
ordinal = 0
|
||||
),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private static void checkNaturalSpawn(MobCategory cat, ServerLevel level, ChunkAccess ca, BlockPos pos, SpawnPredicate pred, AfterSpawnCallback cb, CallbackInfo ci,
|
||||
StructureFeatureManager sfm, ChunkGenerator cg, int i, BlockPos.MutableBlockPos mutablePos, int j, int k, int l, int m, int n,
|
||||
SpawnerData spawnerData, SpawnGroupData sgd, int o, int p, int q, double d, double e, Player player, double f, Mob entity) {
|
||||
InteractionResult result = EntityEvent.CHECK_SPAWN.invoker()
|
||||
.canSpawn(entity, level, d, i, e, MobSpawnType.NATURAL, null);
|
||||
if (result == InteractionResult.FAIL) {
|
||||
arch$naturalSpawnOverride = -1;
|
||||
} else if (result == InteractionResult.SUCCESS || result == InteractionResult.CONSUME) {
|
||||
arch$naturalSpawnOverride = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "spawnCategoryForPosition",
|
||||
at = @At(
|
||||
@@ -90,23 +65,12 @@ public abstract class MixinNaturalSpawner {
|
||||
)
|
||||
)
|
||||
private static boolean overrideNaturalSpawnCondition(ServerLevel level, Mob entity, double f) {
|
||||
return arch$naturalSpawnOverride != -1 && (arch$naturalSpawnOverride == 1 || isValidPositionForMob(level, entity, f));
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "spawnMobsForChunkGeneration",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/entity/Mob;checkSpawnRules(Lnet/minecraft/world/level/LevelAccessor;Lnet/minecraft/world/entity/MobSpawnType;)Z",
|
||||
ordinal = 0
|
||||
),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private static void checkChunkGenSpawn(ServerLevelAccessor level, Biome biome, int cx, int cz, Random dx, CallbackInfo ci,
|
||||
MobSpawnSettings settings, List<SpawnerData> list, int k, int l, SpawnerData spawnerData, int m, SpawnGroupData sgd,
|
||||
int n, int o, int p, int q, int r, boolean b, int s, BlockPos pos, double d, double e, Entity entity) {
|
||||
arch$skipChunkGenSpawn = EntityEvent.CHECK_SPAWN.invoker()
|
||||
.canSpawn(entity, level, d, pos.getY(), e, MobSpawnType.CHUNK_GENERATION, null) == InteractionResult.FAIL;
|
||||
EventResult result = EntityEvent.CHECK_SPAWN.invoker().canSpawn(entity, level, entity.xOld, entity.yOld, entity.zOld, MobSpawnType.NATURAL, null);
|
||||
if (result.value() != null) {
|
||||
return result.value();
|
||||
} else {
|
||||
return isValidPositionForMob(level, entity, f);
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
@@ -118,7 +82,12 @@ public abstract class MixinNaturalSpawner {
|
||||
)
|
||||
)
|
||||
private static boolean overrideChunkGenSpawnCondition(Mob mob, LevelAccessor level, MobSpawnType type) {
|
||||
return !arch$skipChunkGenSpawn && mob.checkSpawnRules(level, type);
|
||||
EventResult result = EntityEvent.CHECK_SPAWN.invoker().canSpawn(mob, level, mob.xOld, mob.yOld, mob.zOld, MobSpawnType.CHUNK_GENERATION, null);
|
||||
if (result.value() != null) {
|
||||
return result.value();
|
||||
} else {
|
||||
return mob.checkSpawnRules(level, type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.mixin.fabric;
|
||||
|
||||
import me.shedaniel.architectury.event.EventResult;
|
||||
import me.shedaniel.architectury.event.events.EntityEvent;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -50,9 +52,9 @@ public abstract class MixinPatrolSpawner {
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private void checkPatrolSpawn(ServerLevel level, BlockPos pos, Random r, boolean b, CallbackInfoReturnable<Boolean> cir, PatrollingMonster entity) {
|
||||
if (EntityEvent.CHECK_SPAWN.invoker().canSpawn(entity, level, pos.getX(), pos.getY(), pos.getZ(), MobSpawnType.PATROL, null) == InteractionResult.FAIL) {
|
||||
cir.setReturnValue(false);
|
||||
cir.cancel();
|
||||
EventResult result = EntityEvent.CHECK_SPAWN.invoker().canSpawn(entity, level, pos.getX(), pos.getY(), pos.getZ(), MobSpawnType.PATROL, null);
|
||||
if (result.value() != null) {
|
||||
cir.setReturnValue(result.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package me.shedaniel.architectury.mixin.fabric;
|
||||
|
||||
import me.shedaniel.architectury.event.events.EntityEvent;
|
||||
@@ -55,7 +56,7 @@ public abstract class MixinPhantomSpawner {
|
||||
private void checkPhantomSpawn(ServerLevel level, boolean bl, boolean bl2, CallbackInfoReturnable<Integer> cir,
|
||||
Random random, int i, Iterator<ServerPlayer> it, Player player, BlockPos pos, DifficultyInstance diff, BlockPos pos2,
|
||||
SpawnGroupData sgd, int l, int m, Phantom entity) {
|
||||
if (EntityEvent.CHECK_SPAWN.invoker().canSpawn(entity, level, pos.getX(), pos.getY(), pos.getZ(), MobSpawnType.NATURAL, null) == InteractionResult.FAIL) {
|
||||
if (EntityEvent.CHECK_SPAWN.invoker().canSpawn(entity, level, pos.getX(), pos.getY(), pos.getZ(), MobSpawnType.NATURAL, null).value() == Boolean.FALSE) {
|
||||
cir.setReturnValue(0);
|
||||
cir.cancel();
|
||||
}
|
||||
|
||||
@@ -21,8 +21,11 @@ package me.shedaniel.architectury.event.forge;
|
||||
|
||||
import me.shedaniel.architectury.event.Actor;
|
||||
import me.shedaniel.architectury.event.Event;
|
||||
import me.shedaniel.architectury.event.EventActor;
|
||||
import me.shedaniel.architectury.event.EventResult;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -63,4 +66,36 @@ public class EventFactoryImpl {
|
||||
});
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static <T> Event<EventActor<T>> attachToForgeEventActor(Event<EventActor<T>> event) {
|
||||
event.register(eventObj -> {
|
||||
if (!(eventObj instanceof net.minecraftforge.eventbus.api.Event)) {
|
||||
throw new ClassCastException(eventObj.getClass() + " is not an instance of forge Event!");
|
||||
}
|
||||
if (!((net.minecraftforge.eventbus.api.Event) eventObj).isCancelable()) {
|
||||
throw new ClassCastException(eventObj.getClass() + " is not cancellable Event!");
|
||||
}
|
||||
MinecraftForge.EVENT_BUS.post((net.minecraftforge.eventbus.api.Event) eventObj);
|
||||
return EventResult.pass();
|
||||
});
|
||||
return event;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static <T> Event<EventActor<T>> attachToForgeEventActorCancellable(Event<EventActor<T>> event) {
|
||||
event.register(eventObj -> {
|
||||
if (!(eventObj instanceof net.minecraftforge.eventbus.api.Event)) {
|
||||
throw new ClassCastException(eventObj.getClass() + " is not an instance of forge Event!");
|
||||
}
|
||||
if (!((net.minecraftforge.eventbus.api.Event) eventObj).isCancelable()) {
|
||||
throw new ClassCastException(eventObj.getClass() + " is not cancellable Event!");
|
||||
}
|
||||
if (MinecraftForge.EVENT_BUS.post((net.minecraftforge.eventbus.api.Event) eventObj)) {
|
||||
return EventResult.interrupt(false);
|
||||
}
|
||||
return EventResult.pass();
|
||||
});
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
package me.shedaniel.architectury.event.forge;
|
||||
|
||||
import me.shedaniel.architectury.event.CompoundEventResult;
|
||||
import me.shedaniel.architectury.event.EventResult;
|
||||
import me.shedaniel.architectury.event.events.PlayerEvent;
|
||||
import me.shedaniel.architectury.event.events.*;
|
||||
import me.shedaniel.architectury.utils.IntValue;
|
||||
@@ -217,7 +219,7 @@ public class EventHandlerImplCommon {
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGH)
|
||||
public static void event(FarmlandTrampleEvent event) {
|
||||
if (InteractionEvent.FARMLAND_TRAMPLE.invoker().trample((Level) event.getWorld(), event.getPos(), event.getState(), event.getFallDistance(), event.getEntity()) == InteractionResult.FAIL) {
|
||||
if (InteractionEvent.FARMLAND_TRAMPLE.invoker().trample((Level) event.getWorld(), event.getPos(), event.getState(), event.getFallDistance(), event.getEntity()).value() != null) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
@@ -225,18 +227,14 @@ public class EventHandlerImplCommon {
|
||||
@SubscribeEvent(priority = EventPriority.HIGH)
|
||||
public static void event(FillBucketEvent event) {
|
||||
ItemStack oldItem = event.getEmptyBucket();
|
||||
InteractionResultHolder<ItemStack> result = PlayerEvent.FILL_BUCKET.invoker().fill(event.getPlayer(), event.getWorld(), oldItem, event.getTarget());
|
||||
switch (result.getResult()) {
|
||||
case FAIL:
|
||||
event.setCanceled(true);
|
||||
break;
|
||||
case SUCCESS:
|
||||
case CONSUME:
|
||||
event.setResult(Event.Result.ALLOW);
|
||||
event.setFilledBucket(result.getObject());
|
||||
break;
|
||||
case PASS:
|
||||
break;
|
||||
CompoundEventResult<ItemStack> result = PlayerEvent.FILL_BUCKET.invoker().fill(event.getPlayer(), event.getWorld(), oldItem, event.getTarget());
|
||||
if (result.interruptsFurtherEvaluation()) {
|
||||
event.setCanceled(true);
|
||||
event.setFilledBucket(result.object());
|
||||
|
||||
if (result.value() != null) {
|
||||
event.setResult(result.value() ? Event.Result.ALLOW : Event.Result.DENY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,17 +245,12 @@ public class EventHandlerImplCommon {
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGH)
|
||||
public static void event(LivingSpawnEvent.CheckSpawn event) {
|
||||
InteractionResult result = EntityEvent.CHECK_SPAWN.invoker().canSpawn(event.getEntity(), event.getWorld(), event.getX(), event.getY(), event.getZ(), event.getSpawnReason(), event.getSpawner());
|
||||
switch (result) {
|
||||
case FAIL:
|
||||
event.setResult(Event.Result.DENY);
|
||||
break;
|
||||
case SUCCESS:
|
||||
case CONSUME:
|
||||
event.setResult(Event.Result.ALLOW);
|
||||
break;
|
||||
case PASS:
|
||||
break;
|
||||
EventResult result = EntityEvent.CHECK_SPAWN.invoker().canSpawn(event.getEntity(), event.getWorld(), event.getX(), event.getY(), event.getZ(), event.getSpawnReason(), event.getSpawner());
|
||||
if (result.interruptsFurtherEvaluation()) {
|
||||
if (result.value() != null) {
|
||||
event.setResult(result.value() == Boolean.TRUE ? Event.Result.ALLOW : Event.Result.DENY);
|
||||
}
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
package me.shedaniel.architectury.test.events;
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import me.shedaniel.architectury.event.CompoundEventResult;
|
||||
import me.shedaniel.architectury.event.EventResult;
|
||||
import me.shedaniel.architectury.event.events.*;
|
||||
import me.shedaniel.architectury.event.events.client.*;
|
||||
import me.shedaniel.architectury.hooks.ExplosionHooks;
|
||||
@@ -118,10 +120,10 @@ public class DebugEvents {
|
||||
});
|
||||
InteractionEvent.FARMLAND_TRAMPLE.register((level, pos, state, distance, entity) -> {
|
||||
if (entity instanceof Player && ((Player) entity).getItemBySlot(EquipmentSlot.FEET).getItem() == Items.DIAMOND_BOOTS) {
|
||||
return InteractionResult.FAIL;
|
||||
return EventResult.interrupt(false);
|
||||
}
|
||||
SINK.accept("%s trampled farmland (%s) at %s in %s (Fall height: %f blocks)", entity, state, pos, level, distance);
|
||||
return InteractionResult.PASS;
|
||||
return EventResult.pass();
|
||||
});
|
||||
LifecycleEvent.SERVER_BEFORE_START.register(instance -> {
|
||||
SINK.accept("Server ready to start");
|
||||
@@ -188,7 +190,7 @@ public class DebugEvents {
|
||||
});
|
||||
PlayerEvent.FILL_BUCKET.register(((player, level, stack, target) -> {
|
||||
SINK.accept("%s used a bucket (%s) in %s%s while looking at %s", player.getScoreboardName(), stack, level.dimension().location(), logSide(level), target == null ? "nothing" : target.getLocation());
|
||||
return InteractionResultHolder.pass(null);
|
||||
return CompoundEventResult.pass();
|
||||
}));
|
||||
LightningEvent.STRIKE.register((bolt, level, pos, toStrike) -> {
|
||||
SINK.accept(bolt.getScoreboardName() + " struck at " + toShortString(pos) + logSide(level));
|
||||
|
||||
Reference in New Issue
Block a user