diff --git a/src/main/java/appeng/core/sync/packets/ICustomEntity.java b/src/main/java/appeng/core/sync/packets/ICustomEntity.java index 6da93d6ae..2fbbbb696 100644 --- a/src/main/java/appeng/core/sync/packets/ICustomEntity.java +++ b/src/main/java/appeng/core/sync/packets/ICustomEntity.java @@ -1,6 +1,5 @@ package appeng.core.sync.packets; -import net.minecraft.entity.EntityType; import net.minecraft.network.PacketByteBuf; public interface ICustomEntity { diff --git a/src/main/java/appeng/entity/AEBaseItemEntity.java b/src/main/java/appeng/entity/AEBaseItemEntity.java index cab8ae3d9..b2fc2452d 100644 --- a/src/main/java/appeng/entity/AEBaseItemEntity.java +++ b/src/main/java/appeng/entity/AEBaseItemEntity.java @@ -24,10 +24,14 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.ItemEntity; import net.minecraft.item.ItemStack; +import net.minecraft.network.Packet; import net.minecraft.util.math.Box; import net.minecraft.world.World; -public abstract class AEBaseItemEntity extends ItemEntity { +import appeng.core.sync.packets.ICustomEntity; +import appeng.core.sync.packets.SpawnEntityPacket; + +public abstract class AEBaseItemEntity extends ItemEntity implements ICustomEntity { protected AEBaseItemEntity(EntityType entityType, final World world) { super(entityType, world); @@ -48,4 +52,9 @@ public abstract class AEBaseItemEntity extends ItemEntity { return this.world.getOtherEntities(this, region); } + @Override + public Packet createSpawnPacket() { + return SpawnEntityPacket.create(this); + } + } diff --git a/src/main/java/appeng/entity/ChargedQuartzEntity.java b/src/main/java/appeng/entity/ChargedQuartzEntity.java index 096cdec33..4f849c0fd 100644 --- a/src/main/java/appeng/entity/ChargedQuartzEntity.java +++ b/src/main/java/appeng/entity/ChargedQuartzEntity.java @@ -38,7 +38,6 @@ import appeng.api.features.AEFeature; import appeng.client.render.effects.ParticleTypes; import appeng.core.AEConfig; import appeng.core.Api; -import appeng.core.AppEng; import appeng.util.Platform; public final class ChargedQuartzEntity extends AEBaseItemEntity { diff --git a/src/main/java/appeng/hooks/AECustomEntityItem.java b/src/main/java/appeng/hooks/AECustomEntityItem.java new file mode 100644 index 000000000..07b069de5 --- /dev/null +++ b/src/main/java/appeng/hooks/AECustomEntityItem.java @@ -0,0 +1,16 @@ +package appeng.hooks; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.server.world.ServerWorld; + +public interface AECustomEntityItem { + + /** + * @return Either a new entity (not added to the world yet), or the original one + * to keep it. + */ + Entity replaceItemEntity(ServerWorld world, ItemEntity itemEntity, ItemStack itemStack); + +} diff --git a/src/main/java/appeng/items/materials/MaterialItem.java b/src/main/java/appeng/items/materials/MaterialItem.java index cc770a2b1..05035b880 100644 --- a/src/main/java/appeng/items/materials/MaterialItem.java +++ b/src/main/java/appeng/items/materials/MaterialItem.java @@ -24,13 +24,15 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.block.entity.BlockEntity; import net.minecraft.client.item.TooltipContext; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemUsageContext; import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; -import net.minecraft.text.TranslatableText; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.TypedActionResult; @@ -45,12 +47,14 @@ import appeng.api.implementations.items.IUpgradeModule; import appeng.api.implementations.tiles.ISegmentedInventory; import appeng.api.parts.IPartHost; import appeng.api.parts.SelectedPart; +import appeng.hooks.AECustomEntityItem; import appeng.hooks.AEToolItem; import appeng.items.AEBaseItem; import appeng.util.InventoryAdaptor; import appeng.util.inv.AdaptorFixedInv; -public final class MaterialItem extends AEBaseItem implements IStorageComponent, IUpgradeModule, AEToolItem { +public final class MaterialItem extends AEBaseItem + implements IStorageComponent, IUpgradeModule, AEToolItem, AECustomEntityItem { /** * NBT property used by the name press to store the name to be inscribed. @@ -147,31 +151,30 @@ public final class MaterialItem extends AEBaseItem implements IStorageComponent, return ActionResult.PASS; } -// FIXME FABRIC @Override -// FIXME FABRIC public boolean hasCustomEntity(final ItemStack is) { -// FIXME FABRIC return materialType.hasCustomEntity(); -// FIXME FABRIC } + @Override + public Entity replaceItemEntity(ServerWorld world, ItemEntity itemEntity, ItemStack itemStack) { + if (!materialType.hasCustomEntity()) { + return itemEntity; + } -// FIXME FABRIC @Override -// FIXME FABRIC public Entity createEntity(final World w, final Entity location, final ItemStack itemstack) { -// FIXME FABRIC final Class droppedEntity = materialType.getCustomEntityClass(); -// FIXME FABRIC final Entity eqi; + final Class droppedEntity = materialType.getCustomEntityClass(); + final Entity eqi; -// FIXME FABRIC try { -// FIXME FABRIC eqi = droppedEntity.getConstructor(World.class, double.class, double.class, double.class, ItemStack.class) -// FIXME FABRIC .newInstance(w, location.getX(), location.getY(), location.getZ(), itemstack); -// FIXME FABRIC } catch (final Throwable t) { -// FIXME FABRIC throw new IllegalStateException(t); -// FIXME FABRIC } + try { + eqi = droppedEntity.getConstructor(World.class, double.class, double.class, double.class, ItemStack.class) + .newInstance(world, itemEntity.getX(), itemEntity.getY(), itemEntity.getZ(), itemStack); + } catch (final Throwable t) { + throw new IllegalStateException(t); + } -// FIXME FABRIC eqi.setVelocity(location.getVelocity()); + eqi.setVelocity(itemEntity.getVelocity()); -// FIXME FABRIC if (location instanceof ItemEntity && eqi instanceof ItemEntity) { -// FIXME FABRIC ((ItemEntity) eqi).setDefaultPickupDelay(); -// FIXME FABRIC } + if (eqi instanceof ItemEntity) { + ((ItemEntity) eqi).setToDefaultPickupDelay(); + } -// FIXME FABRIC return eqi; -// FIXME FABRIC } + return eqi; + } @Override public int getBytes(final ItemStack is) { diff --git a/src/main/java/appeng/items/misc/CrystalSeedItem.java b/src/main/java/appeng/items/misc/CrystalSeedItem.java index 06c2936af..dc1204dc3 100644 --- a/src/main/java/appeng/items/misc/CrystalSeedItem.java +++ b/src/main/java/appeng/items/misc/CrystalSeedItem.java @@ -29,10 +29,13 @@ import net.fabricmc.api.Environment; import net.minecraft.block.Block; import net.minecraft.block.Material; import net.minecraft.client.item.TooltipContext; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; import net.minecraft.item.ItemConvertible; import net.minecraft.item.ItemGroup; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; import net.minecraft.util.collection.DefaultedList; @@ -41,13 +44,15 @@ import net.minecraft.world.World; import appeng.api.implementations.items.IGrowableCrystal; import appeng.core.localization.ButtonToolTips; +import appeng.entity.GrowingCrystalEntity; +import appeng.hooks.AECustomEntityItem; import appeng.items.AEBaseItem; /** * This item reprents one of the seeds used to grow various forms of quartz by * throwing them into water (for that behavior, see the linked entity) */ -public class CrystalSeedItem extends AEBaseItem implements IGrowableCrystal { +public class CrystalSeedItem extends AEBaseItem implements IGrowableCrystal, AECustomEntityItem { /** * Name of NBT tag used to store the growth progress value. @@ -111,25 +116,19 @@ public class CrystalSeedItem extends AEBaseItem implements IGrowableCrystal { return new LiteralText(Math.round(100 * progress / (float) GROWTH_TICKS_REQUIRED) + "%"); } -// FIXME FABRIC Needs custom mixin -// FIXME FABRIC @Override -// FIXME FABRIC public boolean hasCustomEntity(final ItemStack stack) { -// FIXME FABRIC return true; -// FIXME FABRIC } -// FIXME FABRIC -// FIXME FABRIC @Override -// FIXME FABRIC public Entity createEntity(final World world, final Entity location, final ItemStack itemstack) { -// FIXME FABRIC final GrowingCrystalEntity egc = new GrowingCrystalEntity(world, location.getX(), location.getY(), -// FIXME FABRIC location.getZ(), itemstack); -// FIXME FABRIC -// FIXME FABRIC egc.setVelocity(location.getVelocity()); -// FIXME FABRIC -// FIXME FABRIC // Cannot read the pickup delay of the original item, so we -// FIXME FABRIC // use the pickup delay used for items dropped by a player instead -// FIXME FABRIC egc.setPickupDelay(40); -// FIXME FABRIC -// FIXME FABRIC return egc; -// FIXME FABRIC } + @Override + public Entity replaceItemEntity(ServerWorld world, ItemEntity itemEntity, ItemStack itemStack) { + final GrowingCrystalEntity egc = new GrowingCrystalEntity(world, itemEntity.getX(), itemEntity.getY(), + itemEntity.getZ(), itemStack); + + egc.setVelocity(itemEntity.getVelocity()); + + // Cannot read the pickup delay of the original item, so we + // use the pickup delay used for items dropped by a player instead + egc.setPickupDelay(40); + + return egc; + } @Override public void appendStacks(ItemGroup group, DefaultedList items) { diff --git a/src/main/java/appeng/mixins/ServerWorldCustomItemEntityMixin.java b/src/main/java/appeng/mixins/ServerWorldCustomItemEntityMixin.java new file mode 100644 index 000000000..b2d68375a --- /dev/null +++ b/src/main/java/appeng/mixins/ServerWorldCustomItemEntityMixin.java @@ -0,0 +1,45 @@ +package appeng.mixins; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.world.ServerWorld; + +import appeng.hooks.AECustomEntityItem; + +/** + * This Mixin will replace newly spawned ItemEntities with custom entities if + * the item wants it. This is not applied retroactively to entities that are + * already spawned and just re-added to the world when a chunk is loaded. + */ +@Mixin(value = ServerWorld.class, priority = 9999) +public class ServerWorldCustomItemEntityMixin { + + @SuppressWarnings("ConstantConditions") + @ModifyVariable(method = { "spawnEntity" }, at = @At("HEAD"), argsOnly = true) + public Entity onSpawnEntity(Entity entity) { + if (entity instanceof ItemEntity) { + ItemEntity itemEntity = (ItemEntity) entity; + ItemStack stack = itemEntity.getStack(); + if (stack == null) { + return entity; + } + Item item = stack.getItem(); + if (item instanceof AECustomEntityItem) { + ServerWorld self = (ServerWorld) (Object) this; + entity = ((AECustomEntityItem) item).replaceItemEntity(self, itemEntity, stack); + if (entity != itemEntity) { + // Item may actually want to keep the original + itemEntity.remove(); + } + } + } + return entity; + } + +} diff --git a/src/main/java/appeng/parts/automation/FormationPlanePart.java b/src/main/java/appeng/parts/automation/FormationPlanePart.java index ac03e24c7..72380e121 100644 --- a/src/main/java/appeng/parts/automation/FormationPlanePart.java +++ b/src/main/java/appeng/parts/automation/FormationPlanePart.java @@ -62,6 +62,7 @@ import appeng.container.ContainerOpener; import appeng.container.implementations.FormationPlaneContainer; import appeng.core.AEConfig; import appeng.core.Api; +import appeng.hooks.AECustomEntityItem; import appeng.items.parts.PartModels; import appeng.me.GridAccessException; import appeng.me.storage.MEInventoryHandler; @@ -203,6 +204,11 @@ public class FormationPlanePart extends AbstractFormationPlanePart final BlockEntity te = this.getHost().getTile(); final World w = te.getWorld(); + if (!(w instanceof ServerWorld)) { + return input; + } + ServerWorld serverWorld = (ServerWorld) w; + final AEPartLocation side = this.getSide(); final BlockPos placePos = te.getPos().offset(side.getFacing()); @@ -210,7 +216,7 @@ public class FormationPlanePart extends AbstractFormationPlanePart if (w.getBlockState(placePos).getMaterial().isReplaceable()) { if (placeBlock == YesNo.YES && (i instanceof BlockItem || i instanceof FireworkChargeItem || i instanceof FireworkItem || i instanceof IPartItem)) { - final PlayerEntity player = FakePlayer.getOrCreate((ServerWorld) w); + final PlayerEntity player = FakePlayer.getOrCreate(serverWorld); Platform.configurePlayer(player, side, this.getTile()); Hand hand = player.getActiveHand(); player.setStackInHand(hand, is); @@ -279,20 +285,17 @@ public class FormationPlanePart extends AbstractFormationPlanePart + .5 + te.getPos().getZ(); final ItemEntity ei = new ItemEntity(w, x, y, z, is.copy()); - - Entity result = ei; - ei.setVelocity(side.xOffset * 0.2, side.yOffset * 0.2, side.zOffset * 0.2); - // FIXME FABRIC No custom item entity support - // FIXME FABRIC if (is.getItem().hasCustomEntity(is)) { - // FIXME FABRIC result = is.getItem().createEntity(w, ei, is); - // FIXME FABRIC if (result != null) { - // FIXME FABRIC ei.remove(); - // FIXME FABRIC } else { - // FIXME FABRIC result = ei; - // FIXME FABRIC } - // FIXME FABRIC } + Entity result; + if (is.getItem() instanceof AECustomEntityItem) { + result = ((AECustomEntityItem) is.getItem()).replaceItemEntity(serverWorld, ei, is); + if (result != ei) { + ei.remove(); + } + } else { + result = ei; + } if (!w.spawnEntity(result)) { result.remove(); diff --git a/src/main/resources/appliedenergistics2.mixins.json b/src/main/resources/appliedenergistics2.mixins.json index 8641f3830..471b58301 100644 --- a/src/main/resources/appliedenergistics2.mixins.json +++ b/src/main/resources/appliedenergistics2.mixins.json @@ -14,7 +14,8 @@ "MinecraftServerMixin", "ThreadedAnvilChunkStorageAccessor", "GenerationSettingsAccessor", - "BiomeAccessor" + "BiomeAccessor", + "ServerWorldCustomItemEntityMixin" ], "client": [ "ModelsReloadMixin",