diff --git a/build.gradle b/build.gradle index f602009..d6baced 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,12 @@ repositories { includeGroup "mezz.jei" } } + maven { + url "https://www.cursemaven.com" + content { + includeGroup "curse.maven" + } + } } dependencies { @@ -79,7 +85,7 @@ dependencies { throw new GradleException("Invalid runtime_itemlist_mod value: " + project.runtime_itemlist_mod) } - runtimeOnly("mcp.mobius.waila:wthit:neo-${project.wthit_version}") + implementation("curse.maven:jade-324717:${project.jade_file_id}") } processResources { diff --git a/gradle.properties b/gradle.properties index 76d7d53..5edae48 100644 --- a/gradle.properties +++ b/gradle.properties @@ -29,7 +29,6 @@ neogradle.subsystems.parchment.mappingsVersion=2023.12.26-nightly-SNAPSHOT # In-dev runtime mods rei_version=14.0.688 -wthit_version=10.1.0 jei_minecraft_version=1.20.4 jei_version=17.0.0.37 @@ -40,3 +39,5 @@ emi_version=1.1.0 # Set to emi, jei, or rei to pick which tooltip mod gets picked at runtime # for the dev environment. runtime_itemlist_mod=emi + +jade_file_id=5109393 diff --git a/src/main/java/dev/technici4n/moderndynamics/compat/jade/ItemPipeServerProvider.java b/src/main/java/dev/technici4n/moderndynamics/compat/jade/ItemPipeServerProvider.java new file mode 100644 index 0000000..eeaa6ad --- /dev/null +++ b/src/main/java/dev/technici4n/moderndynamics/compat/jade/ItemPipeServerProvider.java @@ -0,0 +1,148 @@ +/* + * Modern Dynamics + * Copyright (C) 2021 shartte & Technici4n + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package dev.technici4n.moderndynamics.compat.jade; + +import dev.technici4n.moderndynamics.attachment.attached.ItemAttachedIo; +import dev.technici4n.moderndynamics.network.item.ItemHost; +import dev.technici4n.moderndynamics.pipe.PipeBlockEntity; +import dev.technici4n.moderndynamics.util.ItemVariant; +import dev.technici4n.moderndynamics.util.MdId; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import net.minecraft.ChatFormatting; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.Style; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; +import snownee.jade.api.Accessor; +import snownee.jade.api.view.ClientViewGroup; +import snownee.jade.api.view.IClientExtensionProvider; +import snownee.jade.api.view.IServerExtensionProvider; +import snownee.jade.api.view.ItemView; +import snownee.jade.api.view.ViewGroup; + +public enum ItemPipeServerProvider implements IServerExtensionProvider, IClientExtensionProvider { + INSTANCE; + + @Override + public ResourceLocation getUid() { + return MdId.of("item_pipe"); + } + + @Override + public @Nullable List> getGroups(Accessor accessor, PipeBlockEntity pipe) { + // Send attachment data here + var itemHost = pipe.findHost(ItemHost.class); + if (itemHost == null) { + return null; + } + + List> groups = new ArrayList<>(); + + for (var side : Direction.values()) { + if (pipe.getAttachment(side) instanceof ItemAttachedIo io) { + if (io.isStuffed()) { + var group = new ViewGroup(new ArrayList<>()); + group.views.addAll(variantMapToStacks(io.getStuffedItems())); + group.id = "stuffed_" + side.getName(); + groups.add(group); + } + } + } + + // Always add a dummy group. + // We need to run on the client side to add client traveling items on the client side, + // even if the server doesn't provide any stack. + var dummyGroup = new ViewGroup(new ArrayList<>()); + dummyGroup.views.add(ItemStack.EMPTY); + dummyGroup.id = "dummy"; + groups.add(dummyGroup); + + return groups; + } + + private static Collection variantMapToStacks(Map map) { + List stacks = new ArrayList<>(); + for (var entry : map.entrySet()) { + stacks.add(entry.getKey().toStack(entry.getValue())); + } + stacks.sort(Comparator.comparingInt(ItemStack::getCount).reversed()); + return stacks; + } + + @Override + public List> getClientGroups(Accessor accessor, List> serverGroups) { + if (!(accessor.getTarget() instanceof PipeBlockEntity pipe)) { + return List.of(); + } + + List> clientGroups = new ArrayList<>(); + + // Stuffed items + var posInBlock = pipe.getPosInBlock(accessor.getHitResult()); + var hitAttachmentSide = pipe.hitTestAttachments(posInBlock); + if (hitAttachmentSide != null) { + var attachmentItem = pipe.getAttachmentItem(hitAttachmentSide); + if (attachmentItem != null) { + // Find corresponding server group + serverGroups.stream() + .filter(vg -> vg.id != null && vg.id.equals("stuffed_" + hitAttachmentSide.getName())) + .findFirst() + .ifPresent(group -> { + var clientGroup = new ClientViewGroup(new ArrayList<>()); + for (var stack : group.views) { + clientGroup.views.add(new ItemView(stack)); + } + clientGroup.title = Component.translatable("gui.moderndynamics.tooltip.stuffed", attachmentItem.getDescription()) + .withStyle(Style.EMPTY.withColor(ChatFormatting.RED).withBold(true)); + clientGroups.add(clientGroup); + }); + } + } + + // Pipe contents, only if nothing is stuffed + if (!clientGroups.isEmpty()) { + return clientGroups; + } + for (var host : pipe.getHosts()) { + if (host instanceof ItemHost itemHost) { + Map items = new HashMap<>(); + for (var item : itemHost.getClientTravelingItems()) { + items.merge(item.variant(), item.amount(), Integer::sum); + } + + var clientGroup = new ClientViewGroup(new ArrayList<>()); + for (var stack : variantMapToStacks(items)) { + clientGroup.views.add(new ItemView(stack)); + } + if (!clientGroup.views.isEmpty()) { + clientGroups.add(clientGroup); + } + } + } + + return clientGroups; + } +} diff --git a/src/main/java/dev/technici4n/moderndynamics/compat/jade/MdJadePlugin.java b/src/main/java/dev/technici4n/moderndynamics/compat/jade/MdJadePlugin.java new file mode 100644 index 0000000..a937476 --- /dev/null +++ b/src/main/java/dev/technici4n/moderndynamics/compat/jade/MdJadePlugin.java @@ -0,0 +1,38 @@ +/* + * Modern Dynamics + * Copyright (C) 2021 shartte & Technici4n + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package dev.technici4n.moderndynamics.compat.jade; + +import dev.technici4n.moderndynamics.pipe.PipeBlockEntity; +import snownee.jade.api.IWailaClientRegistration; +import snownee.jade.api.IWailaCommonRegistration; +import snownee.jade.api.IWailaPlugin; +import snownee.jade.api.WailaPlugin; + +@WailaPlugin +public class MdJadePlugin implements IWailaPlugin { + @Override + public void register(IWailaCommonRegistration registration) { + registration.registerItemStorage(ItemPipeServerProvider.INSTANCE, PipeBlockEntity.class); + } + + @Override + public void registerClient(IWailaClientRegistration registration) { + registration.registerItemStorageClient(ItemPipeServerProvider.INSTANCE); + } +} diff --git a/src/main/java/dev/technici4n/moderndynamics/model/PipeModelData.java b/src/main/java/dev/technici4n/moderndynamics/model/PipeModelData.java index 27a4562..9faf702 100644 --- a/src/main/java/dev/technici4n/moderndynamics/model/PipeModelData.java +++ b/src/main/java/dev/technici4n/moderndynamics/model/PipeModelData.java @@ -23,7 +23,7 @@ import org.jetbrains.annotations.Nullable; public record PipeModelData(byte pipeConnections, byte inventoryConnections, - AttachmentModelData @Nullable [] attachments) { + @Nullable AttachmentModelData[] attachments) { public static ModelProperty PIPE_DATA = new ModelProperty<>(); diff --git a/src/main/java/dev/technici4n/moderndynamics/network/item/ItemHost.java b/src/main/java/dev/technici4n/moderndynamics/network/item/ItemHost.java index 76975f8..4cc4143 100644 --- a/src/main/java/dev/technici4n/moderndynamics/network/item/ItemHost.java +++ b/src/main/java/dev/technici4n/moderndynamics/network/item/ItemHost.java @@ -501,7 +501,7 @@ public void writeClientNbt(CompoundTag tag) { CompoundTag compound = new CompoundTag(); compound.putInt("id", travelingItem.id); compound.put("v", travelingItem.variant.toNbt()); - compound.putLong("a", travelingItem.amount); + compound.putInt("a", travelingItem.amount); compound.putDouble("td", travelingItem.getPathLength() - 1); compound.putDouble("d", travelingItem.traveledDistance); int currentBlock = (int) Math.floor(travelingItem.traveledDistance); @@ -525,7 +525,7 @@ public void readClientNbt(CompoundTag tag) { var newItem = new ClientTravelingItem( compound.getInt("id"), ItemVariant.fromNbt(compound.getCompound("v")), - compound.getLong("a"), + compound.getInt("a"), compound.getDouble("td"), compound.getDouble("d"), Direction.from3DDataValue(compound.getByte("in")), diff --git a/src/main/java/dev/technici4n/moderndynamics/network/item/sync/ClientTravelingItem.java b/src/main/java/dev/technici4n/moderndynamics/network/item/sync/ClientTravelingItem.java index 2236d33..6a01443 100644 --- a/src/main/java/dev/technici4n/moderndynamics/network/item/sync/ClientTravelingItem.java +++ b/src/main/java/dev/technici4n/moderndynamics/network/item/sync/ClientTravelingItem.java @@ -24,14 +24,14 @@ public final class ClientTravelingItem { public final int id; private final ItemVariant variant; - private final long amount; + private final int amount; public final double totalPathDistance; public double traveledDistance; public Direction in; public Direction out; final double speed; - public ClientTravelingItem(int id, ItemVariant variant, long amount, double totalPathDistance, double traveledDistance, Direction in, + public ClientTravelingItem(int id, ItemVariant variant, int amount, double totalPathDistance, double traveledDistance, Direction in, Direction out, double speed) { this.id = id; this.variant = variant; @@ -47,7 +47,7 @@ public ItemVariant variant() { return variant; } - public long amount() { + public int amount() { return amount; } diff --git a/src/main/java/dev/technici4n/moderndynamics/pipe/PipeBlockEntity.java b/src/main/java/dev/technici4n/moderndynamics/pipe/PipeBlockEntity.java index 3e21e69..a280fb6 100644 --- a/src/main/java/dev/technici4n/moderndynamics/pipe/PipeBlockEntity.java +++ b/src/main/java/dev/technici4n/moderndynamics/pipe/PipeBlockEntity.java @@ -29,6 +29,7 @@ import dev.technici4n.moderndynamics.util.DropHelper; import dev.technici4n.moderndynamics.util.ShapeHelper; import dev.technici4n.moderndynamics.util.WrenchHelper; +import java.util.Objects; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.NonNullList; @@ -39,6 +40,7 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -79,6 +81,16 @@ public final NodeHost[] getHosts() { return hosts; } + @Nullable + public final T findHost(Class hostClass) { + for (var host : getHosts()) { + if (hostClass.isInstance(host)) { + return hostClass.cast(host); + } + } + return null; + } + private boolean hasAttachment(Direction side) { if (isClientSide()) { var pipeData = getPipeModelData(); @@ -105,6 +117,21 @@ public final AttachedAttachment getAttachment(Direction side) { return null; } + @Nullable + public Item getAttachmentItem(Direction side) { + if (isClientSide()) { + var pipeModelData = getPipeModelData(); + if (pipeModelData != null) { + var attachment = pipeModelData.attachments()[side.get3DDataValue()]; + return attachment == null ? null : attachment.item(); + } + return null; + } else { + var attachment = getAttachment(side); + return attachment == null ? null : attachment.getItem(); + } + } + @Override public void sync() { super.sync(); @@ -332,7 +359,7 @@ protected void updateConnection(Direction side, boolean addConnection) { public InteractionResult onUse(Player player, InteractionHand hand, BlockHitResult hitResult) { var stack = player.getItemInHand(hand); - Vec3 posInBlock = hitResult.getLocation().subtract(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); + Vec3 posInBlock = getPosInBlock(hitResult); if (WrenchHelper.isWrench(stack)) { // If the core was hit, add back the pipe on the target side @@ -444,6 +471,10 @@ public InteractionResult onUse(Player player, InteractionHand hand, BlockHitResu return InteractionResult.PASS; } + public Vec3 getPosInBlock(HitResult hitResult) { + return hitResult.getLocation().subtract(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); + } + @Nullable public Direction hitTestAttachments(Vec3 posInBlock) { // Handle click on attachment @@ -459,11 +490,9 @@ public Direction hitTestAttachments(Vec3 posInBlock) { } public ItemStack overridePickBlock(HitResult hitResult) { - Vec3 posInBlock = hitResult.getLocation().subtract(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - Direction side = hitTestAttachments(posInBlock); - var pipeModelData = getPipeModelData(); - if (pipeModelData != null) { - return side != null ? new ItemStack(pipeModelData.attachments()[side.get3DDataValue()].item()) : ItemStack.EMPTY; + Direction side = hitTestAttachments(getPosInBlock(hitResult)); + if (side != null) { + return Objects.requireNonNull(getAttachmentItem(side), "Failed to get attachment item").getDefaultInstance(); } return ItemStack.EMPTY; } diff --git a/src/main/resources/assets/moderndynamics/lang/en_us.json b/src/main/resources/assets/moderndynamics/lang/en_us.json index a4e515f..02c9fd9 100644 --- a/src/main/resources/assets/moderndynamics/lang/en_us.json +++ b/src/main/resources/assets/moderndynamics/lang/en_us.json @@ -7,6 +7,7 @@ "block.moderndynamics.machine_extender": "Machine Extender", "block.moderndynamics.mv_cable": "MV EU Cable", "block.moderndynamics.superconductor_cable": "Superconductor EU Cable", + "config.jade.moderndynamics.item_pipe": "Item Pipes", "gui.moderndynamics.rei.upgrade_category": "Modern Dynamics Upgrades", "gui.moderndynamics.setting.filter_damage.ignore_damage": "Ignore Damage", "gui.moderndynamics.setting.filter_damage.respect_damage": "Match Damage", @@ -53,6 +54,7 @@ "gui.moderndynamics.tooltip.slot.upgrade": "Upgrade Slot", "gui.moderndynamics.tooltip.slot.upgrade_desc1": "Press U on an Attractor/Extractor/Filter", "gui.moderndynamics.tooltip.slot.upgrade_desc2": "in EMI/JEI/REI to view available upgrades.", + "gui.moderndynamics.tooltip.stuffed": "Stuffed %s", "gui.moderndynamics.tooltip.upgrade_addFilterSlots": "Filter Slots: %s", "gui.moderndynamics.tooltip.upgrade_addFluidTransfer": "Base Fluid Transfer: %s", "gui.moderndynamics.tooltip.upgrade_addItemCount": "Max Moved Items: %s",