Skip to content

Commit

Permalink
generalize adventure fixes to tag based restrictions
Browse files Browse the repository at this point in the history
  • Loading branch information
HamaIndustries committed Sep 3, 2024
1 parent d2f3d61 commit 020f8ac
Show file tree
Hide file tree
Showing 14 changed files with 301 additions and 50 deletions.
1 change: 1 addition & 0 deletions src/main/java/net/modfest/fireblanket/Fireblanket.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/net/modfest/fireblanket/FireblanketConstants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.modfest.fireblanket;

import net.minecraft.block.Block;
import net.minecraft.entity.EntityType;
import net.minecraft.item.Item;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;

public class FireblanketConstants {
public static final String MOD_ID = "fireblanket";

public static final TagKey<Block> BLOCK_INTERACTION_RESTRICTED = tag(RegistryKeys.BLOCK, "block_interaction_restricted");
public static final TagKey<Item> ITEM_INTERACTION_RESTRICTED = tag(RegistryKeys.ITEM, "item_interaction_restricted");
public static final TagKey<EntityType<?>> ENTITY_INTERACTION_RESTRICTED = tag(RegistryKeys.ENTITY_TYPE, "entity_interaction_restricted");

public static final TagKey<EntityType<?>> ENTITY_ATTACK_RESTRICTED = tag(RegistryKeys.ENTITY_TYPE, "entity_attack_restricted");

public static Identifier id(String id) { return Identifier.of(MOD_ID, id); }

private static <T> TagKey<T> tag(RegistryKey<? extends Registry<T>> reg, String id) {
return TagKey.of(reg, id(id));
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package net.modfest.fireblanket.mixin.adventure_fix;

import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.world.World;
import net.modfest.fireblanket.FireblanketConstants;
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;

@Mixin(ItemStack.class)
public abstract class MixinItemStack {
@Shadow public abstract boolean isIn(TagKey<Item> tag);

// corner case: item is allowed, but block interact is not allowed.
// preventing use on blocks here might prevent non-interacting items
// like rulers from simply using the blockpos.
@Inject(method = "useOnBlock", at = @At("HEAD"), cancellable = true)
private void fireblanket$filterItemUseOnBlockByTag(ItemUsageContext context, CallbackInfoReturnable<ActionResult> ci) {
PlayerEntity player = context.getPlayer();
if (player == null) return;
if (!player.getAbilities().allowModifyWorld && this.isIn(FireblanketConstants.ITEM_INTERACTION_RESTRICTED)) {
ci.setReturnValue(ActionResult.FAIL);
ci.cancel();
}
}

@Inject(method = "use", at = @At("HEAD"), cancellable = true)
private void fireblanket$filterItemUseByTag(World world, PlayerEntity user, Hand hand, CallbackInfoReturnable<TypedActionResult<ItemStack>> ci) {
if (!user.getAbilities().allowModifyWorld && this.isIn(FireblanketConstants.ITEM_INTERACTION_RESTRICTED)) {
ci.setReturnValue(TypedActionResult.fail(user.getStackInHand(hand)));
ci.cancel();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package net.modfest.fireblanket.mixin.adventure_fix;

import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.util.Hand;
import net.modfest.fireblanket.FireblanketConstants;
import org.spongepowered.asm.mixin.Final;
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;

/**
* Vanilla uses an anonymous class to process entity interaction serverside, so there's not
* really a better place to do this. Entity#interactAt is only called here,
* so we might as well filter both.
*
* While the client has an easy way to do the check in ClientPlayerInteractionManager,
* the ServerPlayerInteractionManager does not. /shrug
*/
@Mixin(targets = "net.minecraft.server.network.ServerPlayNetworkHandler$1")
public class MixinPlayerInteractEntityC2SPacketHandler {
@Shadow @Final private ServerPlayNetworkHandler field_28963; // outer this
@Shadow @Final private Entity field_28962; // target entity captured variable

@Inject(
method = "Lnet/minecraft/server/network/ServerPlayNetworkHandler$1;processInteract(Lnet/minecraft/util/Hand;Lnet/minecraft/server/network/ServerPlayNetworkHandler$Interaction;)V",
at = @At("HEAD"),
cancellable = true
)
private void fireblanket$filterEntityInteractByTag(Hand hand, ServerPlayNetworkHandler.Interaction action, CallbackInfo ci) {
PlayerEntity player = field_28963.player;
ItemStack stack = player.getStackInHand(hand);
if (!player.getAbilities().allowModifyWorld && (
stack.isIn(FireblanketConstants.ITEM_INTERACTION_RESTRICTED) ||
field_28962.getType().isIn(FireblanketConstants.ENTITY_INTERACTION_RESTRICTED))) {
ci.cancel();
}
}

@Inject(
method = "Lnet/minecraft/server/network/ServerPlayNetworkHandler$1;attack()V",
at = @At("HEAD"),
cancellable = true
)
private void fireblanket$filterAttackEntityByTag(CallbackInfo ci) {
PlayerEntity player = field_28963.player;
if (!player.getAbilities().allowModifyWorld && field_28962.getType().isIn(FireblanketConstants.ENTITY_ATTACK_RESTRICTED)) {
ci.cancel();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package net.modfest.fireblanket.mixin.adventure_fix;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerInteractionManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.ItemActionResult;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.world.World;
import net.modfest.fireblanket.FireblanketConstants;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(ServerPlayerInteractionManager.class)
public class MixinServerPlayerInteractionManager {
@WrapOperation(
method = "interactBlock",
at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;onUseWithItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;Lnet/minecraft/util/hit/BlockHitResult;)Lnet/minecraft/util/ItemActionResult;")
)
private ItemActionResult fireblanket$filterItemBlockInteractByTag(BlockState blockState, ItemStack stack, World world, PlayerEntity player, Hand hand, BlockHitResult hitResult, Operation<ItemActionResult> op) {
if (!player.getAbilities().allowModifyWorld) {
if (stack.isIn(FireblanketConstants.ITEM_INTERACTION_RESTRICTED)) {
return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
}
if (blockState.isIn(FireblanketConstants.BLOCK_INTERACTION_RESTRICTED)) {
return ItemActionResult.FAIL;
}
}
return op.call(blockState, stack, world, player, hand, hitResult);
}

@WrapOperation(
method = "interactBlock",
at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;onUse(Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/hit/BlockHitResult;)Lnet/minecraft/util/ActionResult;")
)
private ActionResult fireblanket$filterBlockInteractByTag(BlockState blockState, World world, PlayerEntity player, BlockHitResult hitResult, Operation<ActionResult> op) {
if (!player.getAbilities().allowModifyWorld && blockState.isIn(FireblanketConstants.BLOCK_INTERACTION_RESTRICTED)) {
return ActionResult.FAIL;
}
return op.call(blockState, world, player, hitResult);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package net.modfest.fireblanket.mixin.client.adventure_fix;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.block.BlockState;
import net.minecraft.client.network.ClientPlayerInteractionManager;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.ItemActionResult;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.world.World;
import net.modfest.fireblanket.FireblanketConstants;
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.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(ClientPlayerInteractionManager.class)
public class MixinClientPlayerInteractionManager {
@Inject(
method = "interactEntity",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;syncSelectedSlot()V", shift = At.Shift.AFTER),
cancellable = true)
private void fireblanket$filterEntityInteractByTag(PlayerEntity player, Entity entity, Hand hand, CallbackInfoReturnable<ActionResult> ci) {
if (!player.getAbilities().allowModifyWorld && (
entity.getType().isIn(FireblanketConstants.ENTITY_INTERACTION_RESTRICTED) ||
player.getStackInHand(hand).isIn(FireblanketConstants.ITEM_INTERACTION_RESTRICTED))) {
ci.setReturnValue(ActionResult.FAIL);
ci.cancel();
}
}

@Inject(
method = "interactEntityAtLocation",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;syncSelectedSlot()V", shift = At.Shift.AFTER),
cancellable = true
)
private void fireblanket$filterEntityInteractAtLocationByTag(PlayerEntity player, Entity entity, EntityHitResult hitResult, Hand hand, CallbackInfoReturnable<ActionResult> ci) {
if (!player.getAbilities().allowModifyWorld && (
entity.getType().isIn(FireblanketConstants.ENTITY_INTERACTION_RESTRICTED) ||
player.getStackInHand(hand).isIn(FireblanketConstants.ITEM_INTERACTION_RESTRICTED))) {
ci.setReturnValue(ActionResult.FAIL);
ci.cancel();
}
}

@WrapOperation(
method = "interactBlockInternal",
at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;onUseWithItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;Lnet/minecraft/util/hit/BlockHitResult;)Lnet/minecraft/util/ItemActionResult;")
)
private ItemActionResult fireblanket$filterItemBlockInteractByTag(BlockState blockState, ItemStack stack, World world, PlayerEntity player, Hand hand, BlockHitResult hitResult, Operation<ItemActionResult> op) {
if (!player.getAbilities().allowModifyWorld) {
if (stack.isIn(FireblanketConstants.ITEM_INTERACTION_RESTRICTED)) {
return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
}
if (blockState.isIn(FireblanketConstants.BLOCK_INTERACTION_RESTRICTED)) {
return ItemActionResult.FAIL;
}
}
return op.call(blockState, stack, world, player, hand, hitResult);
}

@WrapOperation(
method = "interactBlockInternal",
at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;onUse(Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/hit/BlockHitResult;)Lnet/minecraft/util/ActionResult;")
)
private ActionResult fireblanket$filterBlockInteractByTag(BlockState blockState, World world, PlayerEntity player, BlockHitResult hitResult, Operation<ActionResult> op) {
if (!player.getAbilities().allowModifyWorld && blockState.isIn(FireblanketConstants.BLOCK_INTERACTION_RESTRICTED)) {
return ActionResult.FAIL;
}
return op.call(blockState, world, player, hitResult);
}

@Inject(
method = "attackEntity",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;syncSelectedSlot()V", shift = At.Shift.AFTER),
cancellable = true
)
private void fireblanket$filterAttackEntityByTag(PlayerEntity player, Entity target, CallbackInfo ci) {
if (!player.getAbilities().allowModifyWorld && target.getType().isIn(FireblanketConstants.ENTITY_ATTACK_RESTRICTED)) {
ci.cancel();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"values": [
"#minecraft:trapdoors",
"#minecraft:shulker_boxes",
"#minecraft:fence_gates",
"minecraft:chest",
"minecraft:trapped_chest",
"minecraft:chiseled_bookshelf",
"minecraft:repeater",
"minecraft:comparator",
"minecraft:daylight_detector",
"minecraft:dispenser",
"minecraft:dropper",
"minecraft:hopper",
"minecraft:flower_pot"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"values": [
"minecraft:item_frame",
"minecraft:painting",
"minecraft:sheep"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"values": [
"minecraft:item_frame",
"minecraft:leash_knot"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"values": [
"minecraft:shears",
"minecraft:egg"
]
}
1 change: 1 addition & 0 deletions src/main/resources/fireblanket.accesswidener
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ accessWidener v2 named
accessible class net/minecraft/client/render/chunk/ChunkBuilder$BuiltChunk$RebuildTask
accessible class net/minecraft/server/world/ServerChunkManager$MainThreadExecutor

accessible class net/minecraft/server/network/ServerPlayNetworkHandler$Interaction
accessible method net/minecraft/server/world/ServerChunkLoadingManager entryIterator ()Ljava/lang/Iterable;

accessible method net/minecraft/world/storage/ChunkCompressionFormat <init> (ILjava/lang/String;Lnet/minecraft/world/storage/ChunkCompressionFormat$Wrapper;Lnet/minecraft/world/storage/ChunkCompressionFormat$Wrapper;)V
Expand Down
6 changes: 4 additions & 2 deletions src/main/resources/fireblanket.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
"accessor.EntityTypeAccessor",
"accessor.ServerChunkManagerAccessor",
"accessor.ServerLoginNetworkHandlerAccessor",
"adventure_fix.MixinEggItem",
"adventure_fix.MixinFlowerPotBlock",
"adventure_fix.MixinItemStack",
"adventure_fix.MixinPlayerInteractEntityC2SPacketHandler",
"adventure_fix.MixinServerPlayerInteractionManager",
"ai.MixinTemptGoal",
"be_sync.MixinBlockEntity",
"be_sync.MixinChunkHolder",
Expand Down Expand Up @@ -57,6 +58,7 @@
"client.MixinClientPlayNetworkHandler",
"client.MixinRenderSystem",
"client.MixinSignBlockEntityRenderer",
"client.adventure_fix.MixinClientPlayerInteractionManager",
"client.be_masking.MixinBlockEntityRenderDispatcher",
"client.be_masking.MixinRebuildTask",
"client.be_masking.sodium.MixinChunkRenderRebuildTask",
Expand Down

0 comments on commit 020f8ac

Please sign in to comment.