From d927d79fb2ebcad2cbc3df3bba77824997411575 Mon Sep 17 00:00:00 2001 From: RecursivePineapple Date: Mon, 23 Sep 2024 00:01:15 -0400 Subject: [PATCH] worked on the block analyzer & applier --- .../tileentity/IRedstoneEmitter.java | 19 +- .../metatileentity/CoverableTileEntity.java | 9 +- .../java/gregtech/api/util/GTUtility.java | 59 ++ src/main/java/gregtech/api/util/Lazy.java | 25 + .../matterManipulator/BlockAnalyzer.java | 685 +++++++++++++++--- .../ItemMatterManipulator.java | 45 +- .../items/matterManipulator/PendingBuild.java | 122 ++-- .../tileentities/base/TileEntityBase.java | 9 +- 8 files changed, 796 insertions(+), 177 deletions(-) create mode 100644 src/main/java/gregtech/api/util/Lazy.java diff --git a/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneEmitter.java b/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneEmitter.java index 1d8047a2e42..42dff0802dc 100644 --- a/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneEmitter.java +++ b/src/main/java/gregtech/api/interfaces/tileentity/IRedstoneEmitter.java @@ -39,10 +39,23 @@ public interface IRedstoneEmitter extends IHasWorldObjectAndCoords { void setStrongOutputRedstoneSignal(ForgeDirection side, byte aStrength); /** - * Works the same as {@link #setStrongOutputRedstoneSignal(ForgeDirection, byte)}, except it sets the output on the - * given side to be a weak signal. + * Sets the strength of the redstone signal on the given side to strong or weak. + * Does not change the actual signal. + * + * @param side Must not be UNKNOWN + * @param isStrong True = strong, false = weak */ - default void setWeakOutputRedstoneSignal(ForgeDirection side, byte aStrength) {} + default void setRedstoneOutputStrength(ForgeDirection side, boolean isStrong) {} + + /** + * Checks if the given side will output a strong redstone signal when emitting a redstone signal. + * + * @param side Must not be UNKNOWN + * @return True = strong signal, false = weak signal + */ + default boolean getRedstoneOutputStrength(ForgeDirection side) { + return false; + } /** * Gets the Output for the comparator on the given Side diff --git a/src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java b/src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java index c90a83fe2da..4f78ddf3035 100644 --- a/src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java +++ b/src/main/java/gregtech/api/metatileentity/CoverableTileEntity.java @@ -462,9 +462,14 @@ public void setStrongOutputRedstoneSignal(ForgeDirection side, byte strength) { } @Override - public void setWeakOutputRedstoneSignal(ForgeDirection side, byte aStrength) { + public void setRedstoneOutputStrength(ForgeDirection side, boolean isStrong) { mStrongRedstone &= ~(byte) side.flag; - setOutputRedstoneSignal(side, aStrength); + setOutputRedstoneSignal(side, mSidedRedstone[side.ordinal()]); + } + + @Override + public boolean getRedstoneOutputStrength(ForgeDirection side) { + return (mStrongRedstone & side.flag) != 0; } @Override diff --git a/src/main/java/gregtech/api/util/GTUtility.java b/src/main/java/gregtech/api/util/GTUtility.java index 3d50c21c784..6753354a500 100644 --- a/src/main/java/gregtech/api/util/GTUtility.java +++ b/src/main/java/gregtech/api/util/GTUtility.java @@ -120,6 +120,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.SetMultimap; +import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; import com.gtnewhorizon.structurelib.alignment.IAlignment; import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider; import com.mojang.authlib.GameProfile; @@ -1776,6 +1777,59 @@ public static boolean areUnificationsEqual(ItemStack aStack1, ItemStack aStack2, aIgnoreNBT); } + public static ItemStackMap getItemStackHistogram(ItemStack[] stacks) { + return getItemStackHistogram(stacks, true); + } + + public static ItemStackMap getItemStackHistogram(ItemStack[] stacks, boolean NBTSensitive) { + ItemStackMap histogram = new ItemStackMap<>(NBTSensitive); + + if (stacks == null) return histogram; + + for (ItemStack stack : stacks) { + if (stack == null) continue; + histogram.merge(stack, (long) stack.stackSize, (Long a, Long b) -> a + b); + } + + return histogram; + } + + public static ItemStackMap getItemStackHistogram(Iterable stacks) { + return getItemStackHistogram(stacks, true); + } + + public static ItemStackMap getItemStackHistogram(Iterable stacks, boolean NBTSensitive) { + ItemStackMap histogram = new ItemStackMap<>(NBTSensitive); + + if (stacks == null) return histogram; + + for (ItemStack stack : stacks) { + if (stack == null) continue; + histogram.merge(stack, (long) stack.stackSize, (Long a, Long b) -> a + b); + } + + return histogram; + } + + public static ArrayList getStacksOfSize(ItemStackMap map, int maxStackSize) { + ArrayList list = new ArrayList<>(); + + map.forEach((item, amount) -> { + while (amount > 0) { + int toRemove = Math + .min(amount > Integer.MAX_VALUE ? Integer.MAX_VALUE : amount.intValue(), maxStackSize); + + ItemStack copy = item.copy(); + copy.stackSize = toRemove; + list.add(copy); + + amount -= toRemove; + } + }); + + return list; + } + public static String getFluidName(Fluid aFluid, boolean aLocalized) { if (aFluid == null) return E; String rName = aLocalized ? aFluid.getLocalizedName(new FluidStack(aFluid, 0)) : aFluid.getUnlocalizedName(); @@ -4614,6 +4668,11 @@ public static Stream streamCompounds(NBTTagList list) { .mapToObj(list::getCompoundTagAt); } + public static Stream streamInventory(IInventory inv) { + return IntStream.range(0, inv.getSizeInventory()) + .mapToObj(inv::getStackInSlot); + } + public static boolean equals(ItemStack[] a, ItemStack[] b) { // because stupid mojang didn't override equals for us if (a == null && b == null) return true; diff --git a/src/main/java/gregtech/api/util/Lazy.java b/src/main/java/gregtech/api/util/Lazy.java new file mode 100644 index 00000000000..f50f0483d53 --- /dev/null +++ b/src/main/java/gregtech/api/util/Lazy.java @@ -0,0 +1,25 @@ +package gregtech.api.util; + +import java.util.function.Supplier; + +public class Lazy { + + private boolean hasValue = false; + private T value; + + private Supplier getter; + + public Lazy(Supplier getter) { + this.getter = getter; + } + + public synchronized T get() { + if (!hasValue) { + value = getter.get(); + getter = null; + hasValue = true; + } + + return value; + } +} diff --git a/src/main/java/gregtech/common/items/matterManipulator/BlockAnalyzer.java b/src/main/java/gregtech/common/items/matterManipulator/BlockAnalyzer.java index 899c0a9a174..a14772a730c 100644 --- a/src/main/java/gregtech/common/items/matterManipulator/BlockAnalyzer.java +++ b/src/main/java/gregtech/common/items/matterManipulator/BlockAnalyzer.java @@ -1,27 +1,88 @@ package gregtech.common.items.matterManipulator; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; import javax.annotation.Nullable; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Vec3; import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.FluidStack; -import appeng.api.implementations.tiles.IColorableTile; +import com.google.gson.JsonElement; +import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; +import com.gtnewhorizon.structurelib.alignment.IAlignment; +import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; + +import appeng.api.AEApi; +import appeng.api.implementations.items.IUpgradeModule; +import appeng.api.parts.IPart; +import appeng.api.parts.IPartHost; +import appeng.api.parts.PartItemStack; import appeng.api.util.AEColor; +import appeng.helpers.ICustomNameObject; +import appeng.helpers.IOreFilterable; +import appeng.items.tools.ToolMemoryCard; +import appeng.parts.AEBasePart; +import appeng.parts.automation.UpgradeInventory; +import appeng.parts.p2p.PartP2PTunnel; import appeng.tile.AEBaseTile; import appeng.util.SettingsFrom; +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.common.registry.GameRegistry.UniqueIdentifier; +import gregtech.api.GregTechAPI; import gregtech.api.interfaces.metatileentity.IConnectable; import gregtech.api.interfaces.metatileentity.IMetaTileEntity; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.MTEBasicMachine; +import gregtech.api.util.CoverBehaviorBase; +import gregtech.api.util.GTUtility; +import gregtech.api.util.GTUtility.ItemId; +import gregtech.api.util.ISerializableObject; +import gregtech.api.util.Lazy; +import gregtech.common.covers.CoverInfo; public class BlockAnalyzer { + private static final ToolMemoryCard memoryCard = (ToolMemoryCard) AEApi.instance() + .definitions() + .items() + .memoryCard() + .maybeItem() + .get(); + + private BlockAnalyzer() {} + + public static @Nullable TileAnalysisResult analyze(BlockActionContext context) { + if (context.world.isRemote) { + throw new IllegalStateException("Cannot analyze a block on the client because it needs a fake player."); + } + + TileEntity te = context.getTileEntity(); + + if (te == null) { + return null; + } + + TileAnalysisResult result = new TileAnalysisResult(context.getFakePlayerLazy(), te); + + return result; + } + public static class BlockActionContext { public World world; @@ -29,9 +90,19 @@ public static class BlockActionContext { public EntityPlayer player; public PendingBuild build; public ItemStack manipulator; + public Lazy fakePlayer; public static final double EU_PER_ACTION = 8192; + public Lazy getFakePlayerLazy() { + if (fakePlayer == null) { + fakePlayer = new Lazy<>( + () -> new FakePlayer((WorldServer) player.getEntityWorld(), player.getGameProfile())); + } + + return fakePlayer; + } + public TileEntity getTileEntity() { return world.getTileEntity(x, y, z); } @@ -41,11 +112,20 @@ public boolean tryConsumePower(double mult) { } public boolean tryConsumeItems(ItemStack... items) { - return build.tryConsumeItems(items); + if (build == null) { + for (ItemStack item : items) System.out.println("consume: " + item); + return true; + } else { + return build.tryConsumeItems(items); + } } public void givePlayerItems(ItemStack... items) { - build.givePlayerItems(items); + if (build == null) { + for (ItemStack item : items) System.out.println("give: " + item); + } else { + build.givePlayerItems(items); + } } public void givePlayerFluids(FluidStack... fluids) { @@ -53,187 +133,564 @@ public void givePlayerFluids(FluidStack... fluids) { } } - static interface BlockAction { - - BlockActionResult apply(BlockActionContext context); + private static ForgeDirection nullIfUnknown(ForgeDirection dir) { + return dir == ForgeDirection.UNKNOWN ? null : dir; } - static enum BlockActionResult { - NOT_APPLICABLE, - COULD_NOT_APPLY, - ALREADY_APPLIED, - APPLIED, - } + public static class TileAnalysisResult { - private BlockAnalyzer() {} + public Byte mConnections = null; + public Byte mGTColour = null; + public ForgeDirection mGTFront = null, mGTMainFacing = null; + public Byte mGTBasicIOFlags = null; + public ExtendedFacing mGTFacing = null; + public CoverData[] mCovers = null; + public Byte mStrongRedstone = null; + public String mGTCustomName = null; - public static BlockAction[] getActions(BlockActionContext context) { - TileEntity te = context.getTileEntity(); + public AEColor mAEColour = null; + public ForgeDirection mAEUp = null, mAEForward = null; + public NBTTagCompound mAEConfig = null; + public UniqueIdentifier[] mAEUpgrades = null; + public String mAECustomName = null; + public AEPartData[] mAEParts = null; - if (te == null) { - return null; - } + private static int counter = 0; + public static final byte GT_BASIC_IO_PUSH_ITEMS = (byte) (0b1 << counter++); + public static final byte GT_BASIC_IO_PUSH_FLUIDS = (byte) (0b1 << counter++); + public static final byte GT_BASIC_IO_DISABLE_FILTER = (byte) (0b1 << counter++); + public static final byte GT_BASIC_IO_DISABLE_MULTISTACK = (byte) (0b1 << counter++); + public static final byte GT_BASIC_IO_INPUT_FROM_OUTPUT_SIDE = (byte) (0b1 << counter++); + + private static final ForgeDirection[] ALL_DIRECTIONS = ForgeDirection.values(); - ArrayList actions = new ArrayList<>(); + public TileAnalysisResult(Lazy fakePlayer, TileEntity te) { + if (te instanceof IGregTechTileEntity gte) { + IMetaTileEntity mte = gte.getMetaTileEntity(); - if (te instanceof IGregTechTileEntity gte) { - IMetaTileEntity imte = gte.getMetaTileEntity(); + if (gte.getColorization() != -1) mGTColour = gte.getColorization(); + + if (mte instanceof MTEBasicMachine basicMachine) { + mGTMainFacing = basicMachine.mMainFacing; + + byte flags = 0; + + if (basicMachine.mItemTransfer) flags |= GT_BASIC_IO_PUSH_ITEMS; + if (basicMachine.mFluidTransfer) flags |= GT_BASIC_IO_PUSH_FLUIDS; + if (basicMachine.mDisableFilter) flags |= GT_BASIC_IO_DISABLE_FILTER; + if (basicMachine.mDisableMultiStack) flags |= GT_BASIC_IO_DISABLE_MULTISTACK; + if (basicMachine.mAllowInputFromOutputSide) flags |= GT_BASIC_IO_INPUT_FROM_OUTPUT_SIDE; + + if (flags != 0) mGTBasicIOFlags = flags; + } + + if (mte instanceof IConnectable connectable) { + byte con = 0; + + for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { + if (connectable.isConnectedAtSide(dir)) { + con |= dir.flag; + } + } + + mConnections = con; + } - if (imte instanceof IConnectable conn) { - actions.add(ConnectionAction.fromTile(conn)); + if (mte instanceof IAlignmentProvider provider) { + IAlignment alignment = provider.getAlignment(); + + mGTFacing = alignment != null ? alignment.getExtendedFacing() : null; + } else { + mGTFront = nullIfUnknown(gte.getFrontFacing()); + } + + CoverData[] covers = new CoverData[6]; + boolean hasCover = false; + byte strongRedstone = 0; + + for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { + if (gte.getCoverIDAtSide(dir) != 0) { + covers[dir.ordinal()] = CoverData.fromInfo(gte.getCoverInfoAtSide(dir)); + hasCover = true; + + if (gte.getRedstoneOutputStrength(dir)) { + strongRedstone |= dir.flag; + } + } + } + + if (hasCover) mCovers = covers; + if (strongRedstone != 0) mStrongRedstone = strongRedstone; + + if (mte instanceof ICustomNameObject customName && customName.hasCustomName()) { + mGTCustomName = customName.getCustomName(); + } } - } + if (te instanceof AEBaseTile ae) { + mAEUp = nullIfUnknown(ae.getUp()); + mAEForward = nullIfUnknown(ae.getForward()); + mAEConfig = ae.downloadSettings(SettingsFrom.MEMORY_CARD); + mAECustomName = ae.getCustomName(); + + if (ae instanceof IPartHost partHost) { + mAEParts = new AEPartData[ALL_DIRECTIONS.length]; + + for (ForgeDirection dir : ALL_DIRECTIONS) { + IPart part = partHost.getPart(dir); - if (te instanceof appeng.tile.AEBaseTile ae) { - actions.add(CopyAETileAction.fromTile(ae)); + if (part instanceof AEBasePart basePart) { + mAEParts[dir.ordinal()] = new AEPartData(fakePlayer, basePart); + } + } + } + } } - if (te instanceof IColorableTile colorable) { - actions.add(ApplyAEColourAction.fromTile(colorable)); + public boolean doesAnything() { + return true; } - actions.removeIf(a -> a == null); + @SuppressWarnings("unused") + public boolean apply(BlockActionContext ctx) { + TileEntity te = ctx.getTileEntity(); - return actions.toArray(new BlockAction[actions.size()]); - } + if (te instanceof IGregTechTileEntity gte) { + IMetaTileEntity mte = gte.getMetaTileEntity(); - public static class ConnectionAction implements BlockAction { + if (mGTColour != null) { + gte.setColorization(mGTColour); + } - public final byte mConnections; + if (mte instanceof MTEBasicMachine basicMachine) { + if (mGTMainFacing != null) basicMachine.mMainFacing = mGTMainFacing; - private ConnectionAction(byte connections) { - this.mConnections = connections; - } + mGTMainFacing = basicMachine.mMainFacing; - public static @Nullable ConnectionAction fromTile(IConnectable conn) { - byte connections = 0; + basicMachine.mItemTransfer = (mGTBasicIOFlags & GT_BASIC_IO_PUSH_ITEMS) != 0; + basicMachine.mFluidTransfer = (mGTBasicIOFlags & GT_BASIC_IO_PUSH_FLUIDS) != 0; + basicMachine.mDisableFilter = (mGTBasicIOFlags & GT_BASIC_IO_DISABLE_FILTER) != 0; + basicMachine.mDisableMultiStack = (mGTBasicIOFlags & GT_BASIC_IO_DISABLE_MULTISTACK) != 0; + basicMachine.mAllowInputFromOutputSide = (mGTBasicIOFlags & GT_BASIC_IO_INPUT_FROM_OUTPUT_SIDE) + != 0; + } - for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { - ForgeDirection dir = ForgeDirection.VALID_DIRECTIONS[i]; + if (mte instanceof IConnectable connectable) { + for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { + boolean shouldBeConnected = (mConnections & dir.flag) != 0; + if (connectable.isConnectedAtSide(dir) != shouldBeConnected) { + if (shouldBeConnected) { + connectable.connect(dir); + } else { + connectable.disconnect(dir); + } + } + } + } + + if (mte instanceof IAlignmentProvider provider) { + IAlignment alignment = provider.getAlignment(); - if (conn.isConnectedAtSide(dir)) { - connections |= dir.flag; + if (mGTFacing != null && alignment != null) alignment.setExtendedFacing(mGTFacing); + } else { + if (mGTFront != null) gte.setFrontFacing(mGTFront); } - } - return connections == 0 ? null : new ConnectionAction(connections); - } + for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { + CoverData expected = mCovers == null ? null : mCovers[dir.ordinal()]; + CoverInfo actual = new CoverInfo( + dir, + gte.getCoverIDAtSide(dir), + gte, + gte.getComplexCoverDataAtSide(dir)); + + if (actual == null && expected != null) { + installCover(ctx, gte, dir, expected); + } else if (actual != null && expected == null) { + removeCover(ctx, gte, dir); + } else if (actual != null && expected != null) { + if (actual.getCoverID() != expected.getCoverID()) { + removeCover(ctx, gte, dir); + installCover(ctx, gte, dir, expected); + } else if (!Objects.equals(actual.getCoverData(), expected.getCoverData())) { + updateCover(ctx, gte, dir, expected); + } + } - public BlockActionResult apply(BlockActionContext context) { - var te = context.getTileEntity() instanceof IGregTechTileEntity igte - && igte.getMetaTileEntity() instanceof IConnectable conn ? conn : null; + if (mStrongRedstone != null) { + gte.setRedstoneOutputStrength(dir, (mStrongRedstone & dir.flag) != 0); + } + } - if (te == null) { - return BlockActionResult.NOT_APPLICABLE; + if (mte instanceof ICustomNameObject customName && mGTCustomName != null) { + customName.setCustomName(mGTCustomName); + } } - if (!context.tryConsumePower(1)) { - return BlockActionResult.COULD_NOT_APPLY; - } + if (te instanceof AEBaseTile ae) { + if (mAEUp != null && mAEForward != null) { + ae.setOrientation(mAEForward, mAEUp); + } - boolean didSomething = false; + if (mAEConfig != null) { + ae.uploadSettings(SettingsFrom.MEMORY_CARD, mAEConfig); + } - for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { - ForgeDirection dir = ForgeDirection.VALID_DIRECTIONS[i]; + if (mAECustomName != null) { + ae.setCustomName(mAECustomName); + } - if (te.isConnectedAtSide(dir) != ((mConnections & dir.flag) != 0)) { - didSomething = true; - if ((mConnections & dir.flag) != 0) { - te.connect(dir); - } else { - te.disconnect(dir); + if (ae instanceof IPartHost partHost && mAEParts != null) { + for (ForgeDirection dir : ALL_DIRECTIONS) { + IPart part = partHost.getPart(dir); + AEPartData expected = mAEParts[dir.ordinal()]; + + ItemId actualItem = part == null ? null + : ItemId.createWithoutNBT(part.getItemStack(PartItemStack.Break)); + ItemId expectedItem = expected == null ? null + : ItemId.createWithoutNBT(expected.getPartStack()); + + if ((expectedItem == null || !Objects.equals(actualItem, expectedItem)) && actualItem != null) { + removePart(ctx, partHost, dir); + actualItem = null; + } + + if (actualItem == null && expectedItem != null) { + if (!installPart(ctx, partHost, dir, expected)) { + return false; + } + } + + if (expected != null) { + if (!expected.updatePart(ctx, partHost, dir)) { + return false; + } + } } } } - return didSomething ? BlockActionResult.APPLIED : BlockActionResult.ALREADY_APPLIED; + return true; } - } - public static class CopyAETileAction implements BlockAction { + private void removeCover(BlockActionContext context, IGregTechTileEntity gte, ForgeDirection side) { + if (gte.getCoverIDAtSide(side) != 0) { + context.givePlayerItems(gte.removeCoverAtSide(side, true)); + } + } - public final boolean hasRotation; - public final ForgeDirection forward; - public final ForgeDirection up; - public final NBTTagCompound data; + private void installCover(BlockActionContext context, IGregTechTileEntity gte, ForgeDirection side, + CoverData cover) { + if (gte.getCoverIDAtSide(side) == 0 && gte.canPlaceCoverItemAtSide(side, cover.getCover()) + && context.tryConsumeItems(cover.getCover())) { + gte.setCoverIdAndDataAtSide( + side, + cover.getCoverID(), + cover.getCoverBehaviour() + .allowsCopyPasteTool() ? cover.getCoverData() : null); + } + } - private CopyAETileAction(boolean hasRotation, ForgeDirection forward, ForgeDirection up, NBTTagCompound data) { - this.hasRotation = hasRotation; - this.forward = forward; - this.up = up; - this.data = data; + private void updateCover(BlockActionContext context, IGregTechTileEntity gte, ForgeDirection side, + CoverData target) { + if (gte.getCoverIDAtSide(side) == target.getCoverID() && gte.getCoverBehaviorAtSideNew(side) + .allowsCopyPasteTool()) { + gte.setCoverDataAtSide(side, target.getCoverData()); + } } - public static @Nullable CopyAETileAction fromTile(AEBaseTile sourceTile) { - boolean hasRotation = false; - ForgeDirection forward = null, up = null; + private void removePart(BlockActionContext context, IPartHost partHost, ForgeDirection side) { + IPart part = partHost.getPart(side); - if (sourceTile.canBeRotated()) { - hasRotation = true; - forward = sourceTile.getForward(); - up = sourceTile.getUp(); + if (part == null) return; + + List drops = new ArrayList<>(); + + part.getDrops(drops, false); + + context.givePlayerItems(drops.toArray(new ItemStack[drops.size()])); + + ItemStack partStack = part.getItemStack(PartItemStack.Break); + + NBTTagCompound tag = partStack.getTagCompound(); + + if (tag != null) { + tag.removeTag("display"); + + if (tag.hasNoTags()) { + partStack.setTagCompound(null); + } } - var data = sourceTile.downloadSettings(SettingsFrom.MEMORY_CARD); + context.givePlayerItems(partStack); - return !hasRotation && data == null ? null : new CopyAETileAction(hasRotation, forward, up, data); + partHost.removePart(side, false); } - @Override - public BlockActionResult apply(BlockActionContext context) { - var te = context.getTileEntity() instanceof appeng.tile.AEBaseTile ae ? ae : null; + private boolean installPart(BlockActionContext context, IPartHost partHost, ForgeDirection side, + AEPartData partData) { + ItemStack partStack = partData.getPartStack(); - if (te == null) { - return BlockActionResult.NOT_APPLICABLE; + if (!partHost.canAddPart(partStack, side)) { + return false; } - if (!context.tryConsumePower(((hasRotation && te.canBeRotated()) ? 1 : 0) + (data != null ? 0.5 : 0))) { - return BlockActionResult.COULD_NOT_APPLY; + context.tryConsumeItems(partStack); + + if (partHost.addPart(partStack, side, context.player) == null) { + context.givePlayerItems(partStack); + return false; } - if (hasRotation && te.canBeRotated()) { - te.setOrientation(forward, up); + return true; + } + } + + public static class CoverData { + + public PortableItemStack cover; + public JsonElement coverData; + public Integer tickRateAddition; + + public transient Integer coverID; + public transient CoverBehaviorBase behaviour; + public transient ISerializableObject coverDataObject; + + public CoverData(PortableItemStack cover, NBTBase coverData, int tickRateAddition) { + this.cover = cover; + this.coverData = NBTState.toJsonObject(coverData); + this.tickRateAddition = tickRateAddition == 0 ? null : tickRateAddition; + } + + public ItemStack getCover() { + return cover.toStack(); + } + + public int getCoverID() { + if (coverID == null) { + ItemStack stack = getCover(); + + coverID = (Item.getIdFromItem(stack.getItem()) & 0xFFFF) + | ((Items.feather.getDamage(stack) & 0xFFFF) << 16); } - if (data != null) { - te.uploadSettings(SettingsFrom.MEMORY_CARD, data); + return coverID; + } + + public CoverBehaviorBase getCoverBehaviour() { + if (behaviour == null) { + behaviour = GregTechAPI.getCoverBehaviorNew(getCoverID()); } - return BlockActionResult.APPLIED; + return behaviour; } - } - public static class ApplyAEColourAction implements BlockAction { + public ISerializableObject getCoverData() { + if (coverDataObject == null) { + coverDataObject = getCoverBehaviour().createDataObject(NBTState.toNbt(coverData)); + } + + return coverDataObject; + } + + public static CoverData fromInfo(CoverInfo info) { + if (info.getCoverID() == 0) return null; + + int itemId = info.getCoverID() & 0xFFFF; + int metadata = (info.getCoverID() >> 16) & 0xFFFF; - public final AEColor colour; + Item item = Item.getItemById(itemId); - private ApplyAEColourAction(AEColor colour) { - this.colour = colour; + return new CoverData( + new PortableItemStack(item, metadata), + info.getCoverData() + .saveDataToNBT(), + info.getTickRateAddition()); } + } + + public static class AEPartData { + + public PortableItemStack mPart; + public String mP2PConfigName = null; + public NBTTagCompound mAESettings = null, mP2PSettings = null; + public PortableItemStack[] mAEUpgrades = null; + public String mAEOreDict = null; + public String mAECustomName = null; + + public transient ItemStack partStack; + + public AEPartData(Lazy fakePlayer, AEBasePart part) { + mPart = new PortableItemStack(part.getItemStack(PartItemStack.Break)); + + mAECustomName = part.hasCustomName() ? part.getCustomName() : null; + + if (part instanceof IOreFilterable filterable) { + mAEOreDict = filterable.getFilter(); + } + + if (part instanceof PartP2PTunnel tunnel) { + FakePlayer player = fakePlayer.get(); + + ItemStack cardStack = AEApi.instance() + .definitions() + .items() + .memoryCard() + .maybeStack(1) + .get(); + player.inventory.mainInventory[0] = cardStack; + + tunnel.onPartShiftActivate(player, Vec3.createVectorHelper(0, 0, 0)); - public static @Nullable ApplyAEColourAction fromTile(IColorableTile colorable) { - return new ApplyAEColourAction(colorable.getColor()); + player.inventory.mainInventory[0] = null; + + mP2PSettings = memoryCard.getData(cardStack); + } else { + if (part.getConfigManager() != null && mAESettings != null) { + part.getConfigManager() + .writeToNBT(mAESettings); + } + + IInventory upgrades = part.getInventoryByName("upgrades"); + + if (upgrades != null) { + mAEUpgrades = GTUtility.streamInventory(upgrades) + .filter(x -> x != null) + .map(PortableItemStack::new) + .toArray(PortableItemStack[]::new); + } + } } - @Override - public BlockActionResult apply(BlockActionContext context) { - var te = context.getTileEntity() instanceof IColorableTile colorable ? colorable : null; + public boolean updatePart(BlockActionContext context, IPartHost partHost, ForgeDirection side) { + if (partHost.getPart(side) instanceof AEBasePart part) { + if (mAECustomName != null) part.setCustomName(mAECustomName); + + if (part instanceof PartP2PTunnel tunnel) { + if (mP2PSettings != null) { + FakePlayer player = context.getFakePlayerLazy() + .get(); + + ItemStack cardStack = AEApi.instance() + .definitions() + .items() + .memoryCard() + .maybeStack(1) + .get(); + + memoryCard.setMemoryCardContents(cardStack, mAECustomName, mP2PSettings); + player.inventory.mainInventory[0] = cardStack; - if (te == null) { - return BlockActionResult.NOT_APPLICABLE; + tunnel.onPartActivate(player, Vec3.createVectorHelper(0, 0, 0)); + + player.inventory.mainInventory[0] = null; + } + } else { + UpgradeInventory upgradeInv = (UpgradeInventory) part.getInventoryByName("upgrades"); + + if (upgradeInv != null) { + ItemStackMap targetMap = GTUtility.getItemStackHistogram( + Arrays.stream(mAEUpgrades) + .map(PortableItemStack::toStack) + .toArray(ItemStack[]::new)); + ItemStackMap actualMap = GTUtility.getItemStackHistogram( + GTUtility.streamInventory(upgradeInv) + .filter(x -> x != null) + .toArray(ItemStack[]::new)); + + if (!targetMap.equals(actualMap)) { + emptyInventory(context, upgradeInv); + + targetMap.replaceAll((item, amount) -> { + if (item.getItem() instanceof IUpgradeModule upgrade) { + int max = upgradeInv.getMaxInstalled(upgrade.getType(item)); + + return Math.min(max, amount); + } else { + return 0l; + } + }); + + List upgradeList = GTUtility.getStacksOfSize(targetMap, 1); + + ItemStack[] upgrades = upgradeList + .subList(0, Math.min(upgradeList.size(), upgradeInv.getSizeInventory())) + .toArray(new ItemStack[0]); + + if (context.tryConsumeItems(upgrades)) { + for (int i = 0; i < upgrades.length; i++) { + upgradeInv.setInventorySlotContents(i, upgrades[i]); + } + } + } + } + + if (part.getConfigManager() != null && mAESettings != null) { + part.getConfigManager() + .readFromNBT(mAESettings); + } + + if (part instanceof IOreFilterable filterable) { + filterable.setFilter(mAEOreDict); + } + } } - if (!context.tryConsumePower(te.getColor() != colour ? 1 : 0)) { - return BlockActionResult.COULD_NOT_APPLY; + return true; + } + + public ItemStack getPartStack() { + return mPart.toStack(); + } + } + + private static void emptyInventory(BlockActionContext context, IInventory inv) { + int size = inv.getSizeInventory(); + + for (int i = 0; i < size; i++) { + ItemStack stack = inv.getStackInSlot(i); + + if (stack != null && stack.getItem() != null) { + inv.setInventorySlotContents(i, null); + + context.givePlayerItems(stack); } + } - if (te.getColor() != colour) { - te.recolourBlock(ForgeDirection.UNKNOWN, colour, context.player); - return BlockActionResult.APPLIED; - } else { - return BlockActionResult.ALREADY_APPLIED; + inv.markDirty(); + } + + public static class PortableItemStack { + + public UniqueIdentifier item; + public Integer metadata, amount; + + public transient ItemStack itemStack; + + public PortableItemStack() {} + + public PortableItemStack(Item item, int metadata) { + this.item = GameRegistry.findUniqueIdentifierFor(item); + this.metadata = metadata == 0 ? null : metadata; + this.amount = null; + } + + public PortableItemStack(ItemStack stack) { + item = GameRegistry.findUniqueIdentifierFor(stack.getItem()); + metadata = Items.feather.getDamage(stack); + if (metadata == 0) metadata = null; + amount = stack.stackSize == 1 ? null : stack.stackSize; + } + + public ItemStack toStack() { + if (itemStack == null) { + itemStack = new ItemStack( + GameRegistry.findItem(item.modId, item.name), + amount == null ? 1 : amount, + metadata); } + + return itemStack; } } } diff --git a/src/main/java/gregtech/common/items/matterManipulator/ItemMatterManipulator.java b/src/main/java/gregtech/common/items/matterManipulator/ItemMatterManipulator.java index 012a82f8d30..b6c6c4929a0 100644 --- a/src/main/java/gregtech/common/items/matterManipulator/ItemMatterManipulator.java +++ b/src/main/java/gregtech/common/items/matterManipulator/ItemMatterManipulator.java @@ -45,6 +45,7 @@ import org.joml.Vector3i; import org.lwjgl.opengl.GL11; import org.spongepowered.libraries.com.google.common.collect.MapMaker; +import org.spongepowered.libraries.com.google.gson.Gson; import com.gtnewhorizon.gtnhlib.util.AboveHotbarHUD; import com.gtnewhorizon.structurelib.StructureLibAPI; @@ -61,10 +62,14 @@ import cpw.mods.fml.common.registry.GameRegistry; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import gregtech.GTMod; import gregtech.api.enums.GTValues; import gregtech.api.interfaces.INetworkUpdatableItem; import gregtech.api.net.GTPacketUpdateItem; import gregtech.api.util.GTLanguageManager; +import gregtech.api.util.GTUtility; +import gregtech.common.items.matterManipulator.BlockAnalyzer.BlockActionContext; +import gregtech.common.items.matterManipulator.BlockAnalyzer.TileAnalysisResult; import gregtech.common.items.matterManipulator.NBTState.BlockRemoveMode; import gregtech.common.items.matterManipulator.NBTState.BlockSelectMode; import gregtech.common.items.matterManipulator.NBTState.Config; @@ -277,6 +282,8 @@ private void addInfoLine(List desc, String format, T value, Function } } + private TileAnalysisResult result; + @Override public ItemStack onItemRightClick(ItemStack itemStack, World world, EntityPlayer player) { if (player.getItemInUse() != null && player.getItemInUse() @@ -309,6 +316,43 @@ public ItemStack onItemRightClick(ItemStack itemStack, World world, EntityPlayer location.offset(ForgeDirection.getOrientation(hit.sideHit)); } + if (state.config.placeMode == PlaceMode.DEBUGGING) { + if (!world.isRemote) { + BlockActionContext bac = new BlockActionContext(); + bac.world = world; + bac.x = location.x; + bac.y = location.y; + bac.z = location.z; + bac.player = player; + bac.build = null; + bac.manipulator = itemStack; + + if (result == null) { + try { + result = BlockAnalyzer.analyze(bac); + + GTUtility.sendChatToPlayer(player, "analysis: " + new Gson().toJson(result)); + } catch (Throwable t) { + GTMod.GT_FML_LOGGER.error("analysis error", t); + + GTUtility.sendChatToPlayer(player, "analysis error: " + t.getMessage()); + } + } else { + try { + GTUtility.sendChatToPlayer(player, "apply: " + result.apply(bac)); + + result = null; + } catch (Throwable t) { + GTMod.GT_FML_LOGGER.error("apply error", t); + + GTUtility.sendChatToPlayer(player, "apply error: " + t.getMessage()); + } + } + } + + return itemStack; + } + switch (state.config.coordMode) { case SET_A: { state.config.coordA = location; @@ -664,7 +708,6 @@ private void addCommonOptions(RadialMenuBuilder builder, UIBuildContext buildCon .done() .option() .label("Debugging") - .hidden(true) .onClicked(() -> { withState(buildContext, state -> { state.config.placeMode = PlaceMode.DEBUGGING; diff --git a/src/main/java/gregtech/common/items/matterManipulator/PendingBuild.java b/src/main/java/gregtech/common/items/matterManipulator/PendingBuild.java index 7e2f584f295..53f70349c37 100644 --- a/src/main/java/gregtech/common/items/matterManipulator/PendingBuild.java +++ b/src/main/java/gregtech/common/items/matterManipulator/PendingBuild.java @@ -12,8 +12,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; import net.minecraft.block.Block; import net.minecraft.entity.item.EntityItem; @@ -35,6 +33,8 @@ import net.minecraftforge.fluids.IFluidContainerItem; import net.minecraftforge.fluids.IFluidHandler; +import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; + import appeng.api.config.Actionable; import appeng.api.implementations.tiles.ISegmentedInventory; import appeng.api.networking.security.PlayerSource; @@ -294,19 +294,23 @@ public boolean tryConsumeItems(ItemStack... items) { if (placingPlayer.capabilities.isCreativeMode) { return true; } else { - if (consumeItemsFromPlayer(items, true)) { - consumeItemsFromPlayer(items, false); + ItemStackMap itemMap = GTUtility.getItemStackHistogram(items); - return true; - } + consumeItemsFromPending(itemMap, true); + consumeItemsFromPlayer(itemMap, true); + consumeItemsFromAE(itemMap, true); - if (consumeItemsFromAE(items, true)) { - consumeItemsFromAE(items, false); + if (itemMap.values() + .stream() + .anyMatch(a -> a > 0)) return false; - return true; - } + itemMap = GTUtility.getItemStackHistogram(items); - return false; + consumeItemsFromPending(itemMap, false); + consumeItemsFromPlayer(itemMap, false); + consumeItemsFromAE(itemMap, false); + + return true; } } @@ -421,7 +425,7 @@ public void actuallyGivePlayerStuff() { .getFluidStack(amount > Integer.MAX_VALUE ? Integer.MAX_VALUE : amount.intValue()); // spotless:off - ItemStack idealCell = inventoryStream(placingPlayer.inventory) + ItemStack idealCell = GTUtility.streamInventory(placingPlayer.inventory) .sorted(Comparator.comparingInt((ItemStack x) -> ( x != null && x.getItem() instanceof IFluidContainerItem container ? container.getCapacity(x) : 0 ))) @@ -445,7 +449,7 @@ public void actuallyGivePlayerStuff() { fluid.amount = amount > Integer.MAX_VALUE ? Integer.MAX_VALUE : amount.intValue(); // spotless:off - List validCells = inventoryStream(placingPlayer.inventory) + List validCells = GTUtility.streamInventory(placingPlayer.inventory) .filter(x -> ( x != null && x.getItem() instanceof IFluidContainerItem container && @@ -476,78 +480,87 @@ public void actuallyGivePlayerStuff() { pendingFluids.clear(); } - private Stream inventoryStream(IInventory inv) { - return IntStream.range(0, inv.getSizeInventory()) - .mapToObj(inv::getStackInSlot); - } + private void consumeItemsFromPending(ItemStackMap requestedItems, boolean simulate) { + requestedItems.replaceAll((item, amount) -> { + if (amount == 0) return 0l; - private boolean consumeItemsFromPlayer(ItemStack[] items, boolean simulate) { - for (int i = 0; i < items.length; i++) { - items[i] = items[i].copy(); - } + long available = pendingItems.get(ItemId.create(item)); + + long toRemove = Math.min(available, amount); + available -= toRemove; + amount -= toRemove; + + if (!simulate) { + if (available == 0) { + pendingItems.remove(ItemId.create(item)); + } else { + pendingItems.put(ItemId.create(item), available); + } + } + + return amount; + }); + } + + private void consumeItemsFromPlayer(ItemStackMap requestedItems, boolean simulate) { ItemStack[] inv = placingPlayer.inventory.mainInventory; - if (simulate) { - inv = GTUtility.copyItemArray(inv); - } + requestedItems.replaceAll((item, amountLong) -> { + if (amountLong == 0) return 0l; + + int remaining = amountLong > Integer.MAX_VALUE ? Integer.MAX_VALUE : amountLong.intValue(); + int initial = remaining; - for (ItemStack item : items) { for (int i = 0; i < inv.length; i++) { ItemStack slot = inv[i]; if (slot != null && areStacksBasicallyEqual(item, slot)) { - int toRemove = Math.min(slot.stackSize, item.stackSize); + int toRemove = Math.min(slot.stackSize, remaining); - slot.stackSize -= toRemove; - item.stackSize -= toRemove; + remaining -= toRemove; - if (slot.stackSize == 0) { - inv[i] = null; + if (!simulate) { + slot.stackSize -= toRemove; + if (slot.stackSize == 0) { + inv[i] = null; + } } } - - if (item.stackSize == 0) { - break; - } } - if (item.stackSize > 0) { - return false; - } - } - - return true; + int provided = initial - remaining; + return amountLong - provided; + }); } - private boolean consumeItemsFromAE(ItemStack[] items, boolean simulate) { + private void consumeItemsFromAE(ItemStackMap requestedItems, boolean simulate) { if (manipulator.encKey == null) { - return false; + return; } if (!manipulator.hasMEConnection()) { if (!manipulator.connectToMESystem()) { - return false; + return; } } if (!manipulator.canInteractWithAE(placingPlayer)) { - return false; + return; } - for (ItemStack item : items) { + requestedItems.replaceAll((item, amount) -> { + if (amount == 0) return 0l; + IAEItemStack result = manipulator.storageGrid.getItemInventory() .extractItems( - AEItemStack.create(item), + Objects.requireNonNull(AEItemStack.create(item)) + .setStackSize(amount), simulate ? Actionable.SIMULATE : Actionable.MODULATE, new PlayerSource(placingPlayer, manipulator.securityTerminal)); - if (result == null || result.getStackSize() == 0) { - return false; - } - } - - return true; + return result == null ? amount : (amount - result.getStackSize()); + }); } private static boolean areBlocksBasicallyEqual(PendingBlock a, PendingBlock b) { @@ -613,8 +626,7 @@ private void removeBlock(World world, int x, int y, int z, Block existing, int e if (existing instanceof IFluidBlock fluidBlock && fluidBlock.canDrain(world, x, y, z)) { givePlayerFluids(fluidBlock.drain(world, x, y, z, true)); - } - if (existing == Blocks.water || existing == Blocks.lava) { + } else if (existing == Blocks.water || existing == Blocks.lava) { givePlayerFluids(new FluidStack(existing == Blocks.water ? FluidRegistry.WATER : FluidRegistry.LAVA, 1000)); } else { givePlayerItems( @@ -736,7 +748,7 @@ private void resetAEMachine(Object machine) { private void resetKeptSettings(TileEntity te) { if (te instanceof IRedstoneEmitter emitter) { for (ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { - emitter.setWeakOutputRedstoneSignal(side, (byte) 0); + emitter.setRedstoneOutputStrength(side, false); } } } diff --git a/src/main/java/gtPlusPlus/core/tileentities/base/TileEntityBase.java b/src/main/java/gtPlusPlus/core/tileentities/base/TileEntityBase.java index 42b24891940..d09914dce1e 100644 --- a/src/main/java/gtPlusPlus/core/tileentities/base/TileEntityBase.java +++ b/src/main/java/gtPlusPlus/core/tileentities/base/TileEntityBase.java @@ -1105,9 +1105,14 @@ public void setStrongOutputRedstoneSignal(ForgeDirection side, byte aStrength) { } @Override - public void setWeakOutputRedstoneSignal(ForgeDirection side, byte aStrength) { + public void setRedstoneOutputStrength(ForgeDirection side, boolean isStrong) { mStrongRedstone &= ~side.flag; - setOutputRedstoneSignal(side, aStrength); + setOutputRedstoneSignal(side, mSidedRedstone[side.ordinal()]); + } + + @Override + public boolean getRedstoneOutputStrength(ForgeDirection side) { + return (mStrongRedstone & side.flag) != 0; } @Override