diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/bridge/core/network/handshake/ServerHandshakeNetHandlerBridge.java b/arclight-common/src/main/java/io/izzel/arclight/common/bridge/core/network/handshake/ServerHandshakeNetHandlerBridge.java deleted file mode 100644 index daa9e5ded..000000000 --- a/arclight-common/src/main/java/io/izzel/arclight/common/bridge/core/network/handshake/ServerHandshakeNetHandlerBridge.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.izzel.arclight.common.bridge.core.network.handshake; - -import io.izzel.arclight.common.bridge.core.network.common.ServerCommonPacketListenerBridge; -import net.minecraft.network.protocol.handshake.ClientIntentionPacket; - -public interface ServerHandshakeNetHandlerBridge extends ServerCommonPacketListenerBridge { - default boolean bridge$forge$handleSpecialLogin(ClientIntentionPacket packet) { - return true; - } -} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/ServerHandshakePacketListenerImplMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/ServerHandshakePacketListenerImplMixin.java index 043760812..4205fabbb 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/ServerHandshakePacketListenerImplMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/ServerHandshakePacketListenerImplMixin.java @@ -4,7 +4,6 @@ import com.mojang.authlib.properties.Property; import com.mojang.util.UndashedUuid; import io.izzel.arclight.common.bridge.core.network.NetworkManagerBridge; -import io.izzel.arclight.common.bridge.core.network.handshake.ServerHandshakeNetHandlerBridge; import io.izzel.arclight.common.mod.util.VelocitySupport; import net.minecraft.network.Connection; import net.minecraft.network.chat.Component; @@ -26,7 +25,7 @@ import java.util.HashMap; @Mixin(ServerHandshakePacketListenerImpl.class) -public abstract class ServerHandshakePacketListenerImplMixin implements ServerHandshakeNetHandlerBridge { +public abstract class ServerHandshakePacketListenerImplMixin { private static final Gson gson = new Gson(); private static final java.util.regex.Pattern HOST_PATTERN = java.util.regex.Pattern.compile("[0-9a-f\\.:]{0,45}"); @@ -37,12 +36,11 @@ public abstract class ServerHandshakePacketListenerImplMixin implements ServerHa @Shadow @Final private Connection connection; // @formatter:on - @Inject(method = "handleIntention", at = @At("HEAD")) + // Don't inject at head to ensure executed after Forge check + // See the corresponding mixin on Forge side + @Inject(method = "handleIntention", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/handshake/ClientIntentionPacket;intention()Lnet/minecraft/network/protocol/handshake/ClientIntent;")) private void arclight$setHostName(ClientIntentionPacket packet, CallbackInfo ci) { // TODO - if (!bridge$forge$handleSpecialLogin(packet)) { - return; - } ((NetworkManagerBridge) this.connection).bridge$setHostname(packet.hostName() + ":" + packet.port()); } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/animal/SheepMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/animal/SheepMixin.java index eebe46808..c85916ea7 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/animal/SheepMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/animal/SheepMixin.java @@ -19,15 +19,7 @@ @Mixin(net.minecraft.world.entity.animal.Sheep.class) public abstract class SheepMixin extends AnimalMixin { - @Inject(method = "shear", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Sheep;spawnAtLocation(Lnet/minecraft/world/level/ItemLike;I)Lnet/minecraft/world/entity/item/ItemEntity;")) - private void arclight$forceDrop(CallbackInfo ci) { - forceDrops = true; - } - - @Inject(method = "shear", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/entity/animal/Sheep;spawnAtLocation(Lnet/minecraft/world/level/ItemLike;I)Lnet/minecraft/world/entity/item/ItemEntity;")) - private void arclight$forceDropReset(CallbackInfo ci) { - forceDrops = false; - } + //Force drop handler moved to PSI @Inject(method = "ate", cancellable = true, at = @At("HEAD")) private void arclight$regrow(CallbackInfo ci) { diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/animal/WolfMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/animal/WolfMixin.java index 4e59a6f9d..b1f231e11 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/animal/WolfMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/animal/WolfMixin.java @@ -44,13 +44,5 @@ public abstract class WolfMixin extends TameableAnimalMixin { return ret == 0 && this.bridge$common$animalTameEvent(player) ? ret : 1; } - @Inject(method = "mobInteract", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Wolf;spawnAtLocation(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/item/ItemEntity;")) - private void arclight$forceDropPre(Player player, InteractionHand interactionHand, CallbackInfoReturnable cir) { - this.forceDrops = true; - } - - @Inject(method = "mobInteract", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/entity/animal/Wolf;spawnAtLocation(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/item/ItemEntity;")) - private void arclight$forceDropPost(Player player, InteractionHand interactionHand, CallbackInfoReturnable cir) { - this.forceDrops = false; - } + // Force drop handler moved to PSI } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/item/BucketItemMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/item/BucketItemMixin.java index 18d27cdd2..48f9d8dd8 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/item/BucketItemMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/item/BucketItemMixin.java @@ -1,9 +1,11 @@ package io.izzel.arclight.common.mixin.core.world.item; -import com.llamalad7.mixinextras.sugar.Local; import io.izzel.arclight.common.bridge.core.entity.player.ServerPlayerEntityBridge; import io.izzel.arclight.common.bridge.core.world.item.BucketItemBridge; import io.izzel.arclight.common.mod.util.DistValidate; +import io.izzel.arclight.mixin.Decorate; +import io.izzel.arclight.mixin.DecorationOps; +import io.izzel.arclight.mixin.Local; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; @@ -16,6 +18,7 @@ import net.minecraft.world.item.BucketItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.BucketPickup; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; @@ -39,20 +42,23 @@ public abstract class BucketItemMixin implements BucketItemBridge { @Shadow public abstract boolean emptyContents(@Nullable Player player, Level worldIn, BlockPos posIn, @javax.annotation.Nullable BlockHitResult rayTrace); // @formatter:on - @Inject(method = "use", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/BucketPickup;pickupBlock(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/level/LevelAccessor;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/world/item/ItemStack;")) - private void arclight$bucketFill(Level worldIn, Player playerIn, InteractionHand handIn, CallbackInfoReturnable> cir, @Local ItemStack stack, @Local BlockHitResult result) { - if (!DistValidate.isValid(worldIn)) return; - BlockPos pos = result.getBlockPos(); - BlockState state = worldIn.getBlockState(pos); - ItemStack dummyFluid = ((BucketPickup) state.getBlock()).pickupBlock(playerIn, DummyGeneratorAccess.INSTANCE, pos, state); - PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent((ServerLevel) worldIn, playerIn, pos, pos, result.getDirection(), stack, dummyFluid.getItem(), handIn); - if (event.isCancelled()) { - ((ServerPlayer) playerIn).connection.send(new ClientboundBlockUpdatePacket(worldIn, pos)); - ((ServerPlayerEntityBridge) playerIn).bridge$getBukkitEntity().updateInventory(); - cir.setReturnValue(new InteractionResultHolder<>(InteractionResult.FAIL, stack)); - } else { - arclight$setCaptureItem(event.getItemStack()); + // Using @Local doesn't work for Forge since they don't have MixinExtras :( + // Use Decorate to capture + @Decorate(method = "use", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/BucketPickup;pickupBlock(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/level/LevelAccessor;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/world/item/ItemStack;")) + private ItemStack arclight$bucketFill(BucketPickup pickup, Player playerIn, LevelAccessor worldIn, BlockPos pos, BlockState state, + @Local(ordinal = 0) InteractionHand handIn, @Local(ordinal = 0) ItemStack stack, @Local(ordinal = 0) BlockHitResult result) throws Throwable { + if (DistValidate.isValid(worldIn)) { + ItemStack dummyFluid = pickup.pickupBlock(playerIn, DummyGeneratorAccess.INSTANCE, pos, state); + PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent((ServerLevel) worldIn, playerIn, pos, pos, result.getDirection(), stack, dummyFluid.getItem(), handIn); + if (event.isCancelled()) { + ((ServerPlayer) playerIn).connection.send(new ClientboundBlockUpdatePacket(worldIn, pos)); + ((ServerPlayerEntityBridge) playerIn).bridge$getBukkitEntity().updateInventory(); + return (ItemStack) DecorationOps.cancel().invoke(new InteractionResultHolder<>(InteractionResult.FAIL, stack)); + } else { + arclight$setCaptureItem(event.getItemStack()); + } } + return (ItemStack) DecorationOps.callsite().invoke(pickup, playerIn, worldIn, pos, state); } @Inject(method = "use", at = @At("RETURN")) diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin.java index bef527cf0..ae5df3145 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin.java @@ -3,7 +3,6 @@ import io.izzel.arclight.common.bridge.core.entity.player.ServerPlayerEntityBridge; import io.izzel.arclight.common.bridge.core.tileentity.AbstractFurnaceTileEntityBridge; import io.izzel.arclight.common.bridge.core.world.item.crafting.RecipeHolderBridge; -import io.izzel.arclight.common.mod.util.ArclightCaptures; import io.izzel.arclight.mixin.Decorate; import io.izzel.arclight.mixin.DecorationOps; import io.izzel.arclight.mixin.Local; @@ -29,7 +28,6 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.event.inventory.FurnaceBurnEvent; import org.bukkit.event.inventory.FurnaceExtractEvent; -import org.bukkit.event.inventory.FurnaceSmeltEvent; import org.bukkit.event.inventory.FurnaceStartSmeltEvent; import org.bukkit.inventory.CookingRecipe; import org.bukkit.inventory.InventoryHolder; @@ -57,30 +55,12 @@ public abstract class AbstractFurnaceBlockEntityMixin extends LockableBlockEntit public List transaction = new ArrayList<>(); private int maxStack = MAX_STACK; - @SuppressWarnings("unchecked") - @Decorate(method = "burn", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/core/NonNullList;get(I)Ljava/lang/Object;")) - private static E arclight$furnaceSmelt(NonNullList instance, int i, @Local(ordinal = -1) ItemStack itemStack2, @Local(ordinal = -2) ItemStack itemStack1) throws Throwable { - var blockEntity = ArclightCaptures.getTickingBlockEntity(); - if (blockEntity != null) { - CraftItemStack source = CraftItemStack.asCraftMirror(itemStack1); - org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemStack2); - - FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(CraftBlock.at(blockEntity.getLevel(), blockEntity.getBlockPos()), source, result); - Bukkit.getPluginManager().callEvent(furnaceSmeltEvent); - - if (furnaceSmeltEvent.isCancelled()) { - return (E) DecorationOps.cancel().invoke(false); - } - - result = furnaceSmeltEvent.getResult(); - itemStack2 = CraftItemStack.asNMSCopy(result); - if (itemStack2.isEmpty()) { - itemStack1.shrink(1); - return (E) DecorationOps.cancel().invoke(true); - } - } - return (E) DecorationOps.callsite().invoke(instance, i); - } + // Static decorator used to be allowed to inline into non-static context, + // but the result will be incorrect. + // This will cause the injected method to become invalid. + // See PSI + // @Decorate(method = "burn", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/core/NonNullList;get(I)Ljava/lang/Object;")) + // private static E arclight$furnaceSmelt(NonNullList instance, int i, @Local(ordinal = 0) AbstractFurnaceBlockEntity blockEntity, @Local(ordinal = -1) ItemStack itemStack2, @Local(ordinal = -2) ItemStack itemStack1) throws Throwable @Decorate(method = "serverTick", at = @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;isLit()Z"), slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity;litDuration:I"))) diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/entity/HopperBlockEntityMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/entity/HopperBlockEntityMixin.java index 4ff53ed7a..26cbabb7d 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/entity/HopperBlockEntityMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/entity/HopperBlockEntityMixin.java @@ -38,7 +38,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import java.util.ArrayList; import java.util.List; @@ -65,7 +64,9 @@ public abstract class HopperBlockEntityMixin extends LockableBlockEntityMixin { return result; } - @Eject(method = "ejectItems", require = 0, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;")) + // Somehow this works for Forge again. + // Removing requires = 0 + @Eject(method = "ejectItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;")) private static ItemStack arclight$moveItem(Container source, Container destination, ItemStack stack, Direction direction, CallbackInfoReturnable cir) { var entity = ((HopperBlockEntity) ArclightCaptures.getTickingBlockEntity()); if (entity == null) { diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/vanilla/world/entity/animal/SheepMixin_Vanilla.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/vanilla/world/entity/animal/SheepMixin_Vanilla.java new file mode 100644 index 000000000..ac3d6f6a4 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/vanilla/world/entity/animal/SheepMixin_Vanilla.java @@ -0,0 +1,16 @@ +package io.izzel.arclight.common.mixin.vanilla.world.entity.animal; + +import io.izzel.arclight.common.mixin.core.world.entity.animal.AnimalMixin; +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; + +@Mixin(net.minecraft.world.entity.animal.Sheep.class) +public abstract class SheepMixin_Vanilla extends AnimalMixin { + @Inject(method = "shear", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Sheep;spawnAtLocation(Lnet/minecraft/world/level/ItemLike;I)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDrop(CallbackInfo ci) { forceDrops = true; } + + @Inject(method = "shear", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/entity/animal/Sheep;spawnAtLocation(Lnet/minecraft/world/level/ItemLike;I)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDropReset(CallbackInfo ci) { forceDrops = false; } +} \ No newline at end of file diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/vanilla/world/entity/animal/WolfMixin_Vanilla.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/vanilla/world/entity/animal/WolfMixin_Vanilla.java new file mode 100644 index 000000000..3fe0ef9da --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/vanilla/world/entity/animal/WolfMixin_Vanilla.java @@ -0,0 +1,24 @@ +package io.izzel.arclight.common.mixin.vanilla.world.entity.animal; + +import io.izzel.arclight.common.mixin.core.world.entity.animal.TameableAnimalMixin; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.animal.Wolf; +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(Wolf.class) +public abstract class WolfMixin_Vanilla extends TameableAnimalMixin { + @Inject(method = "mobInteract", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Wolf;spawnAtLocation(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDropPre(Player player, InteractionHand interactionHand, CallbackInfoReturnable cir) { + this.forceDrops = true; + } + + @Inject(method = "mobInteract", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/entity/animal/Wolf;spawnAtLocation(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDropPost(Player player, InteractionHand interactionHand, CallbackInfoReturnable cir) { + this.forceDrops = false; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/vanilla/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Vanilla.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/vanilla/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Vanilla.java index 7be8d7ccf..f76a96951 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/vanilla/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Vanilla.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/vanilla/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Vanilla.java @@ -1,9 +1,39 @@ package io.izzel.arclight.common.mixin.vanilla.world.level.block.entity; +import io.izzel.arclight.mixin.Decorate; +import io.izzel.arclight.mixin.DecorationOps; +import io.izzel.arclight.mixin.Local; +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v.block.CraftBlock; +import org.bukkit.craftbukkit.v.inventory.CraftItemStack; +import org.bukkit.event.inventory.FurnaceSmeltEvent; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; @Mixin(AbstractFurnaceBlockEntity.class) public abstract class AbstractFurnaceBlockEntityMixin_Vanilla { + @Decorate(method = "burn", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/core/NonNullList;get(I)Ljava/lang/Object;")) + private static E arclight$furnaceSmelt(NonNullList instance, int i, @Local(ordinal = 0) AbstractFurnaceBlockEntity blockEntity, @Local(ordinal = -1) ItemStack itemStack2, @Local(ordinal = -2) ItemStack itemStack1) throws Throwable { + CraftItemStack source = CraftItemStack.asCraftMirror(itemStack1); + org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemStack2); + + FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(CraftBlock.at(blockEntity.getLevel(), blockEntity.getBlockPos()), source, result); + Bukkit.getPluginManager().callEvent(furnaceSmeltEvent); + + if (furnaceSmeltEvent.isCancelled()) { + return (E) DecorationOps.cancel().invoke(false); + } + + result = furnaceSmeltEvent.getResult(); + itemStack2 = CraftItemStack.asNMSCopy(result); + if (itemStack2.isEmpty()) { + itemStack1.shrink(1); + return (E) DecorationOps.cancel().invoke(true); + } + return (E) DecorationOps.callsite().invoke(instance, i); + } } diff --git a/arclight-common/src/main/resources/arclight.accesswidener b/arclight-common/src/main/resources/arclight.accesswidener index 2890829d4..7632cd61f 100644 --- a/arclight-common/src/main/resources/arclight.accesswidener +++ b/arclight-common/src/main/resources/arclight.accesswidener @@ -737,7 +737,7 @@ accessible field net/minecraft/server/dedicated/DedicatedServer serverLinks Lnet mutable field net/minecraft/server/dedicated/DedicatedServer serverLinks Lnet/minecraft/server/ServerLinks; accessible method net/minecraft/world/entity/projectile/Arrow getPotionContents ()Lnet/minecraft/world/item/alchemy/PotionContents; accessible method net/minecraft/world/entity/projectile/Arrow setPotionContents (Lnet/minecraft/world/item/alchemy/PotionContents;)V -accessible field net/minecraft/world/item/BucketItem content Lnet/minecraft/world/level/material/Fluid; +# accessible field net/minecraft/world/item/BucketItem content Lnet/minecraft/world/level/material/Fluid; accessible class net/minecraft/server/level/ServerPlayer$RespawnPosAngle accessible method net/minecraft/world/entity/projectile/windcharge/AbstractWindCharge explode (Lnet/minecraft/world/phys/Vec3;)V accessible field net/minecraft/world/entity/animal/MushroomCow stewEffects Lnet/minecraft/world/item/component/SuspiciousStewEffects; diff --git a/arclight-common/src/main/resources/mixins.arclight.core.json b/arclight-common/src/main/resources/mixins.arclight.core.json index c6f3eeecf..ddec66fc5 100644 --- a/arclight-common/src/main/resources/mixins.arclight.core.json +++ b/arclight-common/src/main/resources/mixins.arclight.core.json @@ -482,6 +482,7 @@ "world.level.block.WitherRoseBlockMixin", "world.level.block.WitherSkullBlockMixin", "world.level.block.entity.AbstractFurnaceBlockEntityMixin", + "world.level.block.entity.BannerBlockEntityMixin", "world.level.block.entity.BarrelBlockEntityMixin", "world.level.block.entity.BeaconTileEntityMixin", "world.level.block.entity.BeehiveBlockEntityMixin", @@ -491,7 +492,6 @@ "world.level.block.entity.BrushableBlockEntityMixin", "world.level.block.entity.CampfireBlockEntityMixin", "world.level.block.entity.ChestBlockEntityMixin", - "world.level.block.entity.BannerBlockEntityMixin", "world.level.block.entity.ChiseledBookShelfBlockEntityMixin", "world.level.block.entity.CommandBlockLogicMixin", "world.level.block.entity.CommandBlockTileEntity1Mixin", diff --git a/arclight-common/src/main/resources/mixins.arclight.vanilla.json b/arclight-common/src/main/resources/mixins.arclight.vanilla.json index 6d1b49641..c0d133789 100644 --- a/arclight-common/src/main/resources/mixins.arclight.vanilla.json +++ b/arclight-common/src/main/resources/mixins.arclight.vanilla.json @@ -24,7 +24,9 @@ "world.entity.EntityMixin_Vanilla", "world.entity.LivingEntityMixin_Vanilla", "world.entity.animal.MushroomCowMixin_Vanilla", + "world.entity.animal.SheepMixin_Vanilla", "world.entity.animal.SnowGolemMixin_Vanilla", + "world.entity.animal.WolfMixin_Vanilla", "world.entity.animal.frog.TadpoleMixin_Vanilla", "world.entity.monster.BoggedMixin_Vanilla", "world.entity.monster.ZombieMixin_Vanilla", diff --git a/arclight-fabric/src/main/java/io/izzel/arclight/fabric/mixin/core/world/entity/animal/SheepMixin_Fabric.java b/arclight-fabric/src/main/java/io/izzel/arclight/fabric/mixin/core/world/entity/animal/SheepMixin_Fabric.java new file mode 100644 index 000000000..771f0b713 --- /dev/null +++ b/arclight-fabric/src/main/java/io/izzel/arclight/fabric/mixin/core/world/entity/animal/SheepMixin_Fabric.java @@ -0,0 +1,16 @@ +package io.izzel.arclight.fabric.mixin.core.world.entity.animal; + +import io.izzel.arclight.common.mixin.core.world.entity.animal.AnimalMixin; +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; + +@Mixin(net.minecraft.world.entity.animal.Sheep.class) +public abstract class SheepMixin_Fabric extends AnimalMixin { + @Inject(method = "shear", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Sheep;spawnAtLocation(Lnet/minecraft/world/level/ItemLike;I)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDrop(CallbackInfo ci) { forceDrops = true; } + + @Inject(method = "shear", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/entity/animal/Sheep;spawnAtLocation(Lnet/minecraft/world/level/ItemLike;I)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDropReset(CallbackInfo ci) { forceDrops = false; } +} diff --git a/arclight-fabric/src/main/java/io/izzel/arclight/fabric/mixin/core/world/entity/animal/WolfMixin_Fabric.java b/arclight-fabric/src/main/java/io/izzel/arclight/fabric/mixin/core/world/entity/animal/WolfMixin_Fabric.java new file mode 100644 index 000000000..712a7911b --- /dev/null +++ b/arclight-fabric/src/main/java/io/izzel/arclight/fabric/mixin/core/world/entity/animal/WolfMixin_Fabric.java @@ -0,0 +1,24 @@ +package io.izzel.arclight.fabric.mixin.core.world.entity.animal; + +import io.izzel.arclight.common.mixin.core.world.entity.animal.TameableAnimalMixin; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.animal.Wolf; +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(Wolf.class) +public abstract class WolfMixin_Fabric extends TameableAnimalMixin { + @Inject(method = "mobInteract", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Wolf;spawnAtLocation(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDropPre(Player player, InteractionHand interactionHand, CallbackInfoReturnable cir) { + this.forceDrops = true; + } + + @Inject(method = "mobInteract", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/entity/animal/Wolf;spawnAtLocation(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDropPost(Player player, InteractionHand interactionHand, CallbackInfoReturnable cir) { + this.forceDrops = false; + } +} diff --git a/arclight-fabric/src/main/java/io/izzel/arclight/fabric/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Fabric.java b/arclight-fabric/src/main/java/io/izzel/arclight/fabric/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Fabric.java new file mode 100644 index 000000000..f56b30b52 --- /dev/null +++ b/arclight-fabric/src/main/java/io/izzel/arclight/fabric/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Fabric.java @@ -0,0 +1,39 @@ +package io.izzel.arclight.fabric.mixin.core.world.level.block.entity; + +import io.izzel.arclight.mixin.Decorate; +import io.izzel.arclight.mixin.DecorationOps; +import io.izzel.arclight.mixin.Local; +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v.block.CraftBlock; +import org.bukkit.craftbukkit.v.inventory.CraftItemStack; +import org.bukkit.event.inventory.FurnaceSmeltEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(AbstractFurnaceBlockEntity.class) +public abstract class AbstractFurnaceBlockEntityMixin_Fabric { + + @Decorate(method = "burn", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/core/NonNullList;get(I)Ljava/lang/Object;")) + private static E arclight$furnaceSmelt(NonNullList instance, int i, @Local(ordinal = 0) AbstractFurnaceBlockEntity blockEntity, @Local(ordinal = -1) ItemStack itemStack2, @Local(ordinal = -2) ItemStack itemStack1) throws Throwable { + CraftItemStack source = CraftItemStack.asCraftMirror(itemStack1); + org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemStack2); + + FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(CraftBlock.at(blockEntity.getLevel(), blockEntity.getBlockPos()), source, result); + Bukkit.getPluginManager().callEvent(furnaceSmeltEvent); + + if (furnaceSmeltEvent.isCancelled()) { + return (E) DecorationOps.cancel().invoke(false); + } + + result = furnaceSmeltEvent.getResult(); + itemStack2 = CraftItemStack.asNMSCopy(result); + if (itemStack2.isEmpty()) { + itemStack1.shrink(1); + return (E) DecorationOps.cancel().invoke(true); + } + return (E) DecorationOps.callsite().invoke(instance, i); + } +} diff --git a/arclight-fabric/src/main/resources/mixins.arclight.fabric.json b/arclight-fabric/src/main/resources/mixins.arclight.fabric.json index d3f028827..2bf33f537 100644 --- a/arclight-fabric/src/main/resources/mixins.arclight.fabric.json +++ b/arclight-fabric/src/main/resources/mixins.arclight.fabric.json @@ -22,10 +22,13 @@ "core.network.ServerLoginNetHandlerMixin_Fabric", "core.world.entity.EntityMixin_Fabric", "core.world.entity.LivingEntityMixin_Fabric", + "core.world.entity.animal.SheepMixin_Fabric", + "core.world.entity.animal.WolfMixin_Fabric", "core.world.entity.monster.ZombieMixin_Fabric", "core.world.entity.player.PlayerMixin_Fabric", "core.world.entity.player.ServerPlayerMixin_Fabric", "core.world.level.block.FireBlockMixin_Fabric", + "core.world.level.block.entity.AbstractFurnaceBlockEntityMixin_Fabric", "core.world.level.block.entity.BrewingStandBlockEntityMixin_Fabric" ] } \ No newline at end of file diff --git a/arclight-forge/src/main/java/io/izzel/arclight/forge/ArclightMod.java b/arclight-forge/src/main/java/io/izzel/arclight/forge/ArclightMod.java index fcbb3befa..f3035bebe 100644 --- a/arclight-forge/src/main/java/io/izzel/arclight/forge/ArclightMod.java +++ b/arclight-forge/src/main/java/io/izzel/arclight/forge/ArclightMod.java @@ -1,10 +1,8 @@ package io.izzel.arclight.forge; import io.izzel.arclight.api.Arclight; -import io.izzel.arclight.common.mod.ArclightCommon; import io.izzel.arclight.common.mod.server.ArclightServer; import io.izzel.arclight.forge.mod.ForgeArclightServer; -import io.izzel.arclight.forge.mod.ForgeCommonImpl; import io.izzel.arclight.forge.mod.event.ArclightEventDispatcherRegistry; import net.minecraftforge.fml.common.Mod; import org.apache.logging.log4j.Level; diff --git a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/network/ServerHandshakeNetHandlerMixin_Forge.java b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/network/ServerHandshakePacketListenerImplMixin_Forge.java similarity index 68% rename from arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/network/ServerHandshakeNetHandlerMixin_Forge.java rename to arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/network/ServerHandshakePacketListenerImplMixin_Forge.java index 2a5880365..f346e46e1 100644 --- a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/network/ServerHandshakeNetHandlerMixin_Forge.java +++ b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/network/ServerHandshakePacketListenerImplMixin_Forge.java @@ -2,31 +2,27 @@ import com.google.gson.Gson; import com.mojang.authlib.properties.Property; -import io.izzel.arclight.common.bridge.core.network.handshake.ServerHandshakeNetHandlerBridge; import io.izzel.arclight.common.mod.util.VelocitySupport; import net.minecraft.network.Connection; import net.minecraft.network.protocol.handshake.ClientIntentionPacket; import net.minecraft.server.network.ServerHandshakePacketListenerImpl; import net.minecraftforge.server.ServerLifecycleHooks; import org.spigotmc.SpigotConfig; -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.Redirect; import java.util.Objects; @Mixin(ServerHandshakePacketListenerImpl.class) -public abstract class ServerHandshakeNetHandlerMixin_Forge implements ServerHandshakeNetHandlerBridge { - - // @formatter:off - @Shadow @Final private Connection connection; - // @formatter:on +public abstract class ServerHandshakePacketListenerImplMixin_Forge { private static final String EXTRA_DATA = "extraData"; private static final Gson GSON = new Gson(); - @Override - public boolean bridge$forge$handleSpecialLogin(ClientIntentionPacket packet) { + // Since forge is doing handleServerLogin we redirect it to add logic + @Redirect(method = "handleIntention", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/server/ServerLifecycleHooks;handleServerLogin(Lnet/minecraft/network/protocol/handshake/ClientIntentionPacket;Lnet/minecraft/network/Connection;)Z")) + public boolean arclight$handleSpecialLogin(ClientIntentionPacket packet, Connection connection) { String ip = packet.hostName(); if (!VelocitySupport.isEnabled() && SpigotConfig.bungee) { String[] split = ip.split("\0"); @@ -38,11 +34,11 @@ public abstract class ServerHandshakeNetHandlerMixin_Forge implements ServerHand // replace the hostname field with embedded data //noinspection deprecation var forgePacket = new ClientIntentionPacket(packet.protocolVersion(), extraData, packet.port(), packet.intention()); - return ServerLifecycleHooks.handleServerLogin(forgePacket, this.connection); + return ServerLifecycleHooks.handleServerLogin(forgePacket, connection); } } } } - return ServerLifecycleHooks.handleServerLogin(packet, this.connection); + return ServerLifecycleHooks.handleServerLogin(packet, connection); } } diff --git a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/entity/animal/SheepMixin_Forge.java b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/entity/animal/SheepMixin_Forge.java new file mode 100644 index 000000000..4ce2ff311 --- /dev/null +++ b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/entity/animal/SheepMixin_Forge.java @@ -0,0 +1,17 @@ +package io.izzel.arclight.forge.mixin.core.world.entity.animal; + +import io.izzel.arclight.common.mixin.core.world.entity.animal.AnimalMixin; +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; + +@Mixin(net.minecraft.world.entity.animal.Sheep.class) +public abstract class SheepMixin_Forge extends AnimalMixin { + // Implementation is altered by minecraftforge, which delegates spawning to dropShearedItems + @Inject(method = "dropShearedItems", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Sheep;spawnAtLocation(Lnet/minecraft/world/item/ItemStack;F)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDrop(CallbackInfo ci) { forceDrops = true; } + + @Inject(method = "dropShearedItems", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/entity/animal/Sheep;spawnAtLocation(Lnet/minecraft/world/item/ItemStack;F)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDropReset(CallbackInfo ci) { forceDrops = false; } +} \ No newline at end of file diff --git a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/entity/animal/WolfMixin_Forge.java b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/entity/animal/WolfMixin_Forge.java new file mode 100644 index 000000000..554789d5f --- /dev/null +++ b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/entity/animal/WolfMixin_Forge.java @@ -0,0 +1,10 @@ +package io.izzel.arclight.forge.mixin.core.world.entity.animal; + +import io.izzel.arclight.common.mixin.core.world.entity.animal.TameableAnimalMixin; +import net.minecraft.world.entity.animal.Wolf; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(Wolf.class) +public abstract class WolfMixin_Forge extends TameableAnimalMixin { + // Dummy: logic moved to ShearsItemMixin +} diff --git a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/item/BucketItemMixin_Forge.java b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/item/BucketItemMixin_Forge.java index 17748bfa7..17ab0ddbf 100644 --- a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/item/BucketItemMixin_Forge.java +++ b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/item/BucketItemMixin_Forge.java @@ -4,7 +4,6 @@ import io.izzel.arclight.common.bridge.core.world.item.BucketItemBridge; import io.izzel.arclight.common.mod.util.DistValidate; import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -17,9 +16,7 @@ import net.minecraft.world.phys.BlockHitResult; import org.bukkit.craftbukkit.v.event.CraftEventFactory; import org.bukkit.event.player.PlayerBucketEmptyEvent; -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.CallbackInfoReturnable; diff --git a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/item/MilkBucketItemMixin_Forge.java b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/item/MilkBucketItemMixin_Forge.java index cca867ef6..699bcd990 100644 --- a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/item/MilkBucketItemMixin_Forge.java +++ b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/item/MilkBucketItemMixin_Forge.java @@ -14,7 +14,7 @@ @Mixin(MilkBucketItem.class) public class MilkBucketItemMixin_Forge { - @Inject(method = "finishUsingItem", at = @At(value = "INVOKE", remap = false, target = "Lnet/minecraft/world/entity/LivingEntity;curePotionEffects(Lnet/minecraft/world/item/ItemStack;)Z")) + @Inject(method = "finishUsingItem", at = @At(value = "INVOKE", remap = false, target = "Lnet/minecraft/world/entity/LivingEntity;removeAllEffects()Z")) private void arclight$cureReason(ItemStack stack, Level worldIn, LivingEntity entityLiving, CallbackInfoReturnable cir) { ((LivingEntityBridge) entityLiving).bridge$pushEffectCause(EntityPotionEffectEvent.Cause.MILK); } diff --git a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java index 2c8e46e9b..7e277a944 100644 --- a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java +++ b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_Forge.java @@ -1,9 +1,42 @@ package io.izzel.arclight.forge.mixin.core.world.level.block.entity; +import io.izzel.arclight.mixin.Decorate; +import io.izzel.arclight.mixin.DecorationOps; +import io.izzel.arclight.mixin.Local; +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v.block.CraftBlock; +import org.bukkit.craftbukkit.v.inventory.CraftItemStack; +import org.bukkit.event.inventory.FurnaceSmeltEvent; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; @Mixin(AbstractFurnaceBlockEntity.class) public abstract class AbstractFurnaceBlockEntityMixin_Forge { + @Decorate(method = "burn", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/core/NonNullList;get(I)Ljava/lang/Object;")) + private E arclight$furnaceSmelt(NonNullList instance, int i, @Local(ordinal = 1) ItemStack itemStack2, @Local(ordinal = 0) ItemStack itemStack1) throws Throwable { + var blockEntity = (AbstractFurnaceBlockEntity) (Object) this; + if (blockEntity != null) { + CraftItemStack source = CraftItemStack.asCraftMirror(itemStack1); + org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemStack2); + + FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(CraftBlock.at(blockEntity.getLevel(), blockEntity.getBlockPos()), source, result); + Bukkit.getPluginManager().callEvent(furnaceSmeltEvent); + + if (furnaceSmeltEvent.isCancelled()) { + return (E) DecorationOps.cancel().invoke(false); + } + + result = furnaceSmeltEvent.getResult(); + itemStack2 = CraftItemStack.asNMSCopy(result); + if (itemStack2.isEmpty()) { + itemStack1.shrink(1); + return (E) DecorationOps.cancel().invoke(true); + } + } + return (E) DecorationOps.callsite().invoke(instance, i); + } } diff --git a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/level/block/entity/HopperBlockEntityMixin_Forge.java b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/level/block/entity/HopperBlockEntityMixin_Forge.java index 8150c7bed..2ebca4d81 100644 --- a/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/level/block/entity/HopperBlockEntityMixin_Forge.java +++ b/arclight-forge/src/main/java/io/izzel/arclight/forge/mixin/core/world/level/block/entity/HopperBlockEntityMixin_Forge.java @@ -1,50 +1,14 @@ package io.izzel.arclight.forge.mixin.core.world.level.block.entity; -import io.izzel.arclight.common.bridge.core.inventory.IInventoryBridge; -import io.izzel.arclight.common.bridge.core.tileentity.TileEntityBridge; -import io.izzel.arclight.common.bridge.core.world.WorldBridge; -import io.izzel.arclight.common.mod.util.ArclightCaptures; -import io.izzel.arclight.mixin.Eject; -import net.minecraft.core.Direction; -import net.minecraft.world.CompoundContainer; -import net.minecraft.world.Container; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.HopperBlockEntity; -import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.v.inventory.CraftInventoryDoubleChest; -import org.bukkit.craftbukkit.v.inventory.CraftItemStack; -import org.bukkit.event.inventory.InventoryMoveItemEvent; -import org.bukkit.inventory.Inventory; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(HopperBlockEntity.class) public class HopperBlockEntityMixin_Forge { - @Eject(method = "ejectItems", remap = false, at = @At(value = "INVOKE", remap = true, target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;")) - private static ItemStack arclight$moveItem(Container source, Container destination, ItemStack stack, Direction direction, CallbackInfoReturnable cir) { - var entity = ((HopperBlockEntity) ArclightCaptures.getTickingBlockEntity()); - if (entity == null) { - return HopperBlockEntity.addItem(source, destination, stack, direction); - } - CraftItemStack original = CraftItemStack.asCraftMirror(stack); - - Inventory destinationInventory; - // Have to special case large chests as they work oddly - if (destination instanceof CompoundContainer) { - destinationInventory = new CraftInventoryDoubleChest(((CompoundContainer) destination)); - } else { - destinationInventory = ((IInventoryBridge) destination).getOwnerInventory(); - } - - InventoryMoveItemEvent event = new InventoryMoveItemEvent(((TileEntityBridge) entity).bridge$getOwner().getInventory(), original.clone(), destinationInventory, true); - Bukkit.getPluginManager().callEvent(event); - if (event.isCancelled()) { - entity.setCooldown(((WorldBridge) entity.getLevel()).bridge$spigotConfig().hopperTransfer); // Delay hopper checks - cir.setReturnValue(false); - return null; - } - return HopperBlockEntity.addItem(source, destination, CraftItemStack.asNMSCopy(event.getItem()), direction); - } + // Somehow common mixin can inject into forge one + // Eject can only be applied once, don't apply here + // @Eject(method = "ejectItems", remap = false, at = @At(value = "INVOKE", remap = true, target = "Lnet/minecraft/world/level/block/entity/HopperBlockEntity;addItem(Lnet/minecraft/world/Container;Lnet/minecraft/world/Container;Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/core/Direction;)Lnet/minecraft/world/item/ItemStack;")) + // private static ItemStack arclight$moveItem(Container source, Container destination, ItemStack stack, Direction direction, CallbackInfoReturnable cir) + // Content is identical to the corresponding common mixin } diff --git a/arclight-forge/src/main/resources/mixins.arclight.forge.json b/arclight-forge/src/main/resources/mixins.arclight.forge.json index 711f3fca9..476fb89c0 100644 --- a/arclight-forge/src/main/resources/mixins.arclight.forge.json +++ b/arclight-forge/src/main/resources/mixins.arclight.forge.json @@ -22,7 +22,7 @@ "core.command.CommandsMixin_Forge", "core.fluid.LavaFluidMixin_Forge", "core.network.ServerCommonPacketListenerImplMixin_Forge", - "core.network.ServerHandshakeNetHandlerMixin_Forge", + "core.network.ServerHandshakePacketListenerImplMixin_Forge", "core.network.ServerLoginNetHandlerMixin_Forge", "core.network.ServerPlayNetHandlerMixin_Forge", "core.network.ServerStatusNetHandlerMixin_Forge", @@ -38,6 +38,8 @@ "core.world.entity.MobMixin_Forge", "core.world.entity.ai.behavior.HarvestFarmlandMixin_Forge", "core.world.entity.animal.MushroomCowMixin_Forge", + "core.world.entity.animal.SheepMixin_Forge", + "core.world.entity.animal.WolfMixin_Forge", "core.world.entity.animal.frog.TadpoleMixin_Forge", "core.world.entity.item.ItemEntityMixin_Forge", "core.world.entity.monster.ZombieMixin_Forge", diff --git a/arclight-neoforge/src/main/java/io/izzel/arclight/neoforge/mixin/core/world/entity/animal/SheepMixin_NeoForge.java b/arclight-neoforge/src/main/java/io/izzel/arclight/neoforge/mixin/core/world/entity/animal/SheepMixin_NeoForge.java new file mode 100644 index 000000000..ea165ba0b --- /dev/null +++ b/arclight-neoforge/src/main/java/io/izzel/arclight/neoforge/mixin/core/world/entity/animal/SheepMixin_NeoForge.java @@ -0,0 +1,16 @@ +package io.izzel.arclight.neoforge.mixin.core.world.entity.animal; + +import io.izzel.arclight.common.mixin.core.world.entity.animal.AnimalMixin; +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; + +@Mixin(net.minecraft.world.entity.animal.Sheep.class) +public abstract class SheepMixin_NeoForge extends AnimalMixin { + @Inject(method = "shear", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Sheep;spawnAtLocation(Lnet/minecraft/world/level/ItemLike;I)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDrop(CallbackInfo ci) { forceDrops = true; } + + @Inject(method = "shear", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/entity/animal/Sheep;spawnAtLocation(Lnet/minecraft/world/level/ItemLike;I)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDropReset(CallbackInfo ci) { forceDrops = false; } +} \ No newline at end of file diff --git a/arclight-neoforge/src/main/java/io/izzel/arclight/neoforge/mixin/core/world/entity/animal/WolfMixin_NeoForge.java b/arclight-neoforge/src/main/java/io/izzel/arclight/neoforge/mixin/core/world/entity/animal/WolfMixin_NeoForge.java new file mode 100644 index 000000000..c0c020b8e --- /dev/null +++ b/arclight-neoforge/src/main/java/io/izzel/arclight/neoforge/mixin/core/world/entity/animal/WolfMixin_NeoForge.java @@ -0,0 +1,24 @@ +package io.izzel.arclight.neoforge.mixin.core.world.entity.animal; + +import io.izzel.arclight.common.mixin.core.world.entity.animal.TameableAnimalMixin; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.animal.Wolf; +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(Wolf.class) +public abstract class WolfMixin_NeoForge extends TameableAnimalMixin { + @Inject(method = "mobInteract", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/animal/Wolf;spawnAtLocation(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDropPre(Player player, InteractionHand interactionHand, CallbackInfoReturnable cir) { + this.forceDrops = true; + } + + @Inject(method = "mobInteract", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/entity/animal/Wolf;spawnAtLocation(Lnet/minecraft/world/item/ItemStack;)Lnet/minecraft/world/entity/item/ItemEntity;")) + private void arclight$forceDropPost(Player player, InteractionHand interactionHand, CallbackInfoReturnable cir) { + this.forceDrops = false; + } +} diff --git a/arclight-neoforge/src/main/java/io/izzel/arclight/neoforge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_NeoForge.java b/arclight-neoforge/src/main/java/io/izzel/arclight/neoforge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_NeoForge.java index 229df6390..40986e2f3 100644 --- a/arclight-neoforge/src/main/java/io/izzel/arclight/neoforge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_NeoForge.java +++ b/arclight-neoforge/src/main/java/io/izzel/arclight/neoforge/mixin/core/world/level/block/entity/AbstractFurnaceBlockEntityMixin_NeoForge.java @@ -1,9 +1,39 @@ package io.izzel.arclight.neoforge.mixin.core.world.level.block.entity; +import io.izzel.arclight.mixin.Decorate; +import io.izzel.arclight.mixin.DecorationOps; +import io.izzel.arclight.mixin.Local; +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v.block.CraftBlock; +import org.bukkit.craftbukkit.v.inventory.CraftItemStack; +import org.bukkit.event.inventory.FurnaceSmeltEvent; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; @Mixin(AbstractFurnaceBlockEntity.class) public abstract class AbstractFurnaceBlockEntityMixin_NeoForge { + @Decorate(method = "burn", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/core/NonNullList;get(I)Ljava/lang/Object;")) + private static E arclight$furnaceSmelt(NonNullList instance, int i, @Local(ordinal = 0) AbstractFurnaceBlockEntity blockEntity, @Local(ordinal = -1) ItemStack itemStack2, @Local(ordinal = -2) ItemStack itemStack1) throws Throwable { + CraftItemStack source = CraftItemStack.asCraftMirror(itemStack1); + org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemStack2); + + FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(CraftBlock.at(blockEntity.getLevel(), blockEntity.getBlockPos()), source, result); + Bukkit.getPluginManager().callEvent(furnaceSmeltEvent); + + if (furnaceSmeltEvent.isCancelled()) { + return (E) DecorationOps.cancel().invoke(false); + } + + result = furnaceSmeltEvent.getResult(); + itemStack2 = CraftItemStack.asNMSCopy(result); + if (itemStack2.isEmpty()) { + itemStack1.shrink(1); + return (E) DecorationOps.cancel().invoke(true); + } + return (E) DecorationOps.callsite().invoke(instance, i); + } } diff --git a/arclight-neoforge/src/main/resources/mixins.arclight.neoforge.json b/arclight-neoforge/src/main/resources/mixins.arclight.neoforge.json index 97366acc6..26693efa5 100644 --- a/arclight-neoforge/src/main/resources/mixins.arclight.neoforge.json +++ b/arclight-neoforge/src/main/resources/mixins.arclight.neoforge.json @@ -39,6 +39,8 @@ "core.world.entity.MobMixin_NeoForge", "core.world.entity.ai.behavior.HarvestFarmlandMixin_NeoForge", "core.world.entity.animal.MushroomCowMixin_NeoForge", + "core.world.entity.animal.SheepMixin_NeoForge", + "core.world.entity.animal.WolfMixin_NeoForge", "core.world.entity.animal.frog.TadpoleMixin_NeoForge", "core.world.entity.item.ItemEntityMixin_NeoForge", "core.world.entity.monster.ZombieMixin_NeoForge", diff --git a/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ArclightJarInJarAdaptor.java b/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ArclightJarInJarAdaptor.java index 8f8cbe1ba..c28122129 100644 --- a/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ArclightJarInJarAdaptor.java +++ b/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ArclightJarInJarAdaptor.java @@ -56,7 +56,8 @@ public boolean isValid(IModFile modFile) { @SuppressWarnings("unchecked") static void inject() { try { - var field = FMLLoader.class.getDeclaredField("modDiscoverer"); + var cl = ModDiscoverer.class.getClassLoader(); + var field = ModDiscoverer.class.getDeclaredField("arclight$INSTANCE"); field.setAccessible(true); var discoverer = (ModDiscoverer) field.get(null); var locatorField = ModDiscoverer.class.getDeclaredField("dependencyLocatorList"); diff --git a/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ArclightLaunchHandler.java b/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ArclightLaunchHandler.java index e5b747c0d..61246f65d 100644 --- a/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ArclightLaunchHandler.java +++ b/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ArclightLaunchHandler.java @@ -14,7 +14,8 @@ public ArclightLaunchHandler() { @Override public String getNaming() { - return "srg"; + // Target mapping in use is SRG -> MCP, translates to MCP + return "mcp"; } @Override diff --git a/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ModBootstrap.java b/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ModBootstrap.java index 8104dd191..1a6eb994b 100644 --- a/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ModBootstrap.java +++ b/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ModBootstrap.java @@ -15,6 +15,10 @@ import net.minecraftforge.securemodules.SecureModuleFinder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.MarkerManager; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; import java.io.File; import java.io.InputStream; @@ -28,10 +32,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.security.CodeSigner; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.jar.Manifest; public class ModBootstrap implements AbstractBootstrap { @@ -42,7 +43,9 @@ public record ModBoot(Configuration configuration, ClassLoader parent) {} static void run() { var plugin = Launcher.INSTANCE.environment().findLaunchPlugin("arclight_implementer"); - if (plugin.isPresent()) return; + if (plugin.isPresent()) { + return; + } var logger = LogManager.getLogger("Arclight"); var marker = MarkerManager.getMarker("INSTALL"); try { @@ -82,6 +85,56 @@ private void inject() throws Throwable { injectLaunchPlugin(); } + // No need to check again since it's already checked in run() + @Override + public void dirtyHacks() throws Exception { + AbstractBootstrap.super.dirtyHacks(); + try (var in = getClass().getClassLoader().getResourceAsStream("net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.class")) { + var clazz = new ClassNode(); + new ClassReader(in).accept(clazz, 0); + final var mdName = "net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer"; + final var mdSig = "L" + mdName + ";"; + { + MethodNode constructor = null; + for (var method: clazz.methods) { + if ("".equals(method.name)) { + constructor = method; + } + } + if (constructor == null) { + throw new RuntimeException("Cannot transform ModDiscoverer: not found"); + } + + FrameNode lastFrame = null; + for (var insn: constructor.instructions) { + if (insn instanceof FrameNode frame) { + lastFrame = frame; + } + } + if (lastFrame == null) { + throw new RuntimeException("Cannot transform ModDiscoverer: return not found"); + } + + var aloadThis = new VarInsnNode(Opcodes.ALOAD, 0); + var putField = new FieldInsnNode(Opcodes.PUTSTATIC, mdName, "arclight$INSTANCE", mdSig); + constructor.instructions.insertBefore(lastFrame, aloadThis); + constructor.instructions.insert(aloadThis, putField); + constructor.instructions.insert(putField, new InsnNode(Opcodes.RETURN)); + constructor.instructions.remove(lastFrame); + } + { + clazz.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "arclight$INSTANCE", mdSig, mdSig, null); + } + var cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + clazz.accept(cw); + byte[] bytes = cw.toByteArray(); + Unsafe.defineClass(mdName.replace('/', '.'), bytes, 0, bytes.length, getClass().getClassLoader() /* MC-BOOTSTRAP */, getClass().getProtectionDomain()); + System.out.println("Redefined ModDiscoverer in cl:" +getClass().getClassLoader()); + } catch (Throwable t) { + t.printStackTrace(); + } + } + private void injectClassPath() throws Throwable { var platform = ClassLoader.getPlatformClassLoader(); var ucpField = platform.getClass().getSuperclass().getDeclaredField("ucp"); diff --git a/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ModuleBootstrap.java b/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ModuleBootstrap.java index e8f038716..c72eab259 100644 --- a/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ModuleBootstrap.java +++ b/bootstrap/src/forge/java/io/izzel/arclight/boot/forge/mod/ModuleBootstrap.java @@ -7,6 +7,10 @@ import io.izzel.arclight.i18n.ArclightConfig; import io.izzel.arclight.i18n.ArclightLocale; import net.minecraftforge.bootstrap.api.BootstrapEntryPoint; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; import java.util.Arrays; import java.util.ServiceLoader; @@ -49,4 +53,53 @@ public void main(String[] args) { System.err.println("Fail to launch Arclight."); } } + + @Override + public void dirtyHacks() throws Exception { + AbstractBootstrap.super.dirtyHacks(); + try (var in = getClass().getClassLoader().getResourceAsStream("net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.class")) { + var clazz = new ClassNode(); + new ClassReader(in).accept(clazz, 0); + final var mdName = "net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer"; + final var mdSig = "L" + mdName + ";"; + { + MethodNode constructor = null; + for (var method: clazz.methods) { + if ("".equals(method.name)) { + constructor = method; + } + } + if (constructor == null) { + throw new RuntimeException("Cannot transform ModDiscoverer: not found"); + } + + FrameNode lastFrame = null; + for (var insn: constructor.instructions) { + if (insn instanceof FrameNode frame) { + lastFrame = frame; + } + } + if (lastFrame == null) { + throw new RuntimeException("Cannot transform ModDiscoverer: return not found"); + } + + var aloadThis = new VarInsnNode(Opcodes.ALOAD, 0); + var putField = new FieldInsnNode(Opcodes.PUTSTATIC, mdName, "arclight$INSTANCE", mdSig); + constructor.instructions.insertBefore(lastFrame, aloadThis); + constructor.instructions.insert(aloadThis, putField); + constructor.instructions.insert(putField, new InsnNode(Opcodes.RETURN)); + constructor.instructions.remove(lastFrame); + } + { + clazz.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "arclight$INSTANCE", mdSig, mdSig, null); + } + var cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + clazz.accept(cw); + byte[] bytes = cw.toByteArray(); + Unsafe.defineClass(mdName.replace('/', '.'), bytes, 0, bytes.length, getClass().getClassLoader() /* MC-BOOTSTRAP */, getClass().getProtectionDomain()); + System.out.println("Redefined ModDiscoverer in cl:" +getClass().getClassLoader()); + } catch (Throwable t) { + t.printStackTrace(); + } + } } diff --git a/bootstrap/src/forge/resources/arclight-log4j2.xml b/bootstrap/src/forge/resources/arclight-log4j2.xml index 811bbe626..b6bcecea9 100644 --- a/bootstrap/src/forge/resources/arclight-log4j2.xml +++ b/bootstrap/src/forge/resources/arclight-log4j2.xml @@ -13,8 +13,8 @@ - - + + diff --git a/bootstrap/src/main/java/io/izzel/arclight/boot/AbstractBootstrap.java b/bootstrap/src/main/java/io/izzel/arclight/boot/AbstractBootstrap.java index 15ac95e1a..2875a55a1 100644 --- a/bootstrap/src/main/java/io/izzel/arclight/boot/AbstractBootstrap.java +++ b/bootstrap/src/main/java/io/izzel/arclight/boot/AbstractBootstrap.java @@ -11,14 +11,7 @@ import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.InsnNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.VarInsnNode; +import org.objectweb.asm.tree.*; import java.io.InputStream; import java.lang.reflect.Field; diff --git a/bootstrap/src/neoforge/java/io/izzel/arclight/boot/neoforge/mod/ArclightJarContentsImplFilter.java b/bootstrap/src/neoforge/java/io/izzel/arclight/boot/neoforge/mod/ArclightJarContentsImplFilter.java new file mode 100644 index 000000000..b23b7ad23 --- /dev/null +++ b/bootstrap/src/neoforge/java/io/izzel/arclight/boot/neoforge/mod/ArclightJarContentsImplFilter.java @@ -0,0 +1,66 @@ +package io.izzel.arclight.boot.neoforge.mod; + +import cpw.mods.jarhandling.impl.JarContentsImpl; +import io.izzel.arclight.api.Unsafe; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Filter out packages already provided by Arclight. + * For duplicate modules, the packages will be removed + * before modules are removed. + * For duplicate packages in non-duplicate library modules, + * only the packages will be removed. + * For duplicate packages in non-duplicate mod modules, + * the packages won't be removed + * Duplicated shaded mods are like modules, and are removed + * later by JarInJarFilter. + */ +public class ArclightJarContentsImplFilter { + // Use unsafe to bypass JPMS accessibility check + private static final MethodHandles.Lookup LOOKUP = Unsafe.lookup(); + private static VarHandle PACKAGES; + private static Set serviceLayerPackages; + private static final Logger LOGGER = LogManager.getLogger("Arclight"); + + static { + try { + PACKAGES = LOOKUP.findVarHandle(JarContentsImpl.class, "packages", Set.class); + } catch (ReflectiveOperationException e) { + LOGGER.error("Arclight failed to filter JarContents. This may cause dependency conflicts with some mods!", e); + } + serviceLayerPackages = ArclightJarContentsImplFilter.class + .getModule() + .getLayer() + .modules() + .stream() + .flatMap(it -> it.getPackages().stream()) + .collect(Collectors.toSet()); + } + + /* + * The result of getPackages() is cached + * Through modifying the cache, we modify the result of getPackages() + * Note: ModJarMetadata use getPackagesExcluding(String...), + * which bypass the cache. This won't work for ModJarMetadata. + */ + public static void filter(JarContentsImpl impl) { + if (PACKAGES != null) { + impl.getPackages(); + Set raw = (Set)PACKAGES.get(impl); + Set result = raw.stream() + .filter(ArclightJarContentsImplFilter::test) + .collect(Collectors.toSet()); + PACKAGES.set(impl, result); + } + } + + public static boolean test(String pkg) { + return !serviceLayerPackages.contains(pkg); + } +} diff --git a/bootstrap/src/neoforge/java/io/izzel/arclight/boot/neoforge/mod/ArclightJarInJarFilter.java b/bootstrap/src/neoforge/java/io/izzel/arclight/boot/neoforge/mod/ArclightJarInJarFilter.java index c3f1ff1ea..fc1d997b5 100644 --- a/bootstrap/src/neoforge/java/io/izzel/arclight/boot/neoforge/mod/ArclightJarInJarFilter.java +++ b/bootstrap/src/neoforge/java/io/izzel/arclight/boot/neoforge/mod/ArclightJarInJarFilter.java @@ -4,12 +4,13 @@ import net.neoforged.neoforgespi.locating.IDependencyLocator; import net.neoforged.neoforgespi.locating.IDiscoveryPipeline; import net.neoforged.neoforgespi.locating.IModFile; +import net.neoforged.neoforgespi.locating.IOrderedProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; -public class ArclightJarInJarFilter implements IDependencyLocator { +public class ArclightJarInJarFilter implements IDependencyLocator, IOrderedProvider { private static final Logger LOGGER = LoggerFactory.getLogger("ArclightJiJ"); @@ -35,4 +36,9 @@ public void scanMods(List loadedMods, IDiscoveryPipeline pipeline) { public String toString() { return "arclight_jij"; } + + @Override + public int getPriority() { + return IOrderedProvider.LOWEST_SYSTEM_PRIORITY; + } } diff --git a/bootstrap/src/neoforge/java/io/izzel/arclight/boot/neoforge/mod/ArclightModFileReader.java b/bootstrap/src/neoforge/java/io/izzel/arclight/boot/neoforge/mod/ArclightModFileReader.java new file mode 100644 index 000000000..c6c3131cc --- /dev/null +++ b/bootstrap/src/neoforge/java/io/izzel/arclight/boot/neoforge/mod/ArclightModFileReader.java @@ -0,0 +1,22 @@ +package io.izzel.arclight.boot.neoforge.mod; + +import cpw.mods.jarhandling.JarContents; +import cpw.mods.jarhandling.impl.JarContentsImpl; +import net.neoforged.neoforgespi.locating.IModFile; +import net.neoforged.neoforgespi.locating.IModFileReader; +import net.neoforged.neoforgespi.locating.IOrderedProvider; +import net.neoforged.neoforgespi.locating.ModFileDiscoveryAttributes; +import org.jetbrains.annotations.Nullable; + +public class ArclightModFileReader implements IModFileReader, IOrderedProvider { + @Override + public @Nullable IModFile read(JarContents jarContents, ModFileDiscoveryAttributes modFileDiscoveryAttributes) { + ArclightJarContentsImplFilter.filter((JarContentsImpl) jarContents); + return null; + } + + @Override + public int getPriority() { + return HIGHEST_SYSTEM_PRIORITY; + } +} diff --git a/bootstrap/src/neoforge/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileReader b/bootstrap/src/neoforge/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileReader new file mode 100644 index 000000000..fd4be41da --- /dev/null +++ b/bootstrap/src/neoforge/resources/META-INF/services/net.neoforged.neoforgespi.locating.IModFileReader @@ -0,0 +1 @@ +io.izzel.arclight.boot.neoforge.mod.ArclightModFileReader \ No newline at end of file