From 1a2d921ad035235389cc44abe6073bd9add2f6e7 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sun, 8 Nov 2020 17:46:29 +0800 Subject: [PATCH] A few more events --- .../event/events/InteractionEvent.java | 66 +++++++ .../architectury/fluid/FluidStack.java | 186 ++++++++++++++++++ .../architectury/hooks/FluidStackHooks.java | 97 +++++++++ .../architectury/utils/Fraction.java | 177 +++++++++++++++++ .../event/fabric/EventHandlerImpl.java | 7 + .../mixin/fabric/MixinPlayer.java | 14 ++ .../mixin/fabric/MixinResultSlot.java | 16 ++ .../mixin/fabric/client/MixinMinecraft.java | 20 ++ .../client/MixinMultiPlayerGameMode.java | 42 ++++ .../main/resources/architectury.mixins.json | 1 + .../event/forge/EventHandlerImpl.java | 62 +++++- 11 files changed, 682 insertions(+), 6 deletions(-) create mode 100644 common/src/main/java/me/shedaniel/architectury/event/events/InteractionEvent.java create mode 100644 common/src/main/java/me/shedaniel/architectury/fluid/FluidStack.java create mode 100644 common/src/main/java/me/shedaniel/architectury/hooks/FluidStackHooks.java create mode 100644 common/src/main/java/me/shedaniel/architectury/utils/Fraction.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMultiPlayerGameMode.java diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/InteractionEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/InteractionEvent.java new file mode 100644 index 00000000..92c34a69 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/event/events/InteractionEvent.java @@ -0,0 +1,66 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.event.events; + +import me.shedaniel.architectury.event.Event; +import me.shedaniel.architectury.event.EventFactory; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; + +public interface InteractionEvent { + Event LEFT_CLICK_BLOCK = EventFactory.createInteractionResult(LeftClickBlock.class); + Event RIGHT_CLICK_BLOCK = EventFactory.createInteractionResult(RightClickBlock.class); + Event RIGHT_CLICK_ITEM = EventFactory.createInteractionResultHolder(RightClickItem.class); + Event CLIENT_LEFT_CLICK_AIR = EventFactory.createLoop(ClientLeftClickAir.class); + Event CLIENT_RIGHT_CLICK_AIR = EventFactory.createLoop(ClientRightClickAir.class); + Event INTERACT_ENTITY = EventFactory.createInteractionResult(InteractEntity.class); + + interface RightClickBlock { + InteractionResult click(Player player, InteractionHand hand, BlockPos pos, Direction face); + } + + interface LeftClickBlock { + InteractionResult click(Player player, InteractionHand hand, BlockPos pos, Direction face); + } + + interface RightClickItem { + InteractionResultHolder click(Player player, InteractionHand hand); + } + + interface ClientRightClickAir { + void click(Player player, InteractionHand hand); + } + + interface ClientLeftClickAir { + void click(Player player, InteractionHand hand); + } + + interface InteractEntity { + InteractionResult interact(Player player, Entity entity, InteractionHand hand); + } + + interface BlockBreak { + InteractionResult breakBlock(Player player, BlockPos pos, BlockState state); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/fluid/FluidStack.java b/common/src/main/java/me/shedaniel/architectury/fluid/FluidStack.java new file mode 100644 index 00000000..15332c05 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/fluid/FluidStack.java @@ -0,0 +1,186 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.fluid; + +import me.shedaniel.architectury.hooks.FluidStackHooks; +import me.shedaniel.architectury.utils.Fraction; +import me.shedaniel.architectury.utils.NbtType; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.Fluids; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Supplier; + +public final class FluidStack { + private Fraction amount; + @Nullable + private CompoundTag tag; + private Supplier fluid; + + private FluidStack(Supplier fluid, Fraction amount, CompoundTag tag) { + this.fluid = Objects.requireNonNull(fluid); + this.amount = Objects.requireNonNull(amount); + this.tag = tag == null ? null : tag.copy(); + } + + public static FluidStack create(Supplier fluid, Fraction amount, @Nullable CompoundTag tag) { + return new FluidStack(fluid, amount, tag); + } + + public static FluidStack create(Supplier fluid, Fraction amount) { + return create(fluid, amount, null); + } + + public static FluidStack create(FluidStack stack, Fraction amount) { + return create(stack.getRawFluidSupplier(), amount, stack.getTag()); + } + + public static Fraction bucketAmount() { + return FluidStackHooks.bucketAmount(); + } + + public final Fluid getFluid() { + return isEmpty() ? Fluids.EMPTY : getRawFluid(); + } + + @Nullable + public final Fluid getRawFluid() { + return fluid.get(); + } + + public final Supplier getRawFluidSupplier() { + return fluid; + } + + public boolean isEmpty() { + return getRawFluid() == Fluids.EMPTY || !amount.isGreaterThan(Fraction.zero()); + } + + public Fraction getAmount() { + return isEmpty() ? Fraction.zero() : amount; + } + + public void setAmount(Fraction amount) { + this.amount = Objects.requireNonNull(amount); + } + + public void grow(Fraction amount) { + setAmount(this.amount.add(amount)); + } + + public void shrink(Fraction amount) { + setAmount(this.amount.minus(amount)); + } + + public boolean hasTag() { + return tag != null; + } + + @Nullable + public CompoundTag getTag() { + return tag; + } + + public void setTag(@Nullable CompoundTag tag) { + this.tag = tag; + } + + public CompoundTag getOrCreateTag() { + if (tag == null) + setTag(new CompoundTag()); + return tag; + } + + @Nullable + public CompoundTag getChildTag(String childName) { + if (tag == null) + return null; + return tag.getCompound(childName); + } + + public CompoundTag getOrCreateChildTag(String childName) { + getOrCreateTag(); + CompoundTag child = tag.getCompound(childName); + if (!tag.contains(childName, NbtType.COMPOUND)) { + tag.put(childName, child); + } + return child; + } + + public void removeChildTag(String childName) { + if (tag != null) + tag.remove(childName); + } + + public Component getName() { + return FluidStackHooks.getName(this); + } + + public String getTranslationKey() { + return FluidStackHooks.getTranslationKey(this); + } + + public FluidStack copy() { + return new FluidStack(fluid, amount, tag); + } + + @Override + public final int hashCode() { + int code = 1; + code = 31 * code + getFluid().hashCode(); + code = 31 * code + amount.hashCode(); + if (tag != null) + code = 31 * code + tag.hashCode(); + return code; + } + + @Override + public final boolean equals(Object o) { + if (!(o instanceof FluidStack)) { + return false; + } + return isFluidStackEqual((FluidStack) o); + } + + public boolean isFluidStackEqual(FluidStack other) { + return getFluid() == other.getFluid() && getAmount().equals(other.getAmount()) && isTagEqual(other); + } + + private boolean isTagEqual(FluidStack other) { + return tag == null ? other.tag == null : other.tag != null && tag.equals(other.tag); + } + + public static FluidStack read(FriendlyByteBuf buf) { + return FluidStackHooks.read(buf); + } + + public static FluidStack read(CompoundTag tag) { + return FluidStackHooks.read(tag); + } + + public void write(FriendlyByteBuf buf) { + FluidStackHooks.write(this, buf); + } + + public CompoundTag write(CompoundTag tag) { + return FluidStackHooks.write(this, tag); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/FluidStackHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/FluidStackHooks.java new file mode 100644 index 00000000..b64a99b8 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/hooks/FluidStackHooks.java @@ -0,0 +1,97 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.hooks; + +import me.shedaniel.architectury.ArchitecturyPopulator; +import me.shedaniel.architectury.Populatable; +import me.shedaniel.architectury.fluid.FluidStack; +import me.shedaniel.architectury.utils.Fraction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; + +public class FluidStackHooks { + private FluidStackHooks() {} + + @Populatable + private static final Impl IMPL = null; + + public static Component getName(FluidStack stack) { + return IMPL.getName(stack); + } + + public static String getTranslationKey(FluidStack stack) { + return IMPL.getTranslationKey(stack); + } + + /** + * Platform-specific FluidStack read. + */ + public static FluidStack read(FriendlyByteBuf buf) { + return IMPL.read(buf); + } + + /** + * Platform-specific FluidStack write. + */ + public static void write(FluidStack stack, FriendlyByteBuf buf) { + IMPL.write(stack, buf); + } + + /** + * Platform-specific FluidStack read. + */ + public static FluidStack read(CompoundTag tag) { + return IMPL.read(tag); + } + + /** + * Platform-specific FluidStack write. + */ + public static CompoundTag write(FluidStack stack, CompoundTag tag) { + return IMPL.write(stack, tag); + } + + /** + * Platform-specific bucket amount. + * Forge: 1000 + * Fabric: 1 + */ + public static Fraction bucketAmount() { + return IMPL.bucketAmount(); + } + + public interface Impl { + Fraction bucketAmount(); + + Component getName(FluidStack stack); + + String getTranslationKey(FluidStack stack); + + FluidStack read(FriendlyByteBuf buf); + + void write(FluidStack stack, FriendlyByteBuf buf); + + FluidStack read(CompoundTag tag); + + CompoundTag write(FluidStack stack, CompoundTag tag); + } + + static { + ArchitecturyPopulator.populate(); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/utils/Fraction.java b/common/src/main/java/me/shedaniel/architectury/utils/Fraction.java new file mode 100644 index 00000000..b950fe8c --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/utils/Fraction.java @@ -0,0 +1,177 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.utils; + +import com.google.common.math.LongMath; +import org.jetbrains.annotations.NotNull; + +import java.text.DecimalFormat; + +public final class Fraction extends Number implements Comparable { + private static final Fraction ZERO = ofWhole(0); + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###.###"); + private final long numerator; + private final long denominator; + private boolean simplified; + + private Fraction(long numerator, long denominator) { + if (denominator > 0) { + this.numerator = numerator; + this.denominator = denominator; + } else if (denominator < 0) { + this.numerator = -numerator; + this.denominator = -denominator; + } else { + throw new ArithmeticException("/ by zero"); + } + this.simplified = (this.numerator >= -1 && this.numerator <= 1) || this.denominator == 1; + } + + public static Fraction zero() { + return ZERO; + } + + public static Fraction ofWhole(long whole) { + return new Fraction(whole, 1); + } + + public static Fraction of(long numerator, long denominator) { + return new Fraction(numerator, denominator); + } + + public static Fraction of(long whole, long numerator, long denominator) { + return of(numerator + whole * denominator, denominator); + } + + public static Fraction from(double value) { + int whole = (int) value; + double part = value - whole; + int i = 1; + + while (true) { + double tem = part / (1D / i); + long numerator = Math.round(tem); + if (Math.abs(tem - numerator) < 0.00001) { + return of(whole, numerator, i); + } + i++; + } + } + + public long getNumerator() { + return numerator; + } + + public long getDenominator() { + return denominator; + } + + public Fraction add(Fraction other) { + if (other.numerator == 0) return this; + return of(numerator * other.denominator + other.numerator * denominator, denominator * other.denominator); + } + + public Fraction minus(Fraction other) { + if (other.numerator == 0) return this; + return of(numerator * other.denominator - other.numerator * denominator, denominator * other.denominator); + } + + public Fraction multiply(Fraction other) { + if (other.numerator == other.denominator) return this; + return of(numerator * other.numerator, denominator * other.denominator); + } + + public Fraction divide(Fraction other) { + if (other.numerator == other.denominator) return this; + return of(numerator * other.denominator, denominator * other.numerator); + } + + public Fraction inverse() { + if (numerator == denominator) + return this; + Fraction fraction = of(denominator, numerator); + fraction.simplified = fraction.simplified && this.simplified; + return fraction; + } + + public Fraction simplify() { + if (simplified) + return this; + if (numerator == 0) + return ofWhole(0); + long gcd = LongMath.gcd(Math.abs(numerator), denominator); + Fraction fraction = of(numerator / gcd, denominator / gcd); + fraction.simplified = true; + return fraction; + } + + public boolean isGreaterThan(Fraction fraction) { + return compareTo(fraction) > 0; + } + + public boolean isLessThan(Fraction fraction) { + return compareTo(fraction) < 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Fraction fraction = (Fraction) o; + return numerator * fraction.denominator == denominator * fraction.numerator; + } + + @Override + public int hashCode() { + return Double.hashCode(doubleValue()); + } + + @Override + public int compareTo(@NotNull Fraction fraction) { + return Long.compare(numerator * fraction.denominator, denominator * fraction.numerator); + } + + @Override + public int intValue() { + return (int) longValue(); + } + + @Override + public long longValue() { + return numerator / denominator; + } + + @Override + public float floatValue() { + return (float) numerator / denominator; + } + + @Override + public double doubleValue() { + return (double) numerator / denominator; + } + + public String toDecimalString() { + return DECIMAL_FORMAT.format(doubleValue()); + } + + @Override + public String toString() { + if (intValue() == doubleValue()) return toDecimalString(); + return String.format("%s (%d/%d)", toDecimalString(), numerator, denominator); + } +} \ No newline at end of file diff --git a/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventHandlerImpl.java b/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventHandlerImpl.java index bc70f081..7f12e543 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventHandlerImpl.java +++ b/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventHandlerImpl.java @@ -26,6 +26,9 @@ import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; +import net.fabricmc.fabric.api.event.player.AttackBlockCallback; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.fabric.api.event.player.UseItemCallback; import net.minecraft.commands.Commands; public class EventHandlerImpl implements EventHandler.Impl { @@ -59,6 +62,10 @@ public class EventHandlerImpl implements EventHandler.Impl { ServerWorldEvents.UNLOAD.register((server, world) -> LifecycleEvent.SERVER_WORLD_UNLOAD.invoker().act(world)); CommandRegistrationCallback.EVENT.register((commandDispatcher, b) -> CommandRegistrationEvent.EVENT.invoker().register(commandDispatcher, b ? Commands.CommandSelection.DEDICATED : Commands.CommandSelection.INTEGRATED)); + + UseItemCallback.EVENT.register((player, world, hand) -> InteractionEvent.RIGHT_CLICK_ITEM.invoker().click(player, hand)); + UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> InteractionEvent.RIGHT_CLICK_BLOCK.invoker().click(player, hand, hitResult.getBlockPos(), hitResult.getDirection())); + AttackBlockCallback.EVENT.register((player, world, hand, pos, face) -> InteractionEvent.LEFT_CLICK_BLOCK.invoker().click(player, hand, pos, face)); } @Override diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayer.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayer.java index 7b9a8013..fc736886 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayer.java +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinPlayer.java @@ -16,9 +16,12 @@ package me.shedaniel.architectury.mixin.fabric; +import me.shedaniel.architectury.event.events.InteractionEvent; import me.shedaniel.architectury.event.events.PlayerEvent; import me.shedaniel.architectury.event.events.TickEvent; +import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; @@ -46,4 +49,15 @@ public class MixinPlayer { cir.setReturnValue(null); } } + + @Inject(method = "interactOn", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/entity/player/Player;getItemInHand(Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/world/item/ItemStack;", + ordinal = 0), + cancellable = true) + private void entityInteract(Entity entity, InteractionHand interactionHand, CallbackInfoReturnable cir) { + InteractionResult result = InteractionEvent.INTERACT_ENTITY.invoker().interact((Player) (Object) this, entity, interactionHand); + if (result != InteractionResult.PASS) { + cir.setReturnValue(result); + } + } } diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinResultSlot.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinResultSlot.java index 6ff7d606..42321716 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinResultSlot.java +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinResultSlot.java @@ -1,3 +1,19 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package me.shedaniel.architectury.mixin.fabric; import me.shedaniel.architectury.event.events.PlayerEvent; diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java index 0c6697eb..cd575368 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMinecraft.java @@ -16,24 +16,44 @@ package me.shedaniel.architectury.mixin.fabric.client; +import me.shedaniel.architectury.event.events.InteractionEvent; import me.shedaniel.architectury.event.events.PlayerEvent; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.HitResult; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; @Mixin(Minecraft.class) public class MixinMinecraft { @Shadow @Nullable public LocalPlayer player; + @Shadow @Nullable public HitResult hitResult; + @Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/chat/NarratorChatListener;clear()V")) private void handleLogin(Screen screen, CallbackInfo ci) { PlayerEvent.CLIENT_PLAYER_QUIT.invoker().quit(player); } + + @Inject(method = "startUseItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z", ordinal = 1), + locals = LocalCapture.CAPTURE_FAILHARD) + private void rightClickAir(CallbackInfo ci, InteractionHand var1[], int var2, int var3, InteractionHand interactionHand, ItemStack itemStack) { + if (itemStack.isEmpty() && (this.hitResult == null || this.hitResult.getType() == HitResult.Type.MISS)) { + InteractionEvent.CLIENT_RIGHT_CLICK_AIR.invoker().click(player, interactionHand); + } + } + + @Inject(method = "startAttack", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;resetAttackStrengthTicker()V", ordinal = 0)) + private void leftClickAir(CallbackInfo ci) { + InteractionEvent.CLIENT_LEFT_CLICK_AIR.invoker().click(player, InteractionHand.MAIN_HAND); + } } diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMultiPlayerGameMode.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMultiPlayerGameMode.java new file mode 100644 index 00000000..ba1bad10 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/client/MixinMultiPlayerGameMode.java @@ -0,0 +1,42 @@ +/* + * Copyright 2020 shedaniel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package me.shedaniel.architectury.mixin.fabric.client; + +import me.shedaniel.architectury.event.events.InteractionEvent; +import net.minecraft.client.multiplayer.MultiPlayerGameMode; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(MultiPlayerGameMode.class) +public class MixinMultiPlayerGameMode { + @Inject(method = "interact", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;send(Lnet/minecraft/network/protocol/Packet;)V", + shift = At.Shift.AFTER), + cancellable = true) + private void entityInteract(Player player, Entity entity, InteractionHand interactionHand, CallbackInfoReturnable cir) { + InteractionResult result = InteractionEvent.INTERACT_ENTITY.invoker().interact(player, entity, interactionHand); + if (result != InteractionResult.PASS) { + cir.setReturnValue(result); + } + } +} diff --git a/fabric/src/main/resources/architectury.mixins.json b/fabric/src/main/resources/architectury.mixins.json index be21d158..91aac86b 100644 --- a/fabric/src/main/resources/architectury.mixins.json +++ b/fabric/src/main/resources/architectury.mixins.json @@ -9,6 +9,7 @@ "client.MixinDebugScreenOverlay", "client.MixinGameRenderer", "client.MixinMinecraft", + "client.MixinMultiPlayerGameMode", "client.MixinScreen" ], "mixins": [ diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImpl.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImpl.java index dd57ce32..086b21e2 100644 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImpl.java +++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImpl.java @@ -17,11 +17,13 @@ package me.shedaniel.architectury.event.forge; import me.shedaniel.architectury.event.EventHandler; +import me.shedaniel.architectury.event.events.PlayerEvent; import me.shedaniel.architectury.event.events.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.IGuiEventListener; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.item.ItemStack; import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResultType; import net.minecraft.util.text.ITextComponent; @@ -38,14 +40,12 @@ import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.event.entity.item.ItemTossEvent; import net.minecraftforge.event.entity.living.LivingAttackEvent; import net.minecraftforge.event.entity.living.LivingDeathEvent; -import net.minecraftforge.event.entity.player.AdvancementEvent; -import net.minecraftforge.event.entity.player.EntityItemPickupEvent; -import net.minecraftforge.event.entity.player.ItemTooltipEvent; -import net.minecraftforge.event.entity.player.PlayerContainerEvent; +import net.minecraftforge.event.entity.player.*; import net.minecraftforge.event.entity.player.PlayerEvent.*; import net.minecraftforge.event.world.ExplosionEvent.Detonate; import net.minecraftforge.event.world.ExplosionEvent.Start; import net.minecraftforge.event.world.WorldEvent; +import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.event.server.FMLServerStartedEvent; @@ -163,6 +163,16 @@ public class EventHandlerImpl implements EventHandler.Impl { public static void event(GuiScreenEvent.DrawScreenEvent.Post event) { GuiEvent.RENDER_POST.invoker().render(event.getGui(), event.getMatrixStack(), event.getMouseX(), event.getMouseY(), event.getRenderPartialTicks()); } + + @SubscribeEvent + public static void event(PlayerInteractEvent.RightClickEmpty event) { + InteractionEvent.CLIENT_RIGHT_CLICK_AIR.invoker().click(event.getPlayer(), event.getHand()); + } + + @SubscribeEvent + public static void event(PlayerInteractEvent.LeftClickEmpty event) { + InteractionEvent.CLIENT_LEFT_CLICK_AIR.invoker().click(event.getPlayer(), event.getHand()); + } } public static class Common { @@ -347,16 +357,56 @@ public class EventHandlerImpl implements EventHandler.Impl { public static void event(ItemTossEvent event) { PlayerEvent.DROP_ITEM.invoker().drop(event.getPlayer(), event.getEntityItem()); } - + @SubscribeEvent public static void event(PlayerContainerEvent.Open event) { PlayerEvent.OPEN_MENU.invoker().open(event.getPlayer(), event.getContainer()); } - + @SubscribeEvent public static void event(PlayerContainerEvent.Close event) { PlayerEvent.CLOSE_MENU.invoker().close(event.getPlayer(), event.getContainer()); } + + @SubscribeEvent + public static void event(PlayerInteractEvent.RightClickItem event) { + ActionResult result = InteractionEvent.RIGHT_CLICK_ITEM.invoker().click(event.getPlayer(), event.getHand()); + if (result.getResult() != ActionResultType.PASS) { + event.setCanceled(true); + event.setCancellationResult(result.getResult()); + } + } + + @SubscribeEvent + public static void event(PlayerInteractEvent.RightClickBlock event) { + ActionResultType result = InteractionEvent.RIGHT_CLICK_BLOCK.invoker().click(event.getPlayer(), event.getHand(), event.getPos(), event.getFace()); + if (result != ActionResultType.PASS) { + event.setCanceled(true); + event.setCancellationResult(result); + event.setUseBlock(Event.Result.DENY); + event.setUseItem(Event.Result.DENY); + } + } + + @SubscribeEvent + public static void event(PlayerInteractEvent.EntityInteract event) { + ActionResultType result = InteractionEvent.INTERACT_ENTITY.invoker().interact(event.getPlayer(), event.getTarget(), event.getHand()); + if (result != ActionResultType.PASS) { + event.setCanceled(true); + event.setCancellationResult(result); + } + } + + @SubscribeEvent + public static void event(PlayerInteractEvent.LeftClickBlock event) { + ActionResultType result = InteractionEvent.LEFT_CLICK_BLOCK.invoker().click(event.getPlayer(), event.getHand(), event.getPos(), event.getFace()); + if (result != ActionResultType.PASS) { + event.setCanceled(true); + event.setCancellationResult(result); + event.setUseBlock(Event.Result.DENY); + event.setUseItem(Event.Result.DENY); + } + } } @OnlyIn(Dist.DEDICATED_SERVER)