Skip to content

Commit

Permalink
Fix duplicate deps crashing server on NeoForge/Forge (#1662)
Browse files Browse the repository at this point in the history
* fix forge JIJ locator; support forge transforming BucketItem.fluid

* fix forge crashing GameTest

* Fix duplicated dependencies causing module resolution failure; Fixes #1011

* Fix forge launch crashing!

* Remove redundant injection.

* Move forge injection to corresponding bootstrap class

* Move forge injection to corresponding bootstrap class

* Add a check to avoid possible redundant modification of ModDiscoverer

* We don't need to check since ModBootstrap won't run in application bootstrap mode
  • Loading branch information
InitAuther97 authored Feb 19, 2025
1 parent 329f5cb commit 7cc6e16
Show file tree
Hide file tree
Showing 40 changed files with 554 additions and 156 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}");
Expand All @@ -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());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<InteractionResult> 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<InteractionResult> cir) {
this.forceDrops = false;
}
// Force drop handler moved to PSI
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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<InteractionResultHolder<ItemStack>> 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"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -57,30 +55,12 @@ public abstract class AbstractFurnaceBlockEntityMixin extends LockableBlockEntit
public List<HumanEntity> 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> E arclight$furnaceSmelt(NonNullList<E> 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> E arclight$furnaceSmelt(NonNullList<E> 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")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Boolean> cir) {
var entity = ((HopperBlockEntity) ArclightCaptures.getTickingBlockEntity());
if (entity == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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; }
}
Original file line number Diff line number Diff line change
@@ -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<InteractionResult> 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<InteractionResult> cir) {
this.forceDrops = false;
}
}
Original file line number Diff line number Diff line change
@@ -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> E arclight$furnaceSmelt(NonNullList<E> 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);
}
}
2 changes: 1 addition & 1 deletion arclight-common/src/main/resources/arclight.accesswidener
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading

0 comments on commit 7cc6e16

Please sign in to comment.