From f78c88a0eb01ec995f3c3e4b33b27b2d9f767e7b Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 18 Nov 2020 00:28:15 +0800 Subject: [PATCH] Add block break and block place events --- README.md | 19 ++++++++ .../me/shedaniel/architectury/ForgeEvent.java | 16 +++++++ .../event/events/EntityEvent.java | 8 ++++ .../event/events/PlayerEvent.java | 8 ++++ fabric/build.gradle | 2 +- .../event/fabric/EventFactoryImpl.java | 16 +++++++ .../mixin/fabric/MixinBlockItem.java | 40 ++++++++++++++++ .../fabric/MixinServerPlayerGameMode.java | 48 +++++++++++++++++++ .../main/resources/architectury.mixins.json | 6 +-- .../event/forge/EventFactoryImpl.java | 16 +++++++ .../event/forge/EventHandlerImplClient.java | 16 +++++++ .../event/forge/EventHandlerImplCommon.java | 38 +++++++++++++++ .../event/forge/EventHandlerImplServer.java | 16 +++++++ 13 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 README.md create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinBlockItem.java create mode 100644 fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerPlayerGameMode.java diff --git a/README.md b/README.md new file mode 100644 index 00000000..5c1309be --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Architectury +A intermediary api aimed to ease developing multiplatform mods. + +### What is Architectury +Architectury is an api to abstract calls to fabric api and forge api as both loader has different implementations of what can be perceived as the same thing. + +Architectury updates regularly, with new hooks and features. Currently contains over **60** events hooks, networking abstraction, loader calls abstraction, game registry abstraction and an easy to use @ExpectPlatform annotation. + +### Advantages of Architectury +- Open sourced +- Less boilerplate for your multiplatform mod + +### Getting started with making multiplatform mods +Gradle Plugin: https://github.com/architectury/architect-plugin + +Example Mod: https://github.com/architectury/architect-example-mod + +### Credits +This library bundles typetools, which you can find its license [here](https://github.com/jhalterman/typetools/blob/master/LICENSE.txt "") \ No newline at end of file diff --git a/common/src/main/java/me/shedaniel/architectury/ForgeEvent.java b/common/src/main/java/me/shedaniel/architectury/ForgeEvent.java index c17f47a0..2de4af78 100644 --- a/common/src/main/java/me/shedaniel/architectury/ForgeEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/ForgeEvent.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; import java.lang.annotation.ElementType; diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java index 9538ab67..bf9fb365 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/EntityEvent.java @@ -18,11 +18,14 @@ 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.world.InteractionResult; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; public interface EntityEvent { /** @@ -37,6 +40,7 @@ public interface EntityEvent { * Invoked before entity is added to a world, equivalent to forge's {@code EntityJoinWorldEvent}. */ Event ADD = EventFactory.createInteractionResult(Add.class); + Event PLACE_BLOCK = EventFactory.createInteractionResult(PlaceBlock.class); interface LivingDeath { InteractionResult die(LivingEntity entity, DamageSource source); @@ -49,4 +53,8 @@ public interface EntityEvent { interface Add { InteractionResult add(Entity entity, Level world); } + + interface PlaceBlock { + InteractionResult placeBlock(Level world, BlockPos pos, BlockState state, @Nullable Entity placer); + } } diff --git a/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java b/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java index 717d4661..6879148d 100644 --- a/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java +++ b/common/src/main/java/me/shedaniel/architectury/event/events/PlayerEvent.java @@ -22,6 +22,7 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.advancements.Advancement; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.InteractionResult; @@ -29,6 +30,8 @@ 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; public interface PlayerEvent { Event PLAYER_JOIN = EventFactory.createLoop(PlayerJoin.class); @@ -46,6 +49,7 @@ public interface PlayerEvent { Event DROP_ITEM = EventFactory.createLoop(DropItem.class); Event OPEN_MENU = EventFactory.createLoop(OpenMenu.class); Event CLOSE_MENU = EventFactory.createLoop(CloseMenu.class); + Event BREAK_BLOCK = EventFactory.createInteractionResult(BreakBlock.class); interface PlayerJoin { void join(ServerPlayer player); @@ -87,6 +91,10 @@ public interface PlayerEvent { InteractionResult drop(Player player, ItemEntity entity); } + interface BreakBlock { + InteractionResult breakBlock(Level world, BlockPos pos, BlockState state, ServerPlayer player); + } + interface OpenMenu { void open(Player player, AbstractContainerMenu menu); } diff --git a/fabric/build.gradle b/fabric/build.gradle index 7074b574..22149351 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -21,7 +21,7 @@ dependencies { implementation "net.jodah:typetools:0.6.2" shadow "net.jodah:typetools:0.6.2" - compileOnly(project(path: ":common", configuration: "transformed")) { + compileOnly(project(path: ":common")) { transitive = false } runtimeOnly(project(path: ":common", configuration: "transformed")) { diff --git a/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java b/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java index 9096eb4c..a91f9d68 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.java +++ b/fabric/src/main/java/me/shedaniel/architectury/event/fabric/EventFactoryImpl.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.event.fabric; import me.shedaniel.architectury.event.Event; diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinBlockItem.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinBlockItem.java new file mode 100644 index 00000000..765bb0b7 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinBlockItem.java @@ -0,0 +1,40 @@ +/* + * 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.EntityEvent; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.context.BlockPlaceContext; +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(BlockItem.class) +public abstract class MixinBlockItem { + @Inject(method = "place", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/item/context/BlockPlaceContext;getClickedPos()Lnet/minecraft/core/BlockPos;"), + cancellable = true) + private void place(BlockPlaceContext context, CallbackInfoReturnable cir) { + InteractionResult result = EntityEvent.PLACE_BLOCK.invoker().placeBlock(context.getLevel(), context.getClickedPos(), context.getLevel().getBlockState(context.getClickedPos()), context.getPlayer()); + if (result != InteractionResult.PASS) { + cir.setReturnValue(result); + } + } +} \ No newline at end of file diff --git a/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerPlayerGameMode.java b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerPlayerGameMode.java new file mode 100644 index 00000000..867c3f4b --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/mixin/fabric/MixinServerPlayerGameMode.java @@ -0,0 +1,48 @@ +/* + * 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; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.level.block.state.BlockState; +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.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ServerPlayerGameMode.class) +public class MixinServerPlayerGameMode { + @Shadow public ServerLevel level; + + @Shadow public ServerPlayer player; + + @Inject(method = "destroyBlock", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/block/state/BlockState;getBlock()Lnet/minecraft/world/level/block/Block;", + ordinal = 0), + locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) + private void onBreak(BlockPos blockPos, CallbackInfoReturnable cir, BlockState state) { + if (PlayerEvent.BREAK_BLOCK.invoker().breakBlock(this.level, blockPos, state, this.player) == InteractionResult.FAIL) { + cir.setReturnValue(false); + } + } +} diff --git a/fabric/src/main/resources/architectury.mixins.json b/fabric/src/main/resources/architectury.mixins.json index d83880ad..8d9309d0 100644 --- a/fabric/src/main/resources/architectury.mixins.json +++ b/fabric/src/main/resources/architectury.mixins.json @@ -14,9 +14,9 @@ "client.MixinTextureAtlas" ], "mixins": [ - "ExplosionPreInvoker", "LivingDeathInvoker", "MixinCommands", "MixinExplosion", "MixinFurnaceResultSlot", "MixinItemEntity", "MixinLivingEntity", - "MixinPlayer", "MixinPlayerAdvancements", "MixinPlayerList", "MixinResultSlot", "MixinServerGamePacketListenerImpl", "MixinServerLevel", - "MixinServerPlayer", "PlayerAttackInvoker" + "ExplosionPreInvoker", "LivingDeathInvoker", "MixinBlockItem", "MixinCommands", "MixinExplosion", "MixinFurnaceResultSlot", "MixinItemEntity", + "MixinLivingEntity", "MixinPlayer", "MixinPlayerAdvancements", "MixinPlayerList", "MixinResultSlot", "MixinServerGamePacketListenerImpl", + "MixinServerLevel", "MixinServerPlayer", "MixinServerPlayerGameMode", "PlayerAttackInvoker" ], "injectors": { "defaultRequire": 1 diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java index 81f0a14a..f7c9ec2f 100644 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.java +++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventFactoryImpl.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.event.forge; import me.shedaniel.architectury.event.Event; diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java index 4730acaf..796c9606 100644 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.java +++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplClient.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.event.forge; import me.shedaniel.architectury.event.events.TextureStitchEvent; diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplCommon.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplCommon.java index 9727378c..2c6de353 100644 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplCommon.java +++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplCommon.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.event.forge; import me.shedaniel.architectury.event.events.*; @@ -6,6 +22,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResultType; import net.minecraft.util.text.ITextComponent; +import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.event.CommandEvent; import net.minecraftforge.event.RegisterCommandsEvent; @@ -23,6 +40,7 @@ import net.minecraftforge.event.entity.player.EntityItemPickupEvent; import net.minecraftforge.event.entity.player.PlayerContainerEvent; import net.minecraftforge.event.entity.player.PlayerEvent.*; import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.world.ExplosionEvent.Detonate; import net.minecraftforge.event.world.ExplosionEvent.Start; import net.minecraftforge.event.world.WorldEvent; @@ -268,6 +286,26 @@ public class EventHandlerImplCommon { } } + @SubscribeEvent + public static void event(BlockEvent.BreakEvent event) { + if (event.getPlayer() instanceof ServerPlayerEntity && event.getWorld() instanceof World) { + ActionResultType result = PlayerEvent.BREAK_BLOCK.invoker().breakBlock((World) event.getWorld(), event.getPos(), event.getState(), (ServerPlayerEntity) event.getPlayer()); + if (result != ActionResultType.PASS) { + event.setCanceled(true); + } + } + } + + @SubscribeEvent + public static void event(BlockEvent.EntityPlaceEvent event) { + if (event.getWorld() instanceof World) { + ActionResultType result = EntityEvent.PLACE_BLOCK.invoker().placeBlock((World) event.getWorld(), event.getPos(), event.getState(), event.getEntity()); + if (result != ActionResultType.PASS) { + event.setCanceled(true); + } + } + } + public static class ModBasedEventHandler { } diff --git a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplServer.java b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplServer.java index 031ca2e6..4c1efefe 100644 --- a/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplServer.java +++ b/forge/src/main/java/me/shedaniel/architectury/event/forge/EventHandlerImplServer.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.event.forge; import net.minecraftforge.api.distmarker.Dist;