diff --git a/build.gradle.kts b/build.gradle.kts index 3cd6083bf1..0ae4e3dea6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,7 +45,7 @@ subprojects { encoding("UTF-8") java { - cleanthat() + cleanthat().version("2.8") eclipse().configFile("$rootDir/config/codeformat/codeformat.xml") diff --git a/endercore/src/main/java/com/enderio/core/client/gui/screen/EnderContainerScreen.java b/endercore/src/main/java/com/enderio/core/client/gui/screen/EnderContainerScreen.java index 69add7d27f..1df1fc268d 100644 --- a/endercore/src/main/java/com/enderio/core/client/gui/screen/EnderContainerScreen.java +++ b/endercore/src/main/java/com/enderio/core/client/gui/screen/EnderContainerScreen.java @@ -55,35 +55,35 @@ protected void centerAlignTitleLabelX() { } @Override - public void render(GuiGraphics pGuiGraphics, int pMouseX, int pMouseY, float pPartialTick) { + public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { if (menu instanceof LegacyBaseBlockEntityMenu baseBlockEntityMenu && baseBlockEntityMenu.getBlockEntity() == null) { return; } - super.render(pGuiGraphics, pMouseX, pMouseY, pPartialTick); + super.render(guiGraphics, mouseX, mouseY, partialTick); } @Override - protected void renderLabels(GuiGraphics pGuiGraphics, int pMouseX, int pMouseY) { + protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) { if (shouldRenderLabels) { - super.renderLabels(pGuiGraphics, pMouseX, pMouseY); + super.renderLabels(guiGraphics, mouseX, mouseY); } // Move back to screen space rather than aligned to the background coordinates - pGuiGraphics.pose().pushPose(); - pGuiGraphics.pose().translate(-leftPos, -topPos, 0.0D); + guiGraphics.pose().pushPose(); + guiGraphics.pose().translate(-leftPos, -topPos, 0.0D); int zOffset = 200; for (var layer : overlayRenderables.keySet()) { // Offset deeper for each layer. - pGuiGraphics.pose().pushPose(); + guiGraphics.pose().pushPose(); zOffset += 150; - pGuiGraphics.pose().translate(0.0D, 0.0D, zOffset); + guiGraphics.pose().translate(0.0D, 0.0D, zOffset); for (var overlay : overlayRenderables.get(layer)) { if (!(overlay instanceof AbstractWidget widget) || widget.isActive()) { - overlay.render(pGuiGraphics, pMouseX, pMouseY, + overlay.render(guiGraphics, mouseX, mouseY, Minecraft.getInstance().getTimer().getGameTimeDeltaPartialTick(false)); if (overlay instanceof BaseOverlay baseOverlay) { @@ -92,19 +92,19 @@ protected void renderLabels(GuiGraphics pGuiGraphics, int pMouseX, int pMouseY) } } - pGuiGraphics.pose().popPose(); + guiGraphics.pose().popPose(); } - pGuiGraphics.pose().popPose(); + guiGraphics.pose().popPose(); - pGuiGraphics.pose().translate(0, 0, zOffset); + guiGraphics.pose().translate(0, 0, zOffset); - pGuiGraphics.pose().pushPose(); - pGuiGraphics.pose().translate(-leftPos, -topPos, 0.0D); + guiGraphics.pose().pushPose(); + guiGraphics.pose().translate(-leftPos, -topPos, 0.0D); - renderTooltip(pGuiGraphics, pMouseX, pMouseY); + renderTooltip(guiGraphics, mouseX, mouseY); - pGuiGraphics.pose().popPose(); + guiGraphics.pose().popPose(); } @Override diff --git a/endercore/src/main/java/com/enderio/core/common/blockentity/EnderBlockEntity.java b/endercore/src/main/java/com/enderio/core/common/blockentity/EnderBlockEntity.java index 878beececb..1d28fe47d2 100644 --- a/endercore/src/main/java/com/enderio/core/common/blockentity/EnderBlockEntity.java +++ b/endercore/src/main/java/com/enderio/core/common/blockentity/EnderBlockEntity.java @@ -93,10 +93,11 @@ public void endTick() { } } - @Override - public void setChanged() { - this.isChangedDeferred = true; - } + // TODO: I think we might be able to kill this optimisation now? +// @Override +// public void setChanged() { +// this.isChangedDeferred = true; +// } // endregion diff --git a/endercore/src/main/java/com/enderio/core/common/menu/BaseBlockEntityMenu.java b/endercore/src/main/java/com/enderio/core/common/menu/BaseBlockEntityMenu.java index ca49f4d3e7..177b0a6d6a 100644 --- a/endercore/src/main/java/com/enderio/core/common/menu/BaseBlockEntityMenu.java +++ b/endercore/src/main/java/com/enderio/core/common/menu/BaseBlockEntityMenu.java @@ -40,7 +40,7 @@ public T getBlockEntity() { } @Override - public boolean stillValid(Player pPlayer) { - return Container.stillValidBlockEntity(getBlockEntity(), pPlayer); + public boolean stillValid(Player player) { + return Container.stillValidBlockEntity(getBlockEntity(), player); } } diff --git a/enderio-base/src/generated/resources/assets/enderio/lang/en_us.json b/enderio-base/src/generated/resources/assets/enderio/lang/en_us.json index 602309f146..69a6e1f469 100644 --- a/enderio-base/src/generated/resources/assets/enderio/lang/en_us.json +++ b/enderio-base/src/generated/resources/assets/enderio/lang/en_us.json @@ -238,16 +238,12 @@ "gui.enderio.collision.mobs_pass": "Not solid to monsters", "gui.enderio.collision.players_block": "Only solid to players", "gui.enderio.collision.players_pass": "Not solid to players", - "gui.enderio.conduit_channel": "Conduit-Channel", "gui.enderio.confirm": "Confirm", "gui.enderio.filter": "Filter", "gui.enderio.filter.blacklist": "BlackList", "gui.enderio.filter.nbt": "Match NBT", "gui.enderio.filter.nonbt": "Ignore NBT", "gui.enderio.filter.whitelist": "Whitelist", - "gui.enderio.fluid_conduit.change_fluid1": "Locked Fluid:", - "gui.enderio.fluid_conduit.change_fluid2": "Click to reset!", - "gui.enderio.fluid_conduit.change_fluid3": "Fluid: %s", "gui.enderio.ioconfig": "IO Configuration", "gui.enderio.ioconfig.both": "Push / Pull", "gui.enderio.ioconfig.disabled": "Disabled", @@ -265,13 +261,24 @@ "gui.enderio.redstone.active_with_signal": "Active with Signal", "gui.enderio.redstone.active_without_signal": "Active without Signal", "gui.enderio.redstone.always_active": "Always Active", + "gui.enderio.redstone.black": "Black", + "gui.enderio.redstone.blue": "Blue", + "gui.enderio.redstone.brown": "Brown", + "gui.enderio.redstone.cyan": "Cyan", + "gui.enderio.redstone.gray": "Gray", + "gui.enderio.redstone.green": "Green", + "gui.enderio.redstone.light_blue": "Light Blue", + "gui.enderio.redstone.light_gray": "Light Gray", + "gui.enderio.redstone.lime": "Lime", + "gui.enderio.redstone.magenta": "Magenta", "gui.enderio.redstone.mode": "Redstone Mode", "gui.enderio.redstone.never_active": "Never Active", - "gui.enderio.redstone_channel": "Redstone-Channel", - "gui.enderio.round_robin.disabled": "Round Robin Disabled", - "gui.enderio.round_robin.enabled": "Round Robin Enabled", - "gui.enderio.self_feed.disabled": "Self Feed Disabled", - "gui.enderio.self_feed.enabled": "Self Feed Enabled", + "gui.enderio.redstone.orange": "Orange", + "gui.enderio.redstone.pink": "Pink", + "gui.enderio.redstone.purple": "Purple", + "gui.enderio.redstone.red": "Red", + "gui.enderio.redstone.white": "White", + "gui.enderio.redstone.yellow": "Yellow", "gui.enderio.visible.false": "Hidden", "gui.enderio.visible.true": "Visible", "guidebook.enderio.book_title": "Book Title", diff --git a/enderio-base/src/main/java/com/enderio/base/api/misc/RedstoneControl.java b/enderio-base/src/main/java/com/enderio/base/api/misc/RedstoneControl.java index a73fde85ee..5dcd4ce81c 100644 --- a/enderio-base/src/main/java/com/enderio/base/api/misc/RedstoneControl.java +++ b/enderio-base/src/main/java/com/enderio/base/api/misc/RedstoneControl.java @@ -14,8 +14,10 @@ public enum RedstoneControl implements StringRepresentable { - ALWAYS_ACTIVE(0, "always_active", bool -> true), ACTIVE_WITH_SIGNAL(1, "active_with_signal", bool -> bool), - ACTIVE_WITHOUT_SIGNAL(2, "active_without_signal", bool -> !bool), NEVER_ACTIVE(3, "never_active", bool -> false); + ALWAYS_ACTIVE(0, "always_active", bool -> true, false), + ACTIVE_WITH_SIGNAL(1, "active_with_signal", bool -> bool, true), + ACTIVE_WITHOUT_SIGNAL(2, "active_without_signal", bool -> !bool, true), + NEVER_ACTIVE(3, "never_active", bool -> false, false); public static final Codec CODEC = StringRepresentable.fromEnum(RedstoneControl::values); public static final IntFunction BY_ID = ByIdMap.continuous(key -> key.id, values(), @@ -25,17 +27,23 @@ public enum RedstoneControl implements StringRepresentable { private final int id; private final String name; private final UnaryOperator isActive; + private final boolean isRedstoneSensitive; - RedstoneControl(int id, String name, UnaryOperator isActive) { + RedstoneControl(int id, String name, UnaryOperator isActive, boolean isRedstoneSensitive) { this.id = id; this.name = name; this.isActive = isActive; + this.isRedstoneSensitive = isRedstoneSensitive; } public boolean isActive(boolean hasRedstone) { return isActive.apply(hasRedstone); } + public boolean isRedstoneSensitive() { + return isRedstoneSensitive; + } + @Override public String getSerializedName() { return name; diff --git a/enderio-base/src/main/java/com/enderio/base/client/gui/widget/DyeColorPickerWidget.java b/enderio-base/src/main/java/com/enderio/base/client/gui/widget/DyeColorPickerWidget.java index c5c9a0c075..9389ba5c38 100644 --- a/enderio-base/src/main/java/com/enderio/base/client/gui/widget/DyeColorPickerWidget.java +++ b/enderio-base/src/main/java/com/enderio/base/client/gui/widget/DyeColorPickerWidget.java @@ -1,45 +1,32 @@ package com.enderio.base.client.gui.widget; import com.enderio.base.client.gui.icon.EIOEnumIcons; +import com.enderio.base.common.lang.EIOEnumLang; import com.enderio.core.client.gui.widgets.BaseEnumPickerWidget; +import java.util.function.Consumer; +import java.util.function.Supplier; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.DyeColor; import org.jetbrains.annotations.Nullable; -import java.util.function.Consumer; -import java.util.function.Supplier; - public class DyeColorPickerWidget extends BaseEnumPickerWidget { // Reproduces the old ColorControl order for player familiarity. - private static final DyeColor[] ORDERED_VALUES = new DyeColor[] { - DyeColor.GREEN, - DyeColor.BROWN, - DyeColor.BLUE, - DyeColor.PURPLE, - DyeColor.CYAN, - DyeColor.LIGHT_GRAY, - DyeColor.GRAY, - DyeColor.PINK, - DyeColor.LIME, - DyeColor.YELLOW, - DyeColor.LIGHT_BLUE, - DyeColor.MAGENTA, - DyeColor.ORANGE, - DyeColor.WHITE, - DyeColor.BLACK, - DyeColor.RED, - }; + private static final DyeColor[] ORDERED_VALUES = new DyeColor[] { DyeColor.GREEN, DyeColor.BROWN, DyeColor.BLUE, + DyeColor.PURPLE, DyeColor.CYAN, DyeColor.LIGHT_GRAY, DyeColor.GRAY, DyeColor.PINK, DyeColor.LIME, + DyeColor.YELLOW, DyeColor.LIGHT_BLUE, DyeColor.MAGENTA, DyeColor.ORANGE, DyeColor.WHITE, DyeColor.BLACK, + DyeColor.RED, }; - public DyeColorPickerWidget(int pX, int pY, Supplier getter, Consumer setter, Component optionName) { + public DyeColorPickerWidget(int pX, int pY, Supplier getter, Consumer setter, + Component optionName) { super(pX, pY, 16, 16, DyeColor.class, getter, setter, optionName); } @Override @Nullable public Component getValueTooltip(DyeColor value) { - return null; + return EIOEnumLang.DYE_COLOR.get(value); } @Override diff --git a/enderio-base/src/main/java/com/enderio/base/common/blockentity/Wrenchable.java b/enderio-base/src/main/java/com/enderio/base/common/blockentity/Wrenchable.java index 5265905d9d..e490d04517 100644 --- a/enderio-base/src/main/java/com/enderio/base/common/blockentity/Wrenchable.java +++ b/enderio-base/src/main/java/com/enderio/base/common/blockentity/Wrenchable.java @@ -1,11 +1,7 @@ package com.enderio.base.common.blockentity; -import com.enderio.base.api.UseOnly; -import net.minecraft.core.Direction; import net.minecraft.world.ItemInteractionResult; -import net.minecraft.world.entity.player.Player; -import net.neoforged.fml.LogicalSide; -import org.jetbrains.annotations.Nullable; +import net.minecraft.world.item.context.UseOnContext; // TODO: Move to API. @@ -13,6 +9,5 @@ * An interface that block entities may implement in order to implement special behaviours(other than to rotate the block) when right-clicked with the Yeta wrench. */ public interface Wrenchable { - @UseOnly(LogicalSide.SERVER) - ItemInteractionResult onWrenched(@Nullable Player player, @Nullable Direction side); + ItemInteractionResult onWrenched(UseOnContext context); } diff --git a/enderio-base/src/main/java/com/enderio/base/common/item/tool/YetaWrenchItem.java b/enderio-base/src/main/java/com/enderio/base/common/item/tool/YetaWrenchItem.java index cab46ece92..00a1c7d9ad 100644 --- a/enderio-base/src/main/java/com/enderio/base/common/item/tool/YetaWrenchItem.java +++ b/enderio-base/src/main/java/com/enderio/base/common/item/tool/YetaWrenchItem.java @@ -1,7 +1,6 @@ package com.enderio.base.common.item.tool; import com.enderio.base.api.capability.SideConfig; -import com.enderio.base.common.blockentity.Wrenchable; import com.enderio.base.common.init.EIOCapabilities; import com.mojang.datafixers.util.Either; import java.util.Optional; @@ -29,10 +28,6 @@ public InteractionResult onItemUseFirst(ItemStack stack, UseOnContext pContext) Level level = pContext.getLevel(); BlockPos pos = pContext.getClickedPos(); - if (!level.isClientSide && level.getBlockEntity(pos) instanceof Wrenchable wrenchable) { - return wrenchable.onWrenched(pContext.getPlayer(), pContext.getClickedFace()).result(); - } - // Check for side config capability SideConfig sideConfig = level.getCapability(EIOCapabilities.SideConfig.BLOCK, pos, pContext.getClickedFace()); if (sideConfig != null) { diff --git a/enderio-base/src/main/java/com/enderio/base/common/lang/EIOEnumLang.java b/enderio-base/src/main/java/com/enderio/base/common/lang/EIOEnumLang.java index 4d28e6f5e1..045f343d00 100644 --- a/enderio-base/src/main/java/com/enderio/base/common/lang/EIOEnumLang.java +++ b/enderio-base/src/main/java/com/enderio/base/common/lang/EIOEnumLang.java @@ -8,6 +8,7 @@ import com.enderio.regilite.Regilite; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.DyeColor; public class EIOEnumLang { @@ -20,6 +21,25 @@ public class EIOEnumLang { .addTranslation(RedstoneControl.NEVER_ACTIVE, "Never Active") .build(); + public static final EnumTranslationMap DYE_COLOR = builder(DyeColor.class, "redstone") + .addTranslation(DyeColor.WHITE, "White") + .addTranslation(DyeColor.ORANGE, "Orange") + .addTranslation(DyeColor.MAGENTA, "Magenta") + .addTranslation(DyeColor.LIGHT_BLUE, "Light Blue") + .addTranslation(DyeColor.YELLOW, "Yellow") + .addTranslation(DyeColor.LIME, "Lime") + .addTranslation(DyeColor.PINK, "Pink") + .addTranslation(DyeColor.GRAY, "Gray") + .addTranslation(DyeColor.LIGHT_GRAY, "Light Gray") + .addTranslation(DyeColor.CYAN, "Cyan") + .addTranslation(DyeColor.PURPLE, "Purple") + .addTranslation(DyeColor.BLUE, "Blue") + .addTranslation(DyeColor.BROWN, "Brown") + .addTranslation(DyeColor.GREEN, "Green") + .addTranslation(DyeColor.RED, "Red") + .addTranslation(DyeColor.BLACK, "Black") + .build(); + public static final EnumTranslationMap GLASS_COLLISION = builder( GlassCollisionPredicate.class, "collision") .addTranslation(GlassCollisionPredicate.PLAYERS_PASS, "Not solid to players") diff --git a/enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java b/enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java index 38daedb175..89b1e1f835 100644 --- a/enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java +++ b/enderio-base/src/main/java/com/enderio/base/common/lang/EIOLang.java @@ -34,19 +34,19 @@ public class EIOLang { // region Items - public static final Component DARK_STEEL_LADDER_FASTER = TooltipUtil.style( - addTranslation("tooltip", EnderIO.loc("dark_steel_ladder.faster"), "Faster than regular ladders")); + public static final Component DARK_STEEL_LADDER_FASTER = TooltipUtil + .style(addTranslation("tooltip", EnderIO.loc("dark_steel_ladder.faster"), "Faster than regular ladders")); public static final Component SOUL_VIAL_ERROR_PLAYER = addTranslation("message", EnderIO.loc("soul_vial.error_player"), "You cannot put player in a bottle!"); - public static final Component SOUL_VIAL_ERROR_BOSS = addTranslation("message", - EnderIO.loc("soul_vial.error_boss"), "Nice try. Bosses don't like bottles."); + public static final Component SOUL_VIAL_ERROR_BOSS = addTranslation("message", EnderIO.loc("soul_vial.error_boss"), + "Nice try. Bosses don't like bottles."); public static final Component SOUL_VIAL_ERROR_BLACKLISTED = addTranslation("message", EnderIO.loc("soul_vial.error_blacklisted"), "This entity has been blacklisted."); public static final Component SOUL_VIAL_ERROR_FAILED = addTranslation("message", EnderIO.loc("soul_vial.error_failed"), "This entity cannot be captured."); - public static final Component SOUL_VIAL_ERROR_DEAD = addTranslation("message", - EnderIO.loc("soul_vial.error_dead"), "Cannot capture a dead mob!"); + public static final Component SOUL_VIAL_ERROR_DEAD = addTranslation("message", EnderIO.loc("soul_vial.error_dead"), + "Cannot capture a dead mob!"); public static final MutableComponent SOUL_VIAL_TOOLTIP_HEALTH = addTranslation("tooltip", EnderIO.loc("soul_vial.health"), "Health: %s/%s"); @@ -57,26 +57,7 @@ public class EIOLang { public static final Component TOO_MANY_LEVELS = addTranslation("info", EnderIO.loc("too_many_levels"), "You have more than 21862 levels, that's too much XP."); - public static final Component CONDUIT_CHANNEL = addTranslation("gui", EnderIO.loc("conduit_channel"), - "Conduit-Channel"); - public static final Component REDSTONE_CHANNEL = addTranslation("gui", EnderIO.loc("redstone_channel"), - "Redstone-Channel"); - public static final Component REDSTONE_MODE = addTranslation("gui", EnderIO.loc("redstone.mode"), - "Redstone Mode"); - public static final Component ROUND_ROBIN_ENABLED = addTranslation("gui", EnderIO.loc("round_robin.enabled"), - "Round Robin Enabled"); - public static final Component ROUND_ROBIN_DISABLED = addTranslation("gui", EnderIO.loc("round_robin.disabled"), - "Round Robin Disabled"); - public static final Component SELF_FEED_ENABLED = addTranslation("gui", EnderIO.loc("self_feed.enabled"), - "Self Feed Enabled"); - public static final Component SELF_FEED_DISABLED = addTranslation("gui", EnderIO.loc("self_feed.disabled"), - "Self Feed Disabled"); - public static final Component FLUID_CONDUIT_CHANGE_FLUID1 = addTranslation("gui", - EnderIO.loc("fluid_conduit.change_fluid1"), "Locked Fluid:"); - public static final Component FLUID_CONDUIT_CHANGE_FLUID2 = addTranslation("gui", - EnderIO.loc("fluid_conduit.change_fluid2"), "Click to reset!"); - public static final MutableComponent FLUID_CONDUIT_CHANGE_FLUID3 = addTranslation("gui", - EnderIO.loc("fluid_conduit.change_fluid3"), "Fluid: %s"); + public static final Component REDSTONE_MODE = addTranslation("gui", EnderIO.loc("redstone.mode"), "Redstone Mode"); public static final MutableComponent TANK_EMPTY_STRING = addTranslation("tooltip", EnderIO.loc("fluid_tank.tank_empty_tooltip"), "Empty tank"); @@ -260,8 +241,8 @@ private static Component enchantmentDescription(String enchantmentName, String s "Don't tell the others"); public static final Component RICH_ADVANCEMENT_DESCRIPTION = addTranslation("advancements", EnderIO.loc("rich.description"), "Make others think you are rich"); - public static final Component RICHER_ADVANCEMENT_TITLE = addTranslation("advancements", - EnderIO.loc("richer.title"), "Is this real?"); + public static final Component RICHER_ADVANCEMENT_TITLE = addTranslation("advancements", EnderIO.loc("richer.title"), + "Is this real?"); public static final Component RICHER_ADVANCEMENT_DESCRIPTION = addTranslation("advancements", EnderIO.loc("richer.description"), "Make others think you are richer"); @@ -286,10 +267,10 @@ private static Component enchantmentDescription(String enchantmentName, String s // region JEI - public static final Component JEI_FIRE_CRAFTING_TITLE = addTranslation("jei", EnderIO.loc("fire_crafting"), - "title", "Fire Crafting"); - public static final Component JEI_FIRE_CRAFTING_VALID_BLOCKS = addTranslation("jei", - EnderIO.loc("fire_crafting"), "valid_blocks", "Valid Blocks:"); + public static final Component JEI_FIRE_CRAFTING_TITLE = addTranslation("jei", EnderIO.loc("fire_crafting"), "title", + "Fire Crafting"); + public static final Component JEI_FIRE_CRAFTING_VALID_BLOCKS = addTranslation("jei", EnderIO.loc("fire_crafting"), + "valid_blocks", "Valid Blocks:"); public static final Component JEI_FIRE_CRAFTING_VALID_DIMENSIONS = addTranslation("jei", EnderIO.loc("fire_crafting"), "valid_dimensions", "Valid Dimensions:"); public static final Component JEI_FIRE_CRAFTING_LOOT_TABLE = addTranslation("jei", EnderIO.loc("fire_crafting"), @@ -313,12 +294,10 @@ private static void registerGlassLang() { String colorName = createEnglishPrefix(color); addTranslation("block", - EnderIO - .loc("clear_glass" + lightingKeyName + "_" + color.getName().toLowerCase(Locale.ROOT)), + EnderIO.loc("clear_glass" + lightingKeyName + "_" + color.getName().toLowerCase(Locale.ROOT)), colorName + lightingName + "Clear Glass"); addTranslation("block", - EnderIO - .loc("fused_quartz" + lightingKeyName + "_" + color.getName().toLowerCase(Locale.ROOT)), + EnderIO.loc("fused_quartz" + lightingKeyName + "_" + color.getName().toLowerCase(Locale.ROOT)), colorName + lightingName + "Fused Quartz"); } } diff --git a/enderio-base/src/main/java/com/enderio/base/common/menu/AbstractFilterMenu.java b/enderio-base/src/main/java/com/enderio/base/common/menu/AbstractFilterMenu.java new file mode 100644 index 0000000000..92620491e3 --- /dev/null +++ b/enderio-base/src/main/java/com/enderio/base/common/menu/AbstractFilterMenu.java @@ -0,0 +1,140 @@ +package com.enderio.base.common.menu; + +import com.enderio.core.common.menu.BaseEnderMenu; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.items.IItemHandler; +import org.jetbrains.annotations.Nullable; + +// TODO: Test, but should provide everything we need to open the filters from the conduit UI. +public abstract class AbstractFilterMenu extends BaseEnderMenu { + + public static int BACK_BUTTON_ID = 0; + + private final FilterAccess filterAccess; + + /** + * Server menu constructor + */ + protected AbstractFilterMenu(@Nullable MenuType menuType, int containerId, Inventory playerInventory, FilterAccess filterAccess) { + super(menuType, containerId, playerInventory); + this.filterAccess = filterAccess; + } + + /** + * Client menu constructor. + * Filter access should be created from the network buffer available in the client constructors. + */ + protected AbstractFilterMenu(@Nullable MenuType menuType, int containerId, Inventory playerInventory, + ClientFilterAccess filterAccess) { + super(menuType, containerId, playerInventory); + this.filterAccess = filterAccess; + } + + protected ItemStack getFilterStack() { + return filterAccess.getFilterItem(); + } + + @Override + public boolean stillValid(Player player) { + return filterAccess.stillValid(player); + } + + @Override + public boolean clickMenuButton(Player player, int id) { + if (id == BACK_BUTTON_ID) { + if (filterAccess.hasCustomBackDestination()) { + filterAccess.goBack(); + } else { + // Simply close the menu. + this.getPlayerInventory().player.closeContainer(); + } + return true; + } + + return super.clickMenuButton(player, id); + } + + public sealed interface FilterAccess { + ItemStack getFilterItem(); + boolean stillValid(Player player); + boolean hasCustomBackDestination(); + void goBack(); + } + + protected static final class ClientFilterAccess implements FilterAccess { + + public ItemStack stack = ItemStack.EMPTY; + + @Override + public ItemStack getFilterItem() { + return stack; + } + + @Override + public boolean stillValid(Player player) { + // TODO: Some way to verify on the client? + return true; + } + + @Override + public boolean hasCustomBackDestination() { + return false; + } + + @Override + public void goBack() { + } + } + + public record HandFilterAccess(ItemStack stack) implements FilterAccess { + + @Override + public ItemStack getFilterItem() { + return stack; + } + + @Override + public boolean stillValid(Player player) { + return player.getMainHandItem().equals(stack); + } + + @Override + public boolean hasCustomBackDestination() { + return false; + } + + @Override + public void goBack() { + } + } + + public record InventoryFilterAccess(ItemStack stack, IItemHandler itemHandler, int slot, Runnable goBackRunnable) implements FilterAccess { + + @Override + public ItemStack getFilterItem() { + return stack; + } + + @Override + public boolean stillValid(Player player) { + // TODO: Maybe check the position of the container too so we can determine if its in range? + // Assumption is that we are though because we've been opened from another gui. + return itemHandler.getStackInSlot(slot).equals(stack); + } + + @Override + public boolean hasCustomBackDestination() { + return true; + } + + @Override + public void goBack() { + if (hasCustomBackDestination()) { + goBackRunnable.run(); + } + } + } +} diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/ConduitModule.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/ConduitModule.java index 339c77797d..e3f34bda8a 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/ConduitModule.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/ConduitModule.java @@ -12,7 +12,7 @@ public interface ConduitModule { void register(IEventBus modEventBus); - void bootstrapConduits(BootstrapContext> context); + void bootstrapConduits(BootstrapContext> context); void buildConduitConditions(BiConsumer, ICondition> conditions); void buildRecipes(HolderLookup.Provider lookupProvider, RecipeOutput recipeOutput); } diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/AE2ConduitsModule.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/AE2ConduitsModule.java index 59bbbfb021..a74172b8b8 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/AE2ConduitsModule.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/AE2ConduitsModule.java @@ -7,7 +7,7 @@ import com.enderio.conduits.api.Conduit; import com.enderio.conduits.api.ConduitApi; import com.enderio.conduits.api.ConduitCapabilities; -import com.enderio.conduits.api.ConduitDataType; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; import com.enderio.conduits.api.ConduitType; import com.enderio.conduits.api.EnderIOConduitsRegistries; import com.enderio.modconduits.ConduitModule; @@ -53,9 +53,9 @@ public class AE2ConduitsModule implements ConduitModule { .exposeCapability(AECapabilities.IN_WORLD_GRID_NODE_HOST) .build()); - public static ResourceKey> NORMAL = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> NORMAL = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("me")); - public static ResourceKey> DENSE = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> DENSE = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("dense_me")); public static final Supplier> DATA = CONDUIT_DATA_TYPES.register("me", @@ -97,7 +97,7 @@ private void registerFacadeCapability(RegisterCapabilitiesEvent event) { } @Override - public void bootstrapConduits(BootstrapContext> context) { + public void bootstrapConduits(BootstrapContext> context) { context.register(NORMAL, new MEConduit(EnderIO.loc("block/conduit/me"), LANG_ME_CONDUIT, false)); context.register(DENSE, new MEConduit(EnderIO.loc("block/conduit/dense_me"), LANG_DENSE_ME_CONDUIT, true)); } diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/ConduitInWorldGridNodeHost.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/ConduitInWorldGridNodeHost.java index b7ecbb74e4..3171136114 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/ConduitInWorldGridNodeHost.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/ConduitInWorldGridNodeHost.java @@ -5,8 +5,8 @@ import appeng.api.networking.IManagedGridNode; import appeng.api.util.AECableType; import com.enderio.base.api.network.DumbStreamCodec; -import com.enderio.conduits.api.ConduitData; -import com.enderio.conduits.api.ConduitDataType; +import com.enderio.conduits.api.network.node.legacy.ConduitData; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.core.Direction; diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/MEConduit.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/MEConduit.java index d76e38216e..f527e0319e 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/MEConduit.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/appeng/MEConduit.java @@ -7,8 +7,8 @@ import com.enderio.conduits.api.ColoredRedstoneProvider; import com.enderio.conduits.api.Conduit; import com.enderio.conduits.api.ConduitMenuData; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNode; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; import com.enderio.conduits.api.ConduitType; import com.enderio.conduits.api.EnderIOConduitsRegistries; import com.enderio.conduits.api.ticker.ConduitTicker; @@ -66,7 +66,7 @@ public boolean hasConnectionDelay() { } @Override - public boolean canConnectTo(Holder> other) { + public boolean canConnectTo(Holder> other) { return other.value().type() == type(); } @@ -110,7 +110,7 @@ private void initMainNode(Level level, ConduitInWorldGridNodeHost nodeHost) { throw new UnsupportedOperationException("mainNode is already initialized"); } - Holder> asHolder = level.registryAccess().registryOrThrow(EnderIOConduitsRegistries.Keys.CONDUIT).wrapAsHolder(this); + Holder> asHolder = level.registryAccess().registryOrThrow(EnderIOConduitsRegistries.Keys.CONDUIT).wrapAsHolder(this); mainNode = GridHelper.createManagedNode(nodeHost, GridNodeListener.INSTANCE) .setVisualRepresentation(ConduitBlockItem.getStackFor(asHolder, 1)) diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduit.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduit.java index 6f4773f0e1..262865c3cc 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduit.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduit.java @@ -3,9 +3,9 @@ import com.enderio.base.api.filter.ResourceFilter; import com.enderio.conduits.api.Conduit; import com.enderio.conduits.api.ConduitMenuData; -import com.enderio.conduits.api.ConduitNode; +import com.enderio.conduits.api.network.node.ConduitNode; import com.enderio.conduits.api.ConduitType; -import com.enderio.conduits.api.SlotType; +import com.enderio.conduits.api.bundle.SlotType; import com.enderio.conduits.common.init.ConduitLang; import com.enderio.core.common.util.TooltipUtil; import com.mojang.serialization.Codec; @@ -58,7 +58,7 @@ public ConduitMenuData getMenuData() { } @Override - public boolean canBeInSameBundle(Holder> otherConduit) { + public boolean canBeInSameBundle(Holder> otherConduit) { if (otherConduit.value().type() != type()) { return true; } @@ -67,7 +67,7 @@ public boolean canBeInSameBundle(Holder> otherConduit) { } @Override - public boolean canBeReplacedBy(Holder> otherConduit) { + public boolean canBeReplacedBy(Holder> otherConduit) { if (otherConduit.value().type() != type()) { return false; } diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduitData.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduitData.java index e540540299..55e05b0c67 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduitData.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduitData.java @@ -1,7 +1,7 @@ package com.enderio.modconduits.mods.mekanism; -import com.enderio.conduits.api.ConduitData; -import com.enderio.conduits.api.ConduitDataType; +import com.enderio.conduits.api.network.node.legacy.ConduitData; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduitScreenExtension.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduitScreenExtension.java index dd522a6aa5..d63e1a9ffd 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduitScreenExtension.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalConduitScreenExtension.java @@ -1,7 +1,7 @@ package com.enderio.modconduits.mods.mekanism; import com.enderio.base.api.EnderIO; -import com.enderio.conduits.api.ConduitDataAccessor; +import com.enderio.conduits.api.network.node.legacy.ConduitDataAccessor; import com.enderio.conduits.api.screen.ConduitScreenExtension; import com.enderio.core.common.util.TooltipUtil; import com.mojang.blaze3d.systems.RenderSystem; diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalTicker.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalTicker.java index 7e25bc5c8e..4e37a5860c 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalTicker.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/ChemicalTicker.java @@ -1,8 +1,8 @@ package com.enderio.modconduits.mods.mekanism; import com.enderio.conduits.api.ColoredRedstoneProvider; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNode; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; import com.enderio.conduits.api.ticker.CapabilityAwareConduitTicker; import mekanism.api.Action; import mekanism.api.chemical.Chemical; diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/HeatTicker.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/HeatTicker.java index e399e95f02..37b4db20f1 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/HeatTicker.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/HeatTicker.java @@ -1,7 +1,7 @@ package com.enderio.modconduits.mods.mekanism; import com.enderio.conduits.api.ColoredRedstoneProvider; -import com.enderio.conduits.api.ConduitNetwork; +import com.enderio.conduits.api.network.ConduitNetwork; import com.enderio.conduits.api.ticker.CapabilityAwareConduitTicker; import mekanism.api.heat.IHeatHandler; import net.minecraft.core.Direction; diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/MekanismModule.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/MekanismModule.java index ec876aad8b..23f8b83066 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/MekanismModule.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/MekanismModule.java @@ -5,7 +5,7 @@ import com.enderio.base.common.init.EIOCreativeTabs; import com.enderio.base.common.init.EIOItems; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitDataType; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; import com.enderio.conduits.api.ConduitType; import com.enderio.conduits.api.EnderIOConduitsRegistries; import com.enderio.conduits.api.screen.RegisterConduitScreenExtensionsEvent; @@ -106,13 +106,13 @@ public static class Item { } } - public static final ResourceKey> CHEMICAL = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static final ResourceKey> CHEMICAL = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("chemical")); - public static final ResourceKey> PRESSURIZED_CHEMICAL = ResourceKey + public static final ResourceKey> PRESSURIZED_CHEMICAL = ResourceKey .create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("pressurized_chemical")); - public static final ResourceKey> ENDER_CHEMICAL = ResourceKey + public static final ResourceKey> ENDER_CHEMICAL = ResourceKey .create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("ender_chemical")); - public static final ResourceKey> HEAT = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static final ResourceKey> HEAT = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("heat")); public static final Supplier> CHEMICAL_DATA_TYPE = CONDUIT_DATA_TYPES @@ -161,7 +161,7 @@ public void registerScreen(RegisterConduitScreenExtensionsEvent event) { } @Override - public void bootstrapConduits(BootstrapContext> context) { + public void bootstrapConduits(BootstrapContext> context) { context.register(HEAT, new HeatConduit(EnderIO.loc("block/conduit/heat"), LANG_HEAT_CONDUIT)); context.register(CHEMICAL, new ChemicalConduit(EnderIO.loc("block/conduit/chemical"), LANG_CHEMICAL_CONDUIT, 750, false)); diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/MultiCapabilityAwareConduitTicker.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/MultiCapabilityAwareConduitTicker.java index 4d583440a5..78426728f8 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/MultiCapabilityAwareConduitTicker.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/mekanism/MultiCapabilityAwareConduitTicker.java @@ -2,8 +2,8 @@ import com.enderio.conduits.api.ColoredRedstoneProvider; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNode; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; import com.enderio.conduits.api.ticker.IOAwareConduitTicker; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RSConduit.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RSConduit.java index f6527bea4f..2c55e3b36d 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RSConduit.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RSConduit.java @@ -3,8 +3,8 @@ import com.enderio.conduits.api.ColoredRedstoneProvider; import com.enderio.conduits.api.Conduit; import com.enderio.conduits.api.ConduitMenuData; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNode; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; import com.enderio.conduits.api.ConduitType; import com.enderio.conduits.api.ticker.ConduitTicker; import com.mojang.serialization.MapCodec; diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RSNetworkHost.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RSNetworkHost.java index 55943edc21..5f9fb8fc1a 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RSNetworkHost.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RSNetworkHost.java @@ -1,8 +1,8 @@ package com.enderio.modconduits.mods.refinedstorage; import com.enderio.base.api.network.DumbStreamCodec; -import com.enderio.conduits.api.ConduitData; -import com.enderio.conduits.api.ConduitDataType; +import com.enderio.conduits.api.network.node.legacy.ConduitData; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; diff --git a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RefinedStorageModule.java b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RefinedStorageModule.java index 2e3c23d537..6f7242b781 100644 --- a/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RefinedStorageModule.java +++ b/enderio-conduits-modded/src/main/java/com/enderio/modconduits/mods/refinedstorage/RefinedStorageModule.java @@ -4,7 +4,7 @@ import com.enderio.base.common.init.EIOItems; import com.enderio.conduits.api.Conduit; import com.enderio.conduits.api.ConduitApi; -import com.enderio.conduits.api.ConduitDataType; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; import com.enderio.conduits.api.ConduitType; import com.enderio.conduits.api.EnderIOConduitsRegistries; import com.enderio.modconduits.ConduitModule; @@ -45,7 +45,7 @@ public class RefinedStorageModule implements ConduitModule { RefinedStorageNeoForgeApiImpl.INSTANCE.getNetworkNodeContainerProviderCapability()) .build()); - public static ResourceKey> RS = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> RS = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("rs")); public static final Supplier> DATA = CONDUIT_DATA_TYPES.register("rs", @@ -65,7 +65,7 @@ public void register(IEventBus modEventBus) { } @Override - public void bootstrapConduits(BootstrapContext> context) { + public void bootstrapConduits(BootstrapContext> context) { context.register(RS, new RSConduit(EnderIO.loc("block/conduit/rs"), LANG_RS_CONDUIT)); } diff --git a/enderio-conduits/build.gradle.kts b/enderio-conduits/build.gradle.kts index b44d9f1819..5857104e88 100644 --- a/enderio-conduits/build.gradle.kts +++ b/enderio-conduits/build.gradle.kts @@ -31,6 +31,7 @@ val graphlibVersion: String by project val graphlibVersionRange: String by project val cctMinecraftVersion: String by project val cctVersion: String by project +val jadeFileId: String by project configurations { runtimeClasspath.get().extendsFrom(create("localRuntime")) @@ -50,6 +51,9 @@ dependencies { compileOnly("cc.tweaked:cc-tweaked-$cctMinecraftVersion-core-api:$cctVersion") compileOnly("cc.tweaked:cc-tweaked-$cctMinecraftVersion-forge-api:$cctVersion") + // Jade for addon + compileOnly("curse.maven:jade-324717:${jadeFileId}") + // For painting recipe. // TODO: This isn't great. compileOnly(project(":enderio-machines")) diff --git a/enderio-conduits/src/generated/resources/assets/enderio/lang/en_us.json b/enderio-conduits/src/generated/resources/assets/enderio/lang/en_us.json index e18c6dddc0..4eed1333a1 100644 --- a/enderio-conduits/src/generated/resources/assets/enderio/lang/en_us.json +++ b/enderio-conduits/src/generated/resources/assets/enderio/lang/en_us.json @@ -1,7 +1,22 @@ { "block.enderio.conduit": "Conduit Bundle", + "gui.enderio.conduit.enabled": "Enabled", + "gui.enderio.conduit.error.no_screen_type": "Error: No screen type defined", "gui.enderio.conduit.extract": "Extract", + "gui.enderio.conduit.input": "Input", "gui.enderio.conduit.insert": "Insert", + "gui.enderio.conduit.output": "Output", + "gui.enderio.conduit.redstone.signal_color": "Signal Color", + "gui.enderio.conduit.redstone.strong_signal": "Strong Signal", + "gui.enderio.conduit_channel": "Channel", + "gui.enderio.fluid_conduit.change_fluid1": "Locked Fluid:", + "gui.enderio.fluid_conduit.change_fluid2": "Click to reset!", + "gui.enderio.fluid_conduit.change_fluid3": "Fluid: %s", + "gui.enderio.redstone_channel": "Signal Color", + "gui.enderio.round_robin.disabled": "Round Robin Disabled", + "gui.enderio.round_robin.enabled": "Round Robin Enabled", + "gui.enderio.self_feed.disabled": "Self Feed Disabled", + "gui.enderio.self_feed.enabled": "Self Feed Enabled", "item.enderio.conduit": " Conduit", "item.enderio.conduit.ender_energy": "Ender Energy Conduit", "item.enderio.conduit.ender_fluid": "Ender Fluid Conduit", @@ -14,10 +29,6 @@ "item.enderio.conduit.pressurized_fluid": "Pressurized Fluid Conduit", "item.enderio.conduit.redstone": "Redstone Conduit", "item.enderio.conduit_facade": "Conduit Facade", - "item.enderio.extraction_speed_upgrade_1": "Tier 1 Extraction Speed Upgrade", - "item.enderio.extraction_speed_upgrade_2": "Tier 2 Extraction Speed Upgrade", - "item.enderio.extraction_speed_upgrade_3": "Tier 3 Extraction Speed Upgrade", - "item.enderio.extraction_speed_upgrade_4": "Tier 4 Extraction Speed Upgrade", "item.enderio.hardened_conduit_facade": "Hardened Conduit Facade", "item.enderio.redstone_and_filter": "Redstone And Filter", "item.enderio.redstone_counting_filter": "Redstone Counting Filter", diff --git a/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_1.json b/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_1.json deleted file mode 100644 index bd0880e5f9..0000000000 --- a/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_1.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "enderio:item/extraction_speed_upgrade_1" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_2.json b/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_2.json deleted file mode 100644 index b5d38874f9..0000000000 --- a/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_2.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "enderio:item/extraction_speed_upgrade_2" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_3.json b/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_3.json deleted file mode 100644 index 3180e14fef..0000000000 --- a/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_3.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "enderio:item/extraction_speed_upgrade_3" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_4.json b/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_4.json deleted file mode 100644 index e2ac092ca1..0000000000 --- a/enderio-conduits/src/generated/resources/assets/enderio/models/item/extraction_speed_upgrade_4.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "minecraft:item/generated", - "textures": { - "layer0": "enderio:item/extraction_speed_upgrade_4" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_1.json b/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_1.json deleted file mode 100644 index e4992dcade..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_1.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_ingredient": { - "conditions": { - "items": [ - { - "items": "enderio:redstone_alloy_ingot" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "enderio:extraction_speed_upgrade_1" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_ingredient" - ] - ], - "rewards": { - "recipes": [ - "enderio:extraction_speed_upgrade_1" - ] - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_1_upgrade.json b/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_1_upgrade.json deleted file mode 100644 index 895185355d..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_1_upgrade.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_ingredient": { - "conditions": { - "items": [ - { - "items": "enderio:conductive_alloy_ingot" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "enderio:extraction_speed_upgrade_1_upgrade" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_ingredient" - ] - ], - "rewards": { - "recipes": [ - "enderio:extraction_speed_upgrade_1_upgrade" - ] - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_2.json b/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_2.json deleted file mode 100644 index 2dbebf1f28..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_2.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_ingredient": { - "conditions": { - "items": [ - { - "items": "enderio:conductive_alloy_ingot" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "enderio:extraction_speed_upgrade_2" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_ingredient" - ] - ], - "rewards": { - "recipes": [ - "enderio:extraction_speed_upgrade_2" - ] - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_2_upgrade.json b/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_2_upgrade.json deleted file mode 100644 index 5e52d41bda..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_2_upgrade.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_ingredient": { - "conditions": { - "items": [ - { - "items": "enderio:conductive_alloy_ingot" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "enderio:extraction_speed_upgrade_2_upgrade" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_ingredient" - ] - ], - "rewards": { - "recipes": [ - "enderio:extraction_speed_upgrade_2_upgrade" - ] - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_3.json b/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_3.json deleted file mode 100644 index 5e21c03b6f..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_3.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_ingredient": { - "conditions": { - "items": [ - { - "items": "enderio:soularium_ingot" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "enderio:extraction_speed_upgrade_3" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_ingredient" - ] - ], - "rewards": { - "recipes": [ - "enderio:extraction_speed_upgrade_3" - ] - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_3_upgrade.json b/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_3_upgrade.json deleted file mode 100644 index ec6c9962d3..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_3_upgrade.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_ingredient": { - "conditions": { - "items": [ - { - "items": "enderio:energetic_alloy_ingot" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "enderio:extraction_speed_upgrade_3_upgrade" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_ingredient" - ] - ], - "rewards": { - "recipes": [ - "enderio:extraction_speed_upgrade_3_upgrade" - ] - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_4.json b/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_4.json deleted file mode 100644 index 66fbd2171c..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/advancement/recipes/misc/extraction_speed_upgrade_4.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "parent": "minecraft:recipes/root", - "criteria": { - "has_ingredient": { - "conditions": { - "items": [ - { - "items": "enderio:energetic_alloy_ingot" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "enderio:extraction_speed_upgrade_4" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_ingredient" - ] - ], - "rewards": { - "recipes": [ - "enderio:extraction_speed_upgrade_4" - ] - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_1.json b/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_1.json deleted file mode 100644 index 730df3164f..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_1.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "minecraft:crafting_shaped", - "category": "misc", - "key": { - "A": { - "tag": "c:ingots/redstone_alloy" - }, - "I": { - "tag": "c:ingots/iron" - }, - "P": { - "item": "minecraft:piston" - }, - "T": { - "item": "minecraft:redstone_torch" - } - }, - "pattern": [ - "III", - "APA", - "ATA" - ], - "result": { - "count": 2, - "id": "enderio:extraction_speed_upgrade_1" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_1_upgrade.json b/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_1_upgrade.json deleted file mode 100644 index e384958a55..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_1_upgrade.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "minecraft:crafting_shapeless", - "category": "misc", - "ingredients": [ - { - "item": "enderio:extraction_speed_upgrade_1" - }, - { - "tag": "c:ingots/conductive_alloy" - }, - { - "tag": "c:ingots/conductive_alloy" - } - ], - "result": { - "count": 1, - "id": "enderio:extraction_speed_upgrade_2" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_2.json b/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_2.json deleted file mode 100644 index 6c66df10bf..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_2.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "minecraft:crafting_shaped", - "category": "misc", - "key": { - "A": { - "tag": "c:ingots/conductive_alloy" - }, - "I": { - "tag": "c:ingots/iron" - }, - "P": { - "item": "minecraft:piston" - }, - "T": { - "item": "minecraft:redstone_torch" - } - }, - "pattern": [ - "III", - "APA", - "ATA" - ], - "result": { - "count": 2, - "id": "enderio:extraction_speed_upgrade_2" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_2_upgrade.json b/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_2_upgrade.json deleted file mode 100644 index 4088922f80..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_2_upgrade.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "minecraft:crafting_shapeless", - "category": "misc", - "ingredients": [ - { - "item": "enderio:extraction_speed_upgrade_2" - }, - { - "tag": "c:ingots/soularium" - }, - { - "tag": "c:ingots/soularium" - } - ], - "result": { - "count": 1, - "id": "enderio:extraction_speed_upgrade_3" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_3.json b/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_3.json deleted file mode 100644 index a7b8a396eb..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_3.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "minecraft:crafting_shaped", - "category": "misc", - "key": { - "A": { - "tag": "c:ingots/soularium" - }, - "I": { - "tag": "c:ingots/dark_steel" - }, - "P": { - "item": "minecraft:piston" - }, - "T": { - "item": "minecraft:redstone_torch" - } - }, - "pattern": [ - "III", - "APA", - "ATA" - ], - "result": { - "count": 2, - "id": "enderio:extraction_speed_upgrade_3" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_3_upgrade.json b/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_3_upgrade.json deleted file mode 100644 index 2fd3cb49f3..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_3_upgrade.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "minecraft:crafting_shapeless", - "category": "misc", - "ingredients": [ - { - "item": "enderio:extraction_speed_upgrade_3" - }, - { - "tag": "c:ingots/energetic_alloy" - }, - { - "tag": "c:ingots/energetic_alloy" - } - ], - "result": { - "count": 1, - "id": "enderio:extraction_speed_upgrade_4" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_4.json b/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_4.json deleted file mode 100644 index bca4686aec..0000000000 --- a/enderio-conduits/src/generated/resources/data/enderio/recipe/extraction_speed_upgrade_4.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "minecraft:crafting_shaped", - "category": "misc", - "key": { - "A": { - "tag": "c:ingots/energetic_alloy" - }, - "I": { - "tag": "c:ingots/dark_steel" - }, - "P": { - "item": "minecraft:piston" - }, - "T": { - "item": "minecraft:redstone_torch" - } - }, - "pattern": [ - "III", - "APA", - "ATA" - ], - "result": { - "count": 2, - "id": "enderio:extraction_speed_upgrade_4" - } -} \ No newline at end of file diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/EnderIOConduits.java b/enderio-conduits/src/main/java/com/enderio/conduits/EnderIOConduits.java index 28dbd775b0..cc61b76eeb 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/EnderIOConduits.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/EnderIOConduits.java @@ -39,7 +39,7 @@ public class EnderIOConduits { public static Regilite REGILITE = new Regilite(EnderIO.NAMESPACE); public EnderIOConduits(IEventBus modEventBus, ModContainer modContainer) { - Conduits.register(modEventBus); + Conduits.register(); ConduitTypes.register(modEventBus); ConduitBlockEntities.register(modEventBus); ConduitMenus.register(modEventBus); @@ -56,6 +56,8 @@ public EnderIOConduits(IEventBus modEventBus, ModContainer modContainer) { public static void onNewRegistries(NewRegistryEvent event) { event.register(EnderIOConduitsRegistries.CONDUIT_TYPE); event.register(EnderIOConduitsRegistries.CONDUIT_DATA_TYPE); + event.register(EnderIOConduitsRegistries.CONDUIT_CONNECTION_CONFIG_TYPE); + event.register(EnderIOConduitsRegistries.CONDUIT_NODE_DATA_TYPE); event.register(EnderIOConduitsRegistries.CONDUIT_NETWORK_CONTEXT_TYPE); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java index 3563cd339f..8d051ad716 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/ColoredRedstoneProvider.java @@ -1,10 +1,10 @@ package com.enderio.conduits.api; import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.DyeColor; +import net.minecraft.world.level.Level; @FunctionalInterface public interface ColoredRedstoneProvider { - boolean isRedstoneActive(ServerLevel serverLevel, BlockPos pos, DyeColor color); + boolean isRedstoneActive(Level level, BlockPos pos, DyeColor color); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java index b7b52e135e..58e04b8c73 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/Conduit.java @@ -2,14 +2,20 @@ import com.enderio.base.api.filter.ResourceFilter; import com.enderio.base.api.misc.RedstoneControl; +import com.enderio.conduits.api.bundle.ConduitBundleReader; +import com.enderio.conduits.api.bundle.SlotType; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.network.node.ConduitNode; +import com.enderio.conduits.api.network.node.legacy.ConduitDataAccessor; import com.enderio.conduits.api.ticker.ConduitTicker; -import com.enderio.conduits.api.upgrade.ConduitUpgrade; import com.mojang.serialization.Codec; import java.util.Set; import java.util.function.Consumer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.codec.ByteBufCodecs; @@ -17,6 +23,7 @@ import net.minecraft.resources.RegistryFixedCodec; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Item; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.component.TooltipProvider; @@ -24,14 +31,15 @@ import net.neoforged.neoforge.capabilities.BlockCapability; import org.jetbrains.annotations.Nullable; -public interface Conduit> extends Comparable, TooltipProvider { +public interface Conduit, TConnectionConfig extends ConnectionConfig> + extends Comparable, TooltipProvider { - Codec> DIRECT_CODEC = EnderIOConduitsRegistries.CONDUIT_TYPE.byNameCodec() + Codec> DIRECT_CODEC = EnderIOConduitsRegistries.CONDUIT_TYPE.byNameCodec() .dispatch(Conduit::type, ConduitType::codec); - Codec>> CODEC = RegistryFixedCodec.create(EnderIOConduitsRegistries.Keys.CONDUIT); + Codec>> CODEC = RegistryFixedCodec.create(EnderIOConduitsRegistries.Keys.CONDUIT); - StreamCodec>> STREAM_CODEC = ByteBufCodecs + StreamCodec>> STREAM_CODEC = ByteBufCodecs .holderRegistry(EnderIOConduitsRegistries.Keys.CONDUIT); /** @@ -64,39 +72,60 @@ default int graphTickRate() { */ ConduitTicker getTicker(); - ConduitMenuData getMenuData(); + /** + * @implNote if a conduit has a menu, you must also register a {@link com.enderio.conduits.api.screen.ConduitScreenType} for it. + * @return whether this conduit has a menu. + */ + boolean hasMenu(); + + // region Conduit Checks - default boolean canBeInSameBundle(Holder> otherConduit) { + default boolean canBeInSameBundle(Holder> otherConduit) { return true; } - default boolean canBeReplacedBy(Holder> otherConduit) { + default boolean canBeReplacedBy(Holder> otherConduit) { return false; } /** * @return true if both types are compatible */ - default boolean canConnectTo(Holder> other) { + default boolean canConnectToConduit(Holder> other) { return this.equals(other.value()); } + /** + * If this conduit overrides {@link #canConnectConduits(ConduitNode, ConduitNode)}, return true. + * This will avoid showing connections between conduits on the client until the server evaluates whether they can connect. + * @apiNote Failing to override this properly could result in connection desyncs. + * @return whether this conduit has additional server-side connection checks. + */ + default boolean hasServerConnectionChecks() { + return false; + } + /** * This can be used to prevent connection between nodes with incompatible data. + * @apiNote Not called by the server if {@link #hasServerConnectionChecks()} does not return true. * @return true if both nodes are compatible. */ - default boolean canConnectTo(ConduitNode selfNode, ConduitNode otherNode) { + default boolean canConnectConduits(ConduitNode selfNode, ConduitNode otherNode) { return true; } - default boolean canConnectTo(Level level, BlockPos conduitPos, Direction direction) { - return getTicker().canConnectTo(level, conduitPos, direction); - } + // endregion - default boolean canForceConnectTo(Level level, BlockPos conduitPos, Direction direction) { - return getTicker().canForceConnectTo(level, conduitPos, direction); + // region Connection Checks + + boolean canConnectToBlock(Level level, BlockPos conduitPos, Direction direction); + + default boolean canForceConnectToBlock(Level level, BlockPos conduitPos, Direction direction) { + return canConnectToBlock(level, conduitPos, direction); } + // endregion + /** * @return if this is not always able to determine connectivity to its neighbours at time of placement, but the tick later */ @@ -104,18 +133,15 @@ default boolean hasConnectionDelay() { return false; } - default boolean canApplyUpgrade(SlotType slotType, ConduitUpgrade conduitUpgrade) { - return false; - } - default boolean canApplyFilter(SlotType slotType, ResourceFilter resourceFilter) { return false; } /** * Gets the conduit texture to display, given the data. + * @param extraWorldData client data from {@link #getExtraWorldData(ConduitBundleReader, ConduitNode)}. */ - default ResourceLocation getTexture(ConduitNode node) { + default ResourceLocation getTexture(@Nullable CompoundTag extraWorldData) { return texture(); } @@ -136,18 +162,61 @@ default void onConnectTo(ConduitNode selfNode, ConduitNode otherNode) { // endregion @Nullable - default TCapability proxyCapability(BlockCapability capability, - ConduitNode node, Level level, BlockPos pos, @Nullable TContext context) { + default TCapability proxyCapability(Level level, + ColoredRedstoneProvider coloredRedstoneProvider, ConduitNode node, + BlockCapability capability, @Nullable TContext context) { return null; } - default ConduitConnectionData getDefaultConnection(Level level, BlockPos pos, Direction direction) { - return new ConduitConnectionData(false, true, RedstoneControl.NEVER_ACTIVE); + // region Conduit Data + + /** + * @return the expected conduit connection config type. + */ + ConnectionConfigType connectionConfigType(); + + /** + * Convert old conduit connection data into the new connection config. + * This is executed during world load, so no level is available to query. + * @implNote Only needs to be implemented if the conduit existed in Ender IO 7.1 or earlier. + * @deprecated Only for conversion of <7.1 conduit data. Will be removed in Ender IO 8. + */ + @Deprecated(since = "7.2") + default TConnectionConfig convertConnection(boolean isInsert, boolean isExtract, DyeColor inputChannel, + DyeColor outputChannel, RedstoneControl redstoneControl, DyeColor redstoneChannel) { + return connectionConfigType().getDefault(); + } + + /** + * Copy legacy data from the old conduit data accessor to the new node however you wish. + * @implNote The node is guaranteed to have a network at this point, so the context can be accessed. + * @param node the node. + * @param legacyDataAccessor the legacy data. + */ + @Deprecated(since = "7.2") + default void copyLegacyData(ConduitNode node, ConduitDataAccessor legacyDataAccessor) { + } + + // endregion + + // region Custom Sync + + @Nullable + default CompoundTag getExtraGuiData(ConduitBundleReader conduitBundle, ConduitNode node, Direction side) { + return null; } - record ConduitConnectionData(boolean isInsert, boolean isExtract, RedstoneControl control) { + /** + * Create a custom tag for syncing data from node data or network context to the client for extra behaviours. + * @return custom sync data. + */ + @Nullable + default CompoundTag getExtraWorldData(ConduitBundleReader conduitBundle, ConduitNode node) { + return null; } + // endregion + @Override default void addToTooltip(Item.TooltipContext pContext, Consumer pTooltipAdder, TooltipFlag pTooltipFlag) { diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitApi.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitApi.java index eac2ce6793..080a9a8686 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitApi.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitApi.java @@ -1,20 +1,21 @@ package com.enderio.conduits.api; +import java.util.ServiceLoader; import net.minecraft.core.Holder; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; -import java.util.ServiceLoader; - public interface ConduitApi { ConduitApi INSTANCE = ServiceLoader.load(ConduitApi.class).findFirst().orElseThrow(); - default ItemStack getStackForType(Holder> conduit) { + default ItemStack getStackForType(Holder> conduit) { return getStackForType(conduit, 1); } - ItemStack getStackForType(Holder> conduit, int count); + ItemStack getStackForType(Holder> conduit, int count); + + Ingredient getIngredientForType(Holder> conduit); - Ingredient getIngredientForType(Holder> conduit); + int getConduitSortIndex(Holder> conduit); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitCapabilities.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitCapabilities.java index 3dab87a20a..756bd3f916 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitCapabilities.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitCapabilities.java @@ -2,12 +2,9 @@ import com.enderio.base.api.EnderIO; import com.enderio.conduits.api.facade.ConduitFacadeProvider; -import com.enderio.conduits.api.upgrade.ConduitUpgrade; import net.neoforged.neoforge.capabilities.ItemCapability; public class ConduitCapabilities { - public static final ItemCapability CONDUIT_UPGRADE = ItemCapability - .createVoid(EnderIO.loc("conduit_upgrade"), ConduitUpgrade.class); public static final ItemCapability CONDUIT_FACADE_PROVIDER = ItemCapability .createVoid(EnderIO.loc("conduit_facade_provider"), ConduitFacadeProvider.class); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitMenuData.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitMenuData.java deleted file mode 100644 index 5c71d826b1..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitMenuData.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.enderio.conduits.api; - -public interface ConduitMenuData { - boolean hasFilterInsert(); - - boolean hasFilterExtract(); - - boolean hasUpgrade(); - - default boolean showBarSeparator() { - return true; - } - - default boolean showBothEnable() { - return true; - } - - boolean showColorInsert(); - - boolean showColorExtract(); - - boolean showRedstoneExtract(); - - record Simple(boolean hasFilterInsert, boolean hasFilterExtract, boolean hasUpgrade, boolean showColorInsert, boolean showColorExtract, - boolean showRedstoneExtract) implements ConduitMenuData {} -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContextSerializer.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContextSerializer.java deleted file mode 100644 index da31ab1c33..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContextSerializer.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.enderio.conduits.api; - -import net.minecraft.nbt.CompoundTag; - -public interface ConduitNetworkContextSerializer> { - CompoundTag save(T context); - T load(CompoundTag tag); -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNode.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNode.java deleted file mode 100644 index d3d2b396fd..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNode.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.enderio.conduits.api; - -import com.enderio.base.api.filter.ResourceFilter; -import com.enderio.base.api.misc.RedstoneControl; -import com.enderio.conduits.api.upgrade.ConduitUpgrade; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.item.DyeColor; -import org.jetbrains.annotations.Nullable; - -import java.util.Optional; - -public interface ConduitNode extends ConduitDataAccessor { - Optional getIOState(Direction direction); - BlockPos getPos(); - - @Nullable - ConduitUpgrade getUpgrade(Direction direction); - - @Nullable - ResourceFilter getExtractFilter(Direction direction); - - @Nullable - ResourceFilter getInsertFilter(Direction direction); - - @Nullable - ConduitNetwork getParentGraph(); - - record IOState(Optional insert, Optional extract, RedstoneControl control, DyeColor redstoneChannel) { - - public boolean isInsert() { - return insert().isPresent(); - } - - public boolean isExtract() { - return extract().isPresent(); - } - - public static IOState of(@Nullable DyeColor in, @Nullable DyeColor extract, RedstoneControl control, DyeColor redstoneChannel) { - return new IOState(Optional.ofNullable(in), Optional.ofNullable(extract), control, redstoneChannel); - } - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitRedstoneSignalAware.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitRedstoneSignalAware.java new file mode 100644 index 0000000000..ad6020b23e --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitRedstoneSignalAware.java @@ -0,0 +1,18 @@ +package com.enderio.conduits.api; + +import net.minecraft.world.item.DyeColor; +import org.jetbrains.annotations.Nullable; + +@FunctionalInterface +public interface ConduitRedstoneSignalAware { + // Useful in contexts in which you know the config doesn't require signals. + // Mainly intended for the hardcoded redstone conduit checks. + ConduitRedstoneSignalAware NONE = signalColor -> false; + + /** + * Whether the block has a redstone signal. + * @param signalColor if set, will also check for a signal in the attached redstone conduit network. + * @return whether there is a redstone signal to this block. + */ + boolean hasRedstoneSignal(@Nullable DyeColor signalColor); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitType.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitType.java index cd57ea75a7..18ba1d718b 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitType.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitType.java @@ -3,7 +3,9 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.Direction; +import java.util.HashSet; +import java.util.Set; +import java.util.function.BiFunction; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; @@ -12,15 +14,12 @@ import net.minecraft.resources.ResourceLocation; import net.neoforged.neoforge.capabilities.BlockCapability; -import java.util.HashSet; -import java.util.Set; -import java.util.function.BiFunction; - -public interface ConduitType> { +// TODO: Change this from an interface to a record + builder? +// No point in having an interface imo. +public interface ConduitType> { Codec> CODEC = Codec.lazyInitialized(EnderIOConduitsRegistries.CONDUIT_TYPE::byNameCodec); - StreamCodec> STREAM_CODEC = StreamCodec.recursive( - streamCodec -> ByteBufCodecs.registry(EnderIOConduitsRegistries.Keys.CONDUIT_TYPE) - ); + StreamCodec> STREAM_CODEC = StreamCodec + .recursive(streamCodec -> ByteBufCodecs.registry(EnderIOConduitsRegistries.Keys.CONDUIT_TYPE)); /** * @return The codec used for datapack read and sync. @@ -32,28 +31,26 @@ public interface ConduitType> { */ Set> exposedCapabilities(); - static > ConduitType of(MapCodec codec) { + static > ConduitType of(MapCodec codec) { return builder(codec).build(); } - static > Builder builder(MapCodec codec) { + static > Builder builder(MapCodec codec) { return new Builder<>(codec); } - static > ConduitType of(BiFunction factory) { + static > ConduitType of(BiFunction factory) { return builder(factory).build(); } - static > Builder builder(BiFunction factory) { - return new Builder(RecordCodecBuilder.mapCodec( - builder -> builder.group( - ResourceLocation.CODEC.fieldOf("texture").forGetter(Conduit::texture), - ComponentSerialization.CODEC.fieldOf("description").forGetter(Conduit::description) - ).apply(builder, factory) - )); + static > Builder builder(BiFunction factory) { + return new Builder(RecordCodecBuilder.mapCodec(builder -> builder + .group(ResourceLocation.CODEC.fieldOf("texture").forGetter(Conduit::texture), + ComponentSerialization.CODEC.fieldOf("description").forGetter(Conduit::description)) + .apply(builder, factory))); } - class Builder> { + class Builder> { private final MapCodec codec; private final Set> exposedCapabilities; @@ -71,9 +68,8 @@ public ConduitType build() { return new SimpleType<>(codec, exposedCapabilities); } - record SimpleType>( - MapCodec codec, - Set> exposedCapabilities - ) implements ConduitType {} + record SimpleType>(MapCodec codec, Set> exposedCapabilities) + implements ConduitType { + } } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java index eb36ee500b..0792a56054 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/EnderIOConduitsRegistries.java @@ -1,9 +1,9 @@ package com.enderio.conduits.api; -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitDataType; -import com.enderio.conduits.api.ConduitNetworkContextType; -import com.enderio.conduits.api.ConduitType; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.network.ConduitNetworkContextType; +import com.enderio.conduits.api.network.node.NodeDataType; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; @@ -11,28 +11,41 @@ public class EnderIOConduitsRegistries { - public static final Registry> CONDUIT_TYPE = new RegistryBuilder<>(Keys.CONDUIT_TYPE) - .sync(true) - .create(); + public static final Registry> CONDUIT_TYPE = new RegistryBuilder<>(Keys.CONDUIT_TYPE).sync(true) + .create(); + @Deprecated(forRemoval = true, since = "7.2") public static final Registry> CONDUIT_DATA_TYPE = new RegistryBuilder<>(Keys.CONDUIT_DATA_TYPE) - .sync(true) - .create(); + .sync(true) + .create(); - public static final Registry> CONDUIT_NETWORK_CONTEXT_TYPE = new RegistryBuilder<>(Keys.CONDUIT_NETWORK_CONTEXT_TYPE) - .sync(true) - .create(); + public static final Registry> CONDUIT_NODE_DATA_TYPE = new RegistryBuilder<>( + Keys.CONDUIT_NODE_DATA_TYPE).sync(true).create(); + + public static final Registry> CONDUIT_CONNECTION_CONFIG_TYPE = new RegistryBuilder<>( + Keys.CONDUIT_CONNECTION_CONFIG_TYPE).sync(true).create(); + + public static final Registry> CONDUIT_NETWORK_CONTEXT_TYPE = new RegistryBuilder<>( + Keys.CONDUIT_NETWORK_CONTEXT_TYPE).sync(true).create(); public static class Keys { - public static final ResourceKey>> CONDUIT_DATA_TYPE = createKey("conduit_data_type"); - public static final ResourceKey>> CONDUIT_NETWORK_CONTEXT_TYPE = createKey("conduit_network_context_type"); + @Deprecated(forRemoval = true, since = "7.2") + public static final ResourceKey>> CONDUIT_DATA_TYPE = createKey( + "conduit_data_type"); + + public static final ResourceKey>> CONDUIT_NODE_DATA_TYPE = createKey( + "conduit_node_data_type"); + public static final ResourceKey>> CONDUIT_CONNECTION_CONFIG_TYPE = createKey( + "conduit_connection_config_type"); + public static final ResourceKey>> CONDUIT_NETWORK_CONTEXT_TYPE = createKey( + "conduit_network_context_type"); public static final ResourceKey>> CONDUIT_TYPE = createKey("conduit_type"); /** * Conduit types are now a datapack registry. */ - public static final ResourceKey>> CONDUIT = createKey("conduit"); + public static final ResourceKey>> CONDUIT = createKey("conduit"); private static ResourceKey> createKey(String name) { return ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath("enderio", name)); diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/SlotType.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/SlotType.java deleted file mode 100644 index f67376d2e4..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/SlotType.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.enderio.conduits.api; - -public enum SlotType { - FILTER_EXTRACT, - FILTER_INSERT, - UPGRADE_EXTRACT; - - public static final int Y_POSITION = 71; - public int getX() { - return switch (this) { - case FILTER_EXTRACT -> 113; - case FILTER_INSERT -> 23; - case UPGRADE_EXTRACT -> 131; - }; - } - - public int getY() { - return Y_POSITION; - } - - public boolean isAvailableFor(ConduitMenuData data) { - return switch (this) { - case FILTER_INSERT -> data.hasFilterInsert(); - case FILTER_EXTRACT -> data.hasFilterExtract(); - case UPGRADE_EXTRACT -> data.hasUpgrade(); - }; - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/AddConduitResult.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/AddConduitResult.java new file mode 100644 index 0000000000..cb3e9a9d2b --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/AddConduitResult.java @@ -0,0 +1,34 @@ +package com.enderio.conduits.api.bundle; + +import com.enderio.conduits.api.Conduit; +import net.minecraft.core.Holder; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public sealed interface AddConduitResult + permits AddConduitResult.Upgrade, AddConduitResult.Blocked, AddConduitResult.Insert { + record Upgrade(Holder> replacedConduit) implements AddConduitResult { + @Override + public String toString() { + return "Upgrade[" + replacedConduit.getRegisteredName() + "]"; + } + } + + final class Insert implements AddConduitResult { + @Override + public String toString() { + return "Insert"; + } + } + + final class Blocked implements AddConduitResult { + @Override + public String toString() { + return "Blocked"; + } + } + + default boolean hasChanged() { + return !(this instanceof Blocked); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundleAccessor.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundleAccessor.java new file mode 100644 index 0000000000..e2d5ceb593 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundleAccessor.java @@ -0,0 +1,87 @@ +package com.enderio.conduits.api.bundle; + +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.connection.ConnectionStatus; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.network.node.ConduitNode; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +/** + * Mutable access to a conduit bundle. + */ +@ApiStatus.Experimental +public interface ConduitBundleAccessor extends ConduitBundleReader { + + boolean canAddConduit(Holder> conduit); + + /** + * Attempt to add a conduit to the bundle. + * @param conduit the conduit to add + * @param player the player adding the conduit, or null if performed from another source. + * @return the result of the add operation. + */ + AddConduitResult addConduit(Holder> conduit, @Nullable Direction primaryConnectionSide, + @Nullable Player player); + + /** + * Remove a conduit from the bundle. + * @throws IllegalArgumentException if this conduit is not present (in dev only). + */ + void removeConduit(Holder> conduit, @Nullable Player player); + + /** + * @param conduit the conduit to get the inventory for. + * @return the inventory for this conduit. + */ + ConduitInventory getInventory(Holder> conduit); + + /** + * @throws IllegalArgumentException if the conduit is not present. + * @param conduit the conduit to get a node for. + * @return the conduit node. + */ + ConduitNode getConduitNode(Holder> conduit); + + // region Connections + + /** + * @throws IllegalStateException if {@link #getConnectionStatus} is not {@link ConnectionStatus#CONNECTED_BLOCK}. + * @throws IllegalArgumentException if the connection config is not the right type for this conduit. + * @param side + * @param config + */ + void setConnectionConfig(Direction side, Holder> conduit, ConnectionConfig config); + + /** + * Attempt to connect this conduit something in the given direction. + * @param side the direction to be connected to. + * @param conduit the conduit type that is being connected. + * @param isForcedConnection whether this is a forced connection or automated connection. (Wrench) + * @return whether a new connection was made. + */ + boolean tryConnectTo(Direction side, Holder> conduit, boolean isForcedConnection); + + // endregion + + // region Facades + + /** + * Set the facade provider for this bundle. + * @apiNote The item must have an exposed {@link com.enderio.conduits.api.facade.ConduitFacadeProvider} capability. + * @param providerStack the stack providing the facade. + */ + void setFacadeProvider(ItemStack providerStack); + + /** + * Remove the facade from the bundle. + */ + void clearFacade(); + + // endregion + +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundleReader.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundleReader.java new file mode 100644 index 0000000000..a24d54da49 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitBundleReader.java @@ -0,0 +1,137 @@ +package com.enderio.conduits.api.bundle; + +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.ConduitType; +import com.enderio.conduits.api.connection.ConnectionStatus; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.facade.FacadeType; +import java.util.List; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +/** + * Immutable access to a conduit bundle. + */ +@ApiStatus.Experimental +public interface ConduitBundleReader { + + /** + * @implNote Must be sorted according to {@link com.enderio.conduits.api.ConduitApi#getConduitSortIndex(Holder)} + * @return a list of all conduits in the bundle. + */ + List>> getConduits(); + + /** + * @param conduit the conduit to get data for. + * @return the client data tag, or null if there is none or the conduit doesn't sync extra data. + */ + @Nullable + CompoundTag getConduitExtraWorldData(Holder> conduit); + + // TODO: Docs + @Nullable + CompoundTag getConduitExtraGuiData(Direction side, Holder> conduit); + + /** + * @implNote compare conduits using {@link Conduit#canConnectToConduit(Holder)} + * @param conduit the conduit to check for + * @return whether the bundle has this conduit, or another which is compatible. + */ + boolean hasConduitByType(Holder> conduit); + + // TODO: Docs + boolean hasConduitByType(ConduitType conduitType); + + // TODO: Docs + Holder> getConduitByType(ConduitType conduitType); + + /** + * @param conduit the conduit to check for + * @return whether the bundle has this specific conduit. + */ + boolean hasConduitStrict(Holder> conduit); + + /** + * @return whether the bundle has no conduits and no facade. + */ + boolean isEmpty(); + + /** + * @return whether the bundle has the maximum number of conduits. + */ + boolean isFull(); + + // region Connections + + /** + * @implNote Must be sorted according to {@link com.enderio.conduits.api.ConduitApi#getConduitSortIndex(Holder)} + * @param side the side to check for. + * @return a list of all conduits connected on this side. + */ + List>> getConnectedConduits(Direction side); + + /** + * + * @param side + * @param conduit + * @return + */ + ConnectionStatus getConnectionStatus(Direction side, Holder> conduit); + + /** + * @param side + * @param conduit + * @return + */ + ConnectionConfig getConnectionConfig(Direction side, Holder> conduit); + + /** + * @param side + * @param conduit + * @return + */ + T getConnectionConfig(Direction side, Holder> conduit, + ConnectionConfigType type); + + /** + * An endpoint is a side which has a "connection plate" to another block, rather than to continued line of bundles. + * @param side the side being checked. + * @return whether this side is an endpoint. + */ + boolean isEndpoint(Direction side); + + // endregion + + // region Facades + + /** + * @return whether the bundle has a facade. + */ + boolean hasFacade(); + + /** + * @throws IllegalStateException if {@link #hasFacade} is false. + * @return the block this bundle is mimicing. + */ + Block getFacadeBlock(); + + /** + * @throws IllegalStateException if {@link #hasFacade} is false. + * @return the type of facade this bundle has. + */ + FacadeType getFacadeType(); + + /** + * @return the item providing this bundle's facade. + */ + ItemStack getFacadeProvider(); + + // endregion + +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitInventory.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitInventory.java new file mode 100644 index 0000000000..d1f9a68f9c --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/ConduitInventory.java @@ -0,0 +1,16 @@ +package com.enderio.conduits.api.bundle; + +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.ApiStatus; + +/** + * Represents the inventory of a single conduit in a bundle. + * The inventory is divided into sides for each possible connection. + */ +@ApiStatus.Experimental +public interface ConduitInventory { + ItemStack getStackInSlot(Direction side, SlotType slotType); + + void setStackInSlot(Direction side, SlotType slotType, ItemStack stack); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java new file mode 100644 index 0000000000..76cde35f40 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/bundle/SlotType.java @@ -0,0 +1,19 @@ +package com.enderio.conduits.api.bundle; + +public enum SlotType { + FILTER_EXTRACT, FILTER_INSERT, UPGRADE_EXTRACT; + + public static final int Y_POSITION = 71; + + public int getX() { + return switch (this) { + case FILTER_EXTRACT -> 113; + case FILTER_INSERT -> 23; + case UPGRADE_EXTRACT -> 131; + }; + } + + public int getY() { + return Y_POSITION; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/ConnectionStatus.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/ConnectionStatus.java new file mode 100644 index 0000000000..c32666b877 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/ConnectionStatus.java @@ -0,0 +1,75 @@ +package com.enderio.conduits.api.connection; + +import io.netty.buffer.ByteBuf; +import java.util.function.IntFunction; +import javax.annotation.Nullable; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.util.ByIdMap; +import net.minecraft.util.StringRepresentable; + +public enum ConnectionStatus implements StringRepresentable { + /** + * This conduit is not connected to anything. + */ + DISCONNECTED("none"), + + /** + * This conduit is connected to a block for extract. + */ + CONNECTED_BLOCK("connected_block"), + + /** + * This conduit is connected to another conduit. + */ + CONNECTED_CONDUIT("connected_conduit"), + + /** + * Intentionally disabled by the player, should not automatically reconnect without Yeta Wrench. + */ + DISABLED("disabled"); + + public static final StringRepresentable.EnumCodec CODEC = StringRepresentable + .fromEnum(ConnectionStatus::values); + public static final IntFunction BY_ID = ByIdMap.continuous(Enum::ordinal, values(), + ByIdMap.OutOfBoundsStrategy.ZERO); + public static final StreamCodec STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, + Enum::ordinal); + + private final String name; + + ConnectionStatus(String name) { + this.name = name; + } + + /** + * @return Whether a new connection can be made from this face. + */ + public boolean canConnect() { + return this == DISCONNECTED; + } + + /** + * @return Whether this face is connected. + */ + public boolean isConnected() { + return this != DISCONNECTED && this != DISABLED; + } + + /** + * @return Whether this face is connected to a block. + */ + public boolean isEndpoint() { + return this == CONNECTED_BLOCK; + } + + @Override + public String getSerializedName() { + return name; + } + + @Nullable + public static ConnectionStatus byName(@Nullable String name) { + return CODEC.byName(name); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/ConnectionConfig.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/ConnectionConfig.java new file mode 100644 index 0000000000..7b788e49bb --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/ConnectionConfig.java @@ -0,0 +1,58 @@ +package com.enderio.conduits.api.connection.config; + +import com.enderio.conduits.api.EnderIOConduitsRegistries; +import com.enderio.conduits.api.network.node.legacy.ConduitData; +import com.mojang.serialization.Codec; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import org.apache.commons.lang3.NotImplementedException; +import org.jetbrains.annotations.ApiStatus; + +/** + * Replacement for {@link ConduitData} that is purely focussed on sided connection context. + * Any data stored in the entire node should now be stored in the network, an individual node should not have connectionless context. + */ +@ApiStatus.Experimental +public interface ConnectionConfig { + + Codec GENERIC_CODEC = EnderIOConduitsRegistries.CONDUIT_CONNECTION_CONFIG_TYPE.byNameCodec() + .dispatch(ConnectionConfig::type, ConnectionConfigType::codec); + + StreamCodec STREAM_CODEC = ByteBufCodecs + .registry(EnderIOConduitsRegistries.Keys.CONDUIT_CONNECTION_CONFIG_TYPE) + .dispatch(ConnectionConfig::type, ConnectionConfigType::streamCodec); + + /** + * @return whether the conduit should still be connected with this configuration. + */ + default boolean isConnected() { + return true; + } + + /** + * Modify the config such that isConnected() is true again. + * This will ensure that when the connection is revived, it isn't invalid. + */ + default ConnectionConfig reconnected() { + if (this.isConnected()) { + return this; + } + + throw new NotImplementedException("This connection config type needs to implement reconnected()."); + } + + /** + * Modify the config such that isConnected() is true again. + * This will ensure that when the connection is revived, it isn't invalid. + */ + default ConnectionConfig disconnected() { + if (this.isConnected()) { + return this; + } + + throw new NotImplementedException("This connection config type needs to implement reconnected()."); + } + + ConnectionConfigType type(); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/ConnectionConfigAccessor.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/ConnectionConfigAccessor.java new file mode 100644 index 0000000000..5c35edd0d3 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/ConnectionConfigAccessor.java @@ -0,0 +1,46 @@ +package com.enderio.conduits.api.connection.config; + +import net.minecraft.core.Direction; +import org.jetbrains.annotations.ApiStatus; + +/** + * Provides access to connection configuration. + * Required to handle the potential of a conduit changing type and the data not being applicable anymore. + */ +@ApiStatus.Experimental +public interface ConnectionConfigAccessor { + + /** + * @param side the side to query. + * @return whether there is a connection on the given side. + */ + boolean isConnectedTo(Direction side); + + /** + * Get the connection config for the given side (generic). + * + * @param side the side to query. + * @return the connection config. + * @throws IllegalArgumentException if there is no connection on this side. + */ + ConnectionConfig getConnectionConfig(Direction side); + + /** + * Get the connection config for the given side (specific). + * + * @param side the side to query. + * @param type the type of connection config to get. + * @return the connection config. + * @throws IllegalArgumentException if there is no connection on this side. + * @throws IllegalStateException if the connection type does not match the requested type. + */ + T getConnectionConfig(Direction side, ConnectionConfigType type); + + /** + * Set the connection config for the given side. + * @param side the side to set. + * @param config the config to set. + * @throws IllegalArgumentException if there is no connection on this side or if the connection config is not the right type for this conduit. + */ + void setConnectionConfig(Direction side, ConnectionConfig config); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/ConnectionConfigType.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/ConnectionConfigType.java new file mode 100644 index 0000000000..e53b8237ee --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/ConnectionConfigType.java @@ -0,0 +1,13 @@ +package com.enderio.conduits.api.connection.config; + +import com.mojang.serialization.MapCodec; +import java.util.function.Supplier; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; + +public record ConnectionConfigType(MapCodec codec, + StreamCodec streamCodec, Supplier defaultSupplier) { + public T getDefault() { + return defaultSupplier.get(); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java new file mode 100644 index 0000000000..07660896d1 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/IOConnectionConfig.java @@ -0,0 +1,47 @@ +package com.enderio.conduits.api.connection.config; + +import com.enderio.conduits.api.ConduitRedstoneSignalAware; +import net.minecraft.world.item.DyeColor; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public interface IOConnectionConfig extends ConnectionConfig { + /** + * @return whether the connection is sending resources to the connected block + */ + boolean isSend(); + + /** + * @return whether the connection is receiving resources from the connected block + */ + boolean isReceive(); + + /** + * These colors are used for send separation in the ticker. + * If no channel separation is required, always return the same colour for any connection. + * In this scenario, this is now simply the color of the arrow on the model. + * @return the send color channel. + */ + DyeColor sendColor(); + + /** + * These colors are used for send separation in the ticker. + * If no channel separation is required, always return the same colour for any connection. + * In this scenario, this is now simply the color of the arrow on the model. + * @return the receive color channel. + */ + DyeColor receiveColor(); + + default boolean canSend(ConduitRedstoneSignalAware signalAware) { + return isSend(); + } + + default boolean canReceive(ConduitRedstoneSignalAware signalAware) { + return isReceive(); + } + + @Override + default boolean isConnected() { + return isSend() || isReceive(); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/RedstoneSensitiveConnectionConfig.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/RedstoneSensitiveConnectionConfig.java new file mode 100644 index 0000000000..2323adf577 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/config/RedstoneSensitiveConnectionConfig.java @@ -0,0 +1,19 @@ +package com.enderio.conduits.api.connection.config; + +import java.util.List; +import net.minecraft.world.item.DyeColor; +import org.jetbrains.annotations.ApiStatus; + +/** + * Get the list of redstone signal colors that this connection is sensitive to. + * This is exclusively used for conduit connection rendering. + */ +@ApiStatus.Experimental +public interface RedstoneSensitiveConnectionConfig { + // TODO: Update this when we support 2 redstone colors. + /** + * @apiNote currently the conduit bundle model is only capable of rendering one signal color. In future this will expand to two. + * @return the redstone signal(s) this conduit is sensitive to. + */ + List getRedstoneSignalColors(); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/upgrade/package-info.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/package-info.java similarity index 67% rename from enderio-conduits/src/main/java/com/enderio/conduits/api/upgrade/package-info.java rename to enderio-conduits/src/main/java/com/enderio/conduits/api/connection/package-info.java index 2c67505d3c..270831a1cf 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/upgrade/package-info.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/connection/package-info.java @@ -1,4 +1,4 @@ @javax.annotation.ParametersAreNonnullByDefault @net.minecraft.MethodsReturnNonnullByDefault -package com.enderio.conduits.api.upgrade; +package com.enderio.conduits.api.connection; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitCoreModelModifier.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java similarity index 72% rename from enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitCoreModelModifier.java rename to enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java index 619511c6fd..7356f50ebe 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitCoreModelModifier.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/model/ConduitModelModifier.java @@ -1,23 +1,23 @@ package com.enderio.conduits.api.model; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitNode; +import java.util.List; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.core.Direction; import net.minecraft.core.Holder; +import net.minecraft.nbt.CompoundTag; import net.minecraft.util.RandomSource; import org.jetbrains.annotations.Nullable; -import java.util.List; +public interface ConduitModelModifier { -public interface ConduitCoreModelModifier { /** * Create additional quads to be rendered at the point of conduit connection. */ - default List createConnectionQuads(Holder> conduit, ConduitNode node, @Nullable Direction facing, Direction connectionDirection, RandomSource rand, - @Nullable RenderType type) { + default List createConnectionQuads(Holder> conduit, @Nullable CompoundTag extraWorldData, + @Nullable Direction facing, Direction connectionDirection, RandomSource rand, @Nullable RenderType type) { return List.of(); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/model/RegisterConduitCoreModelModifiersEvent.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/model/RegisterConduitModelModifiersEvent.java similarity index 70% rename from enderio-conduits/src/main/java/com/enderio/conduits/api/model/RegisterConduitCoreModelModifiersEvent.java rename to enderio-conduits/src/main/java/com/enderio/conduits/api/model/RegisterConduitModelModifiersEvent.java index 41366d7c71..b30cef94ca 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/model/RegisterConduitCoreModelModifiersEvent.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/model/RegisterConduitModelModifiersEvent.java @@ -2,20 +2,19 @@ import com.enderio.conduits.api.Conduit; import com.enderio.conduits.api.ConduitType; -import net.neoforged.bus.api.Event; -import net.neoforged.fml.event.IModBusEvent; - import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; -public class RegisterConduitCoreModelModifiersEvent extends Event implements IModBusEvent { +public class RegisterConduitModelModifiersEvent extends Event implements IModBusEvent { public interface ConduitCoreModelModifierFactory { - ConduitCoreModelModifier createModifier(); + ConduitModelModifier createModifier(); } private final Map, ConduitCoreModelModifierFactory> modifiers = new ConcurrentHashMap<>(); - public void register(ConduitType> type, ConduitCoreModelModifierFactory modifierFactory) { + public void register(ConduitType> type, ConduitCoreModelModifierFactory modifierFactory) { modifiers.put(type, modifierFactory); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetwork.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java similarity index 59% rename from enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetwork.java rename to enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java index f27c8edae0..07a3b8dc30 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetwork.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetwork.java @@ -1,5 +1,6 @@ -package com.enderio.conduits.api; +package com.enderio.conduits.api.network; +import com.enderio.conduits.api.network.node.ConduitNode; import java.util.Collection; public interface ConduitNetwork extends ConduitNetworkContextAccessor { diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContext.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java similarity index 79% rename from enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContext.java rename to enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java index 6081744a26..4223933f8e 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContext.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContext.java @@ -1,4 +1,4 @@ -package com.enderio.conduits.api; +package com.enderio.conduits.api.network; public interface ConduitNetworkContext> { T mergeWith(T other); diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContextAccessor.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java similarity index 89% rename from enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContextAccessor.java rename to enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java index db226d2d6c..e873e3aca9 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContextAccessor.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextAccessor.java @@ -1,4 +1,4 @@ -package com.enderio.conduits.api; +package com.enderio.conduits.api.network; import org.jetbrains.annotations.Nullable; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContextType.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java similarity index 61% rename from enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContextType.java rename to enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java index fb64bd1c6f..cff9971e76 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitNetworkContextType.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/ConduitNetworkContextType.java @@ -1,8 +1,9 @@ -package com.enderio.conduits.api; +package com.enderio.conduits.api.network; import com.mojang.serialization.Codec; -import org.jetbrains.annotations.Nullable; - import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; -public record ConduitNetworkContextType>(@Nullable Codec codec, Supplier factory) {} +public record ConduitNetworkContextType>(@Nullable Codec codec, + Supplier factory) { +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java new file mode 100644 index 0000000000..167816853b --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/ConduitNode.java @@ -0,0 +1,46 @@ +package com.enderio.conduits.api.network.node; + +import com.enderio.base.api.filter.ResourceFilter; +import com.enderio.conduits.api.connection.config.ConnectionConfigAccessor; +import com.enderio.conduits.api.network.ConduitNetwork; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.DyeColor; +import org.jetbrains.annotations.Nullable; + +public interface ConduitNode extends NodeDataAccessor, ConnectionConfigAccessor { + // TODO: A better way to determine if a node's bundle is loaded. + + /** + * @throws IllegalStateException if the node is not loaded in the world. + * @return the world position of the node. + */ + BlockPos getPos(); + + @Nullable + ResourceFilter getExtractFilter(Direction direction); + + @Nullable + ResourceFilter getInsertFilter(Direction direction); + + // TODO: investigate nullability for this interface? + @Nullable + ConduitNetwork getNetwork(); + + /** + * @return whether this node's bundle is loaded and ticking in the world + */ + boolean isLoaded(); + + /** + * Check whether there is a redstone signal to this node's bundle. + * @param channelColor + * @return + */ + boolean hasRedstoneSignal(@Nullable DyeColor channelColor); + + /** + * Mark the node as dirty, causing the owning bundle to save and sync. + */ + void markDirty(); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java new file mode 100644 index 0000000000..ba707bc443 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeData.java @@ -0,0 +1,17 @@ +package com.enderio.conduits.api.network.node; + +import com.enderio.conduits.api.EnderIOConduitsRegistries; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.mojang.serialization.Codec; + +/** + * Data which is stored on each conduit node. This is not synced to the client. + * If you want to sync data from here to the client, use {@link com.enderio.conduits.api.Conduit#getClientDataTag(ConduitNode)}. + * For connection-related settings, use {@link ConnectionConfig}. + */ +public interface NodeData { + Codec GENERIC_CODEC = EnderIOConduitsRegistries.CONDUIT_NODE_DATA_TYPE.byNameCodec() + .dispatch(NodeData::type, NodeDataType::codec); + + NodeDataType type(); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java new file mode 100644 index 0000000000..1c368f872a --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataAccessor.java @@ -0,0 +1,40 @@ +package com.enderio.conduits.api.network.node; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Experimental +public interface NodeDataAccessor { + boolean hasNodeData(NodeDataType type); + + /** + * Get the data attached to this node, with no preferred type. + * @return The attached data or null if there is no data. + */ + @Nullable + NodeData getNodeData(); + + /** + * Get the data attached to this node. + * @param type The expected data type. + * @return The attached data or null if there is no data -or- the data is of a different type. + */ + @Nullable + T getNodeData(NodeDataType type); + + /** + * Gets the data attached to this node. + * @param type The data type that is expected. + * @return The stored data that matched this type, or a new instance of the data. + * @implNote If a conduit data of a different type exists in this node, it will be replaced. + */ + T getOrCreateNodeData(NodeDataType type); + + /** + * Set the data attached to this node. + * + * @param data + * @param + */ + void setNodeData(@Nullable T data); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java new file mode 100644 index 0000000000..306ed23816 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/NodeDataType.java @@ -0,0 +1,7 @@ +package com.enderio.conduits.api.network.node; + +import com.mojang.serialization.MapCodec; +import java.util.function.Supplier; + +public record NodeDataType(MapCodec codec, Supplier factory) { +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitData.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java similarity index 50% rename from enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitData.java rename to enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java index 069f2ee3a2..4ca065c88b 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitData.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitData.java @@ -1,27 +1,38 @@ -package com.enderio.conduits.api; +package com.enderio.conduits.api.network.node.legacy; import com.enderio.conduits.api.EnderIOConduitsRegistries; +import com.enderio.conduits.api.network.node.NodeData; import com.mojang.serialization.Codec; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import org.jetbrains.annotations.Nullable; +@Deprecated(forRemoval = true, since = "7.2") public interface ConduitData> { Codec> CODEC = EnderIOConduitsRegistries.CONDUIT_DATA_TYPE.byNameCodec() - .dispatch(ConduitData::type, ConduitDataType::codec); - StreamCodec> STREAM_CODEC = ByteBufCodecs.registry(EnderIOConduitsRegistries.Keys.CONDUIT_DATA_TYPE) - .dispatch(ConduitData::type, ConduitDataType::streamCodec); + .dispatch(ConduitData::type, ConduitDataType::codec); + StreamCodec> STREAM_CODEC = ByteBufCodecs + .registry(EnderIOConduitsRegistries.Keys.CONDUIT_DATA_TYPE) + .dispatch(ConduitData::type, ConduitDataType::streamCodec); /** * Allows copying of data from a client change. * By default allows no changes. */ default T withClientChanges(T guiData) { - //noinspection unchecked - return (T)this; + // noinspection unchecked + return (T) this; } T deepCopy(); ConduitDataType type(); + + /** + * Convert to modern node data. + * @return the new node data, or null if this data should be discarded. + */ + @Nullable + NodeData toNodeData(); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitDataAccessor.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java similarity index 81% rename from enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitDataAccessor.java rename to enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java index dbdad4ee65..661d073b73 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitDataAccessor.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataAccessor.java @@ -1,12 +1,11 @@ -package com.enderio.conduits.api; +package com.enderio.conduits.api.network.node.legacy; import org.jetbrains.annotations.Nullable; +@Deprecated(forRemoval = true, since = "7.2") public interface ConduitDataAccessor { boolean hasData(ConduitDataType type); - // TODO: I want to add setData at some point and maybe enforce conduit data as records? - /** * Get the data attached to this node. * @param type The expected data type. @@ -22,6 +21,4 @@ public interface ConduitDataAccessor { * @implNote If a conduit data of a different type exists in this node, it will be replaced. */ > T getOrCreateData(ConduitDataType type); - - //> T setData(T data); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitDataType.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java similarity index 55% rename from enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitDataType.java rename to enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java index 1532901c93..f6e4212223 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ConduitDataType.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/network/node/legacy/ConduitDataType.java @@ -1,10 +1,11 @@ -package com.enderio.conduits.api; +package com.enderio.conduits.api.network.node.legacy; import com.mojang.serialization.MapCodec; +import java.util.function.Supplier; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; -import java.util.function.Supplier; - -public record ConduitDataType>(MapCodec codec, StreamCodec streamCodec, Supplier factory) { +@Deprecated(forRemoval = true, since = "7.2") +public record ConduitDataType>(MapCodec codec, + StreamCodec streamCodec, Supplier factory) { } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitMenuDataAccess.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitMenuDataAccess.java new file mode 100644 index 0000000000..4b06335e34 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitMenuDataAccess.java @@ -0,0 +1,23 @@ +package com.enderio.conduits.api.screen; + +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import java.util.function.Function; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Experimental +public interface ConduitMenuDataAccess { + Conduit conduit(); + + BlockPos getBlockPos(); + + T getConnectionConfig(); + + void updateConnectionConfig(Function configModifier); + + @Nullable + CompoundTag getExtraGuiData(); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitScreenExtension.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitScreenExtension.java deleted file mode 100644 index 97bb5b9af8..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitScreenExtension.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.enderio.conduits.api.screen; - -import com.enderio.conduits.api.ConduitDataAccessor; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.core.Direction; -import org.joml.Vector2i; - -import java.util.List; -import java.util.function.Supplier; - -/** - * Extend the conduit screen with additional widgets. - */ -public interface ConduitScreenExtension { - - @FunctionalInterface - interface UpdateDispatcher { - void sendUpdate(); - } - - // TODO: Update Javadocs. - /** - * @param conduitDataAccessor the conduit data the widgets are for, manipulate the state of it in the widgets - * @param updateDispatcher call this to modify the conduit data. - * @param direction the supplier to get the current direction for this extendedconduitdata - * @param widgetsStart the position on which widgets start - * @return Widgets that manipulate the extended ConduitData, these changes are synced back to the server - */ - List createWidgets(Screen screen, ConduitDataAccessor conduitDataAccessor, UpdateDispatcher updateDispatcher, - Supplier direction, Vector2i widgetsStart); -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitScreenHelper.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitScreenHelper.java new file mode 100644 index 0000000000..2f6f18b578 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitScreenHelper.java @@ -0,0 +1,46 @@ +package com.enderio.conduits.api.screen; + +import com.enderio.base.api.misc.RedstoneControl; +import java.util.function.Consumer; +import java.util.function.Supplier; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Renderable; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.DyeColor; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public interface ConduitScreenHelper { + + // Built-in widget support + AbstractWidget addCheckbox(int x, int y, Supplier getter, Consumer setter); + + AbstractWidget addColorPicker(int x, int y, Component title, Supplier getter, Consumer setter); + + AbstractWidget addRedstoneControlPicker(int x, int y, Component title, Supplier getter, + Consumer setter); + + AbstractWidget addToggleButton(int x, int y, int width, int height, Component enabledTitle, Component disabledTitle, + ResourceLocation enabledSprite, ResourceLocation disabledSprite, Supplier getter, + Consumer setter); + + // TODO: Create icon button + + // TODO: Create custom picker? + + // Dynamic UI utilities + void addPreRenderAction(Runnable runnable); + + // Custom widget support + + W addRenderableWidget(W widget); + + W addRenderableOnly(W renderable); + + W addWidget(W listener); + + void removeWidget(GuiEventListener listener); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitScreenType.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitScreenType.java new file mode 100644 index 0000000000..4ab7cec1a1 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/ConduitScreenType.java @@ -0,0 +1,43 @@ +package com.enderio.conduits.api.screen; + +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public abstract class ConduitScreenType { + // Used for startX, startY + private static final int USABLE_START_X = 22; + private static final int USABLE_START_Y = 7; + + protected static final int SLOT_SIZE = 16; + protected static final int PADDED_SLOT_SIZE = SLOT_SIZE + 2; + + // All of these are estimates and will change after the GUI rewrite. + // With any luck, most UIs will just work if they rely upon these values. + protected static final int WIDTH = 162; + protected static final int HEIGHT = 100; + + @ApiStatus.Internal + public void createScreenWidgets(ConduitScreenHelper screen, int guiLeft, int guiTop, ConduitMenuDataAccess dataAccess) { + createWidgets(screen, guiLeft + USABLE_START_X, guiTop + USABLE_START_Y, dataAccess); + } + + @ApiStatus.Internal + public void renderScreenLabels(GuiGraphics guiGraphics, Font font, int mouseX, int mouseY) { + renderLabels(guiGraphics, USABLE_START_X, USABLE_START_Y, font, mouseX, mouseY); + } + + protected abstract void createWidgets(ConduitScreenHelper screen, int startX, int startY, ConduitMenuDataAccess dataAccess); + + /** + * Already projected into gui space (guiLeft & guiRight in Screen), so only local transformations required. + * @param guiGraphics + * @param font + * @param mouseX + * @param mouseY + */ + protected void renderLabels(GuiGraphics guiGraphics, int startX, int startY, Font font, int mouseX, int mouseY) { + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java new file mode 100644 index 0000000000..d845fcee08 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/IOConduitScreenType.java @@ -0,0 +1,77 @@ +package com.enderio.conduits.api.screen; + +import com.enderio.conduits.api.connection.config.IOConnectionConfig; +import com.enderio.conduits.client.gui.screen.ConduitScreen; +import com.enderio.conduits.common.init.ConduitLang; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.network.chat.Component; +import org.jetbrains.annotations.ApiStatus; + +/** + * Default implementation of a conduit screen type for IO connections. + * Adds checkboxes and sided titles. + */ +@ApiStatus.Experimental +public abstract class IOConduitScreenType extends ConduitScreenType { + + // Rows of 9, counts slot width and it's outline edge. + // TODO: Better name. + protected static final int RIGHT_START_X = PADDED_SLOT_SIZE * 5; + + // Titles default to perfect position to be alongside a checkbox. + protected int leftTitleX = PADDED_SLOT_SIZE; + protected int leftTitleY = 4; + protected Component leftTitle = ConduitLang.CONDUIT_INSERT; + + protected int rightTitleX = RIGHT_START_X + PADDED_SLOT_SIZE; + protected int rightTitleY = 4; + protected Component rightTitle = ConduitLang.CONDUIT_EXTRACT; + + @Override + protected void createWidgets(ConduitScreenHelper screen, int startX, int startY, ConduitMenuDataAccess dataAccess) { + createLeftWidgets(screen, startX, startY, dataAccess); + createRightWidgets(screen, startX + RIGHT_START_X, startY, dataAccess); + + // TODO: *could* implement sanity checks to ensure widgets are not outside their bounds? Might be nice static-time check, even if we only do it in dev envs? + } + + public void createLeftWidgets(ConduitScreenHelper screen, int startX, int startY, ConduitMenuDataAccess dataAccess) { + screen.addCheckbox(startX, startY, () -> getLeftEnabled(dataAccess.getConnectionConfig()), + value -> dataAccess.updateConnectionConfig(config -> setLeftEnabled(config, value))); + } + + public void createRightWidgets(ConduitScreenHelper screen, int startX, int startY, ConduitMenuDataAccess dataAccess) { + screen.addCheckbox(startX, startY, () -> getRightEnabled(dataAccess.getConnectionConfig()), + value -> dataAccess.updateConnectionConfig(config -> setRightEnabled(config, value))); + } + + public boolean getLeftEnabled(U config) { + return config.isSend(); + } + + public boolean getRightEnabled(U config) { + return config.isReceive(); + } + + /** + * Unless you changed the order of the titles, this should edit isSend. + */ + protected abstract U setLeftEnabled(U config, boolean isEnabled); + + /** + * Unless you changed the order of the titles, this should edit isReceive. + */ + protected abstract U setRightEnabled(U config, boolean isEnabled); + + @Override + public void renderLabels(GuiGraphics guiGraphics, int startX, int startY, Font font, int mouseX, int mouseY) { + super.renderLabels(guiGraphics, startX, startY, font, mouseX, mouseY); + + // TODO: This should be a sprite. + guiGraphics.blit(ConduitScreen.TEXTURE, startX + (WIDTH / 2), startY, 255, 0, 1, 97); + + guiGraphics.drawString(font, leftTitle, startX + leftTitleX, startY + leftTitleY, 4210752, false); + guiGraphics.drawString(font, rightTitle, startX + rightTitleX, startY + rightTitleY, 4210752, false); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/RegisterConduitScreenExtensionsEvent.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/RegisterConduitScreenExtensionsEvent.java deleted file mode 100644 index ac58086bf1..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/RegisterConduitScreenExtensionsEvent.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.enderio.conduits.api.screen; - -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitType; -import net.neoforged.bus.api.Event; -import net.neoforged.fml.event.IModBusEvent; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class RegisterConduitScreenExtensionsEvent extends Event implements IModBusEvent { - public interface ConduitScreenExtensionFactory { - ConduitScreenExtension createExtension(); - } - - private final Map, ConduitScreenExtensionFactory> extensions = new ConcurrentHashMap<>(); - - public void register(ConduitType> conduitType, ConduitScreenExtensionFactory extensionFactory) { - extensions.put(conduitType, extensionFactory); - } - - public Map, ConduitScreenExtensionFactory> getExtensions() { - return Map.copyOf(extensions); - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/RegisterConduitScreenTypesEvent.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/RegisterConduitScreenTypesEvent.java new file mode 100644 index 0000000000..dd57aa46c0 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/screen/RegisterConduitScreenTypesEvent.java @@ -0,0 +1,20 @@ +package com.enderio.conduits.api.screen; + +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.ConduitType; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.IModBusEvent; + +public class RegisterConduitScreenTypesEvent extends Event implements IModBusEvent { + private final Map, ConduitScreenType> screenTypes = new ConcurrentHashMap<>(); + + public void register(ConduitType> conduitType, ConduitScreenType screenType) { + screenTypes.put(conduitType, screenType); + } + + public Map, ConduitScreenType> getScreenTypes() { + return Map.copyOf(screenTypes); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/CapabilityAwareConduitTicker.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/CapabilityAwareConduitTicker.java deleted file mode 100644 index 4c405d7b21..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/CapabilityAwareConduitTicker.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.enderio.conduits.api.ticker; - -import com.enderio.conduits.api.ColoredRedstoneProvider; -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNode; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.item.DyeColor; -import net.minecraft.world.level.Level; -import net.neoforged.neoforge.capabilities.BlockCapability; - -import java.util.ArrayList; -import java.util.List; - -public abstract class CapabilityAwareConduitTicker, TCap> implements IOAwareConduitTicker { - - @Override - public final void tickColoredGraph(ServerLevel level, TConduit conduit, List inserts, List extracts, - DyeColor color, ConduitNetwork graph, ColoredRedstoneProvider coloredRedstoneProvider) { - - List insertCaps = new ArrayList<>(); - for (Connection insert : inserts) { - // TODO: we should have bundle block entities cache neighbour capabilities... - TCap capability = level.getCapability(getCapability(), insert.move(), insert.direction().getOpposite()); - if (capability != null) { - insertCaps.add(new CapabilityConnection(insert.direction(), insert.node(), capability)); - } - } - - if (!insertCaps.isEmpty()) { - List extractCaps = new ArrayList<>(); - - for (Connection extract : extracts) { - TCap capability = level.getCapability(getCapability(), extract.move(), extract.direction().getOpposite()); - if (capability != null) { - extractCaps.add(new CapabilityConnection(extract.direction(), extract.node(), capability)); - } - } - - if (!extractCaps.isEmpty()) { - tickCapabilityGraph(level, conduit, insertCaps, extractCaps, graph, coloredRedstoneProvider); - } - } - } - - @Override - public boolean canConnectTo(Level level, BlockPos conduitPos, Direction direction) { - TCap capability = level.getCapability(getCapability(), conduitPos.relative(direction), direction.getOpposite()); - return capability != null; - } - - protected abstract void tickCapabilityGraph(ServerLevel level, TConduit conduit, List inserts, - List extracts, ConduitNetwork graph, ColoredRedstoneProvider coloredRedstoneProvider); - - protected abstract BlockCapability getCapability(); - - public class CapabilityConnection extends Connection { - private final TCap capability; - - public CapabilityConnection(Direction dir, ConduitNode node, TCap capability) { - super(dir, node); - this.capability = capability; - } - - public TCap capability() { - return this.capability; - } - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java index c0d86d8252..1a4209c678 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/ConduitTicker.java @@ -2,26 +2,10 @@ import com.enderio.conduits.api.ColoredRedstoneProvider; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitNetwork; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; +import com.enderio.conduits.api.network.ConduitNetwork; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; - -public interface ConduitTicker> { +public interface ConduitTicker> { void tickGraph(ServerLevel level, TConduit conduit, ConduitNetwork graph, ColoredRedstoneProvider coloredRedstoneProvider); - - /** - * @return Whether the conduit can interact with the block in this direction - */ - boolean canConnectTo(Level level, BlockPos conduitPos, Direction direction); - - /** - * @return if the conduit is allowed to have a forced connection (with the wrench) but won't necessarily connect when placed - */ - default boolean canForceConnectTo(Level level, BlockPos conduitPos, Direction direction) { - return canConnectTo(level, conduitPos, direction); - } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java index c9c4c8392d..48b96791a1 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/IOAwareConduitTicker.java @@ -1,102 +1,107 @@ package com.enderio.conduits.api.ticker; import com.enderio.base.api.filter.ResourceFilter; -import com.enderio.base.api.misc.RedstoneControl; import com.enderio.conduits.api.ColoredRedstoneProvider; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNode; -import com.enderio.conduits.api.upgrade.ConduitUpgrade; +import com.enderio.conduits.api.connection.config.IOConnectionConfig; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.DyeColor; +import net.minecraft.world.level.Level; import org.jetbrains.annotations.Nullable; -import java.util.List; +/** + * A channel IO-aware ticker. + * + * This will check {@link ConduitNode#isActive(Direction)} for extraction connections to ensure it has a redstone signal. + * + * @param The conduit type + * @param The type of connection for the ticker implementation + */ +public abstract class IOAwareConduitTicker, U extends IOConnectionConfig, V extends IOAwareConduitTicker.SimpleConnection> + implements ConduitTicker { -public interface IOAwareConduitTicker> extends LoadedAwareConduitTicker { @Override - default void tickGraph(ServerLevel level, TConduit conduit, List loadedNodes, ConduitNetwork graph, - ColoredRedstoneProvider coloredRedstoneProvider) { - - ListMultimap extracts = ArrayListMultimap.create(); - ListMultimap inserts = ArrayListMultimap.create(); - for (ConduitNode node : loadedNodes) { - for (Direction direction : Direction.values()) { - node.getIOState(direction).ifPresent(ioState -> { - ioState - .extract() - .filter(extract -> isRedstoneMode(conduit, level, node.getPos(), ioState, coloredRedstoneProvider)) - .ifPresent(color -> extracts.get(color).add(new Connection(direction, node))); - ioState - .insert() - .ifPresent(color -> inserts.get(color).add(new Connection(direction, node))); - }); + public void tickGraph(ServerLevel level, T conduit, ConduitNetwork graph, + ColoredRedstoneProvider coloredRedstoneProvider) { + ListMultimap senders = ArrayListMultimap.create(); + ListMultimap receivers = ArrayListMultimap.create(); + + for (ConduitNode node : graph.getNodes()) { + // Ensure the node is loaded + if (!node.isLoaded()) { + continue; + } + + for (Direction side : Direction.values()) { + if (node.isConnectedTo(side)) { + var config = node.getConnectionConfig(side, conduit.connectionConfigType()); + + if (canSend(node, config)) { + var connection = createConnection(level, node, side); + if (connection != null) { + senders.get(config.sendColor()).add(connection); + } + } + + if (canReceive(node, config)) { + var connection = createConnection(level, node, side); + if (connection != null) { + receivers.get(config.receiveColor()).add(connection); + } + } + } } } + for (DyeColor color : DyeColor.values()) { - List extractList = extracts.get(color); - List insertList = inserts.get(color); - if (shouldSkipColor(extractList, insertList)) { + List colorSenders = senders.get(color); + List colorReceivers = receivers.get(color); + if (shouldSkipColor(colorSenders, colorReceivers)) { continue; } - tickColoredGraph(level, conduit, insertList, extractList, color, graph, coloredRedstoneProvider); + tickColoredGraph(level, conduit, colorSenders, colorReceivers, color, graph, coloredRedstoneProvider); } } - default boolean shouldSkipColor(List extractList, List insertList) { - return extractList.isEmpty() || insertList.isEmpty(); + protected boolean canSend(ConduitNode node, U config) { + return config.canSend(node::hasRedstoneSignal); } - void tickColoredGraph( - ServerLevel level, - TConduit conduit, - List inserts, - List extracts, - DyeColor color, - ConduitNetwork graph, - ColoredRedstoneProvider coloredRedstoneProvider); - - default boolean isRedstoneMode(TConduit conduit, ServerLevel level, BlockPos pos, ConduitNode.IOState state, - ColoredRedstoneProvider coloredRedstoneProvider) { - if (!conduit.getMenuData().showRedstoneExtract()) { - return true; - } + protected boolean canReceive(ConduitNode node, U config) { + return config.canReceive(node::hasRedstoneSignal); + } - if (state.control() == RedstoneControl.ALWAYS_ACTIVE) { - return true; - } + protected boolean shouldSkipColor(List senders, List receivers) { + return senders.isEmpty() || receivers.isEmpty(); + } - if (state.control() == RedstoneControl.NEVER_ACTIVE) { - return false; - } + protected void preProcessReceivers(List receivers) { + // Could implement a pre-sort here. + } - boolean hasRedstone = false; - for (Direction direction : Direction.values()) { - if (level.getSignal(pos.relative(direction), direction) > 0) { - hasRedstone = true; - break; - } - } + @Nullable + protected abstract V createConnection(Level level, ConduitNode node, Direction side); - return state.control().isActive(hasRedstone || coloredRedstoneProvider.isRedstoneActive(level, pos, state.redstoneChannel())); - } + protected abstract void tickColoredGraph(ServerLevel level, T conduit, List senders, List receivers, + DyeColor color, ConduitNetwork graph, ColoredRedstoneProvider coloredRedstoneProvider); - class Connection { - private final Direction direction; + public static class SimpleConnection { private final ConduitNode node; + private final Direction side; + private final T config; - public Connection(Direction direction, ConduitNode node) { - this.direction = direction; + public SimpleConnection(ConduitNode node, Direction side, T config) { this.node = node; - } - - public Direction direction() { - return direction; + this.side = side; + this.config = config; } public ConduitNode node() { @@ -107,23 +112,30 @@ public BlockPos pos() { return node.getPos(); } - public BlockPos move() { - return pos().relative(direction); + public Direction side() { + return side; } - @Nullable - public ConduitUpgrade upgrade() { - return node.getUpgrade(direction); + public T config() { + return config; } - @Nullable - public ResourceFilter extractFilter() { - return node.getExtractFilter(direction); + public BlockPos neighborPos() { + return pos().relative(side); + } + + public Direction neighborSide() { + return side.getOpposite(); } @Nullable public ResourceFilter insertFilter() { - return node.getInsertFilter(direction); + return node.getInsertFilter(side); + } + + @Nullable + public ResourceFilter extractFilter() { + return node.getExtractFilter(side); } } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java index 88dcffa039..b75ffa9092 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/api/ticker/LoadedAwareConduitTicker.java @@ -2,30 +2,21 @@ import com.enderio.conduits.api.ColoredRedstoneProvider; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNode; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; - +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; import java.util.List; +import net.minecraft.server.level.ServerLevel; -public interface LoadedAwareConduitTicker> extends ConduitTicker { +public interface LoadedAwareConduitTicker> extends ConduitTicker { @Override - default void tickGraph(ServerLevel level, TConduit conduit, ConduitNetwork graph, ColoredRedstoneProvider coloredRedstoneProvider) { - List nodeIdentifiers = graph.getNodes() - .stream().filter(node -> isLoaded(level, node.getPos())) - .toList(); + default void tickGraph(ServerLevel level, TConduit conduit, ConduitNetwork graph, + ColoredRedstoneProvider coloredRedstoneProvider) { + List nodeIdentifiers = graph.getNodes().stream().filter(ConduitNode::isLoaded).toList(); tickGraph(level, conduit, nodeIdentifiers, graph, coloredRedstoneProvider); } - void tickGraph(ServerLevel level, TConduit type, - List loadedNodes, ConduitNetwork graph, - ColoredRedstoneProvider coloredRedstoneProvider); - - default boolean isLoaded(Level level, BlockPos pos) { - return level.isLoaded(pos) && level.shouldTickBlocksAt(pos); - } + void tickGraph(ServerLevel level, TConduit type, List loadedNodes, ConduitNetwork graph, + ColoredRedstoneProvider coloredRedstoneProvider); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/api/upgrade/ConduitUpgrade.java b/enderio-conduits/src/main/java/com/enderio/conduits/api/upgrade/ConduitUpgrade.java deleted file mode 100644 index 469e2df96f..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/api/upgrade/ConduitUpgrade.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.enderio.conduits.api.upgrade; - -public interface ConduitUpgrade { -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitBundleExtension.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitBundleExtension.java new file mode 100644 index 0000000000..eadf91a91f --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitBundleExtension.java @@ -0,0 +1,58 @@ +package com.enderio.conduits.client; + +import com.enderio.conduits.api.bundle.ConduitBundleReader; +import com.enderio.conduits.client.model.conduit.facades.FacadeHelper; +import com.enderio.conduits.client.particle.ConduitBreakParticle; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlockEntity; +import net.minecraft.client.particle.ParticleEngine; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.neoforged.neoforge.client.extensions.common.IClientBlockExtensions; + +public class ConduitBundleExtension implements IClientBlockExtensions { + + public static ConduitBundleExtension INSTANCE = new ConduitBundleExtension(); + + private ConduitBundleExtension() { + } + + @Override + public boolean addHitEffects(BlockState state, Level level, HitResult target, ParticleEngine manager) { + if (!(target instanceof BlockHitResult blockHitResult)) { + return false; + } + + if (level.getBlockEntity(blockHitResult.getBlockPos()) instanceof ConduitBundleBlockEntity conduitBundle) { + // TODO: Get the conduit texture and add it to the particle manager. + + if (conduitBundle.hasFacade() && FacadeHelper.areFacadesVisible()) { + return false; + } + + var conduit = conduitBundle.getShape().getConduit(blockHitResult.getBlockPos(), target); + if (conduit != null) { + ConduitBreakParticle.addCrackEffects(blockHitResult.getBlockPos(), state, conduit.value(), + blockHitResult.getDirection()); + } + + return true; + } + + return false; + } + + @Override + public boolean addDestroyEffects(BlockState state, Level level, BlockPos pos, ParticleEngine manager) { + // Use vanilla particles if we have a visible facade. + // Conduit break particles are handled by the BE. + if (level.getBlockEntity(pos) instanceof ConduitBundleReader conduitBundle) { + return !(conduitBundle.hasFacade() && FacadeHelper.areFacadesVisible()); + } + + // Not a bundle + return false; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitClientSetup.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitClientSetup.java index 31110a3df1..b3a04e35c0 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitClientSetup.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitClientSetup.java @@ -2,16 +2,19 @@ import com.enderio.base.api.EnderIO; import com.enderio.conduits.EnderIOConduits; -import com.enderio.conduits.api.model.RegisterConduitCoreModelModifiersEvent; -import com.enderio.conduits.api.screen.RegisterConduitScreenExtensionsEvent; -import com.enderio.conduits.client.gui.conduit.ConduitScreenExtensions; -import com.enderio.conduits.client.gui.conduit.FluidConduitScreenExtension; -import com.enderio.conduits.client.gui.conduit.ItemConduitScreenExtension; -import com.enderio.conduits.client.model.ConduitGeometry; -import com.enderio.conduits.client.model.ConduitItemModelLoader; -import com.enderio.conduits.client.model.FacadeItemGeometry; -import com.enderio.conduits.client.model.conduit.modifier.ConduitCoreModelModifiers; -import com.enderio.conduits.client.model.conduit.modifier.FluidConduitCoreModelModifier; +import com.enderio.conduits.api.model.RegisterConduitModelModifiersEvent; +import com.enderio.conduits.api.screen.RegisterConduitScreenTypesEvent; +import com.enderio.conduits.client.gui.screen.types.ConduitScreenTypes; +import com.enderio.conduits.client.gui.screen.types.EnergyConduitScreenType; +import com.enderio.conduits.client.gui.screen.types.FluidConduitScreenType; +import com.enderio.conduits.client.gui.screen.types.ItemConduitScreenType; +import com.enderio.conduits.client.gui.screen.types.RedstoneConduitScreenType; +import com.enderio.conduits.client.model.conduit.ConduitItemModelLoader; +import com.enderio.conduits.client.model.conduit.bundle.ConduitBundleGeometry; +import com.enderio.conduits.client.model.conduit.facades.FacadeItemGeometry; +import com.enderio.conduits.client.model.conduit.modifier.ConduitModelModifiers; +import com.enderio.conduits.client.model.conduit.modifier.FluidConduitModelModifier; +import com.enderio.conduits.common.init.ConduitBlocks; import com.enderio.conduits.common.init.ConduitTypes; import java.util.ArrayList; import java.util.HashMap; @@ -24,6 +27,7 @@ import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.neoforge.client.event.ModelEvent; +import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent; @EventBusSubscriber(modid = EnderIOConduits.MODULE_MOD_ID, value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD) public class ConduitClientSetup { @@ -47,23 +51,25 @@ private ConduitClientSetup() { @SubscribeEvent public static void clientSetup(FMLClientSetupEvent event) { - ConduitScreenExtensions.init(); + ConduitScreenTypes.init(); } @SubscribeEvent - public static void registerConduitCoreModelModifiers(RegisterConduitCoreModelModifiersEvent event) { - event.register(ConduitTypes.FLUID.get(), FluidConduitCoreModelModifier::new); + public static void registerConduitCoreModelModifiers(RegisterConduitModelModifiersEvent event) { + event.register(ConduitTypes.FLUID.get(), FluidConduitModelModifier::new); } @SubscribeEvent - public static void registerConduitScreenExtensions(RegisterConduitScreenExtensionsEvent event) { - event.register(ConduitTypes.FLUID.get(), FluidConduitScreenExtension::new); - event.register(ConduitTypes.ITEM.get(), ItemConduitScreenExtension::new); + public static void registerConduitScreenTypes(RegisterConduitScreenTypesEvent event) { + event.register(ConduitTypes.ENERGY.get(), new EnergyConduitScreenType()); + event.register(ConduitTypes.FLUID.get(), new FluidConduitScreenType()); + event.register(ConduitTypes.REDSTONE.get(), new RedstoneConduitScreenType()); + event.register(ConduitTypes.ITEM.get(), new ItemConduitScreenType()); } @SubscribeEvent public static void modelLoader(ModelEvent.RegisterGeometryLoaders event) { - event.register(EnderIO.loc("conduit"), new ConduitGeometry.Loader()); + event.register(EnderIO.loc("conduit"), new ConduitBundleGeometry.Loader()); event.register(EnderIO.loc("conduit_item"), new ConduitItemModelLoader()); event.register(EnderIO.loc("facades_item"), new FacadeItemGeometry.Loader()); } @@ -75,8 +81,8 @@ public static void registerModels(ModelEvent.RegisterAdditional event) { } // Ensure conduit model modifiers are ready, then load all model dependencies. - ConduitCoreModelModifiers.init(); - ConduitCoreModelModifiers.getAllModelDependencies().forEach(event::register); + ConduitModelModifiers.init(); + ConduitModelModifiers.getAllModelDependencies().forEach(event::register); } @SubscribeEvent @@ -86,6 +92,11 @@ public static void bakingModelsFinished(ModelEvent.BakingCompleted event) { } } + @SubscribeEvent + public static void registerClientExtensions(RegisterClientExtensionsEvent event) { + event.registerBlock(ConduitBundleExtension.INSTANCE, ConduitBlocks.CONDUIT); + } + private static ModelResourceLocation loc(String modelName) { ModelResourceLocation loc = ModelResourceLocation.standalone(EnderIO.loc(modelName)); MODEL_LOCATIONS.add(loc); diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitFacadeColor.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitFacadeColor.java index cb5dd95400..6bdc8fece6 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitFacadeColor.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitFacadeColor.java @@ -2,8 +2,7 @@ import com.enderio.base.common.init.EIODataComponents; import com.enderio.conduits.client.model.conduit.facades.FacadeHelper; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity; -import java.util.Optional; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlockEntity; import net.minecraft.client.Minecraft; import net.minecraft.client.color.block.BlockColor; import net.minecraft.client.color.item.ItemColor; @@ -11,7 +10,6 @@ import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; @@ -27,14 +25,16 @@ public int getColor(BlockState state, @Nullable BlockAndTintGetter level, @Nulla BlockEntity entity = level.getBlockEntity(pos); if (entity instanceof ConduitBundleBlockEntity conduitBundleBlock) { - Optional facade = conduitBundleBlock.getBundle().facade(); + if (conduitBundleBlock.hasFacade()) { + var facade = conduitBundleBlock.getFacadeBlock(); - if (facade.isPresent() && FacadeHelper.areFacadesVisible()) { - int color = Minecraft.getInstance() - .getBlockColors() - .getColor(facade.get().defaultBlockState(), level, pos, tintIndex); - if (color != -1) { - return color; + if (FacadeHelper.areFacadesVisible()) { + int color = Minecraft.getInstance() + .getBlockColors() + .getColor(facade.defaultBlockState(), level, pos, tintIndex); + if (color != -1) { + return color; + } } } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitFacadeRendering.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitFacadeRendering.java index 1c54c968cc..ee5415d84b 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitFacadeRendering.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitFacadeRendering.java @@ -1,8 +1,8 @@ package com.enderio.conduits.client; import com.enderio.conduits.client.model.conduit.facades.FacadeHelper; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlock; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlock; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlockEntity; import com.mojang.blaze3d.vertex.VertexConsumer; import java.util.Map; import net.minecraft.client.Minecraft; @@ -30,6 +30,7 @@ static void renderFacade(RenderLevelStageEvent event) { if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_TRIPWIRE_BLOCKS || FacadeHelper.areFacadesVisible()) { return; } + for (Map.Entry entry : ConduitBundleBlockEntity.FACADES.entrySet()) { ClientLevel level = Minecraft.getInstance().level; if (!level.isLoaded(entry.getKey())) { diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitHighlightEvent.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitHighlightEvent.java index 010e327146..ecdec9e2c4 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitHighlightEvent.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/ConduitHighlightEvent.java @@ -2,7 +2,7 @@ import com.enderio.conduits.EnderIOConduits; import com.enderio.conduits.client.model.conduit.facades.FacadeHelper; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlockEntity; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.RenderType; @@ -27,7 +27,7 @@ public static void highlight(RenderHighlightEvent.Block event) { if (minecraft.level .getBlockEntity(event.getTarget().getBlockPos()) instanceof ConduitBundleBlockEntity conduit) { // Use standard block highlights for facades. - if (conduit.getBundle().hasFacade() && FacadeHelper.areFacadesVisible()) { + if (conduit.hasFacade() && FacadeHelper.areFacadesVisible()) { return; } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/ConduitScreen.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/ConduitScreen.java deleted file mode 100644 index 200f1bf6b3..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/ConduitScreen.java +++ /dev/null @@ -1,252 +0,0 @@ -package com.enderio.conduits.client.gui; - -import com.enderio.base.api.EnderIO; -import com.enderio.conduits.api.ConduitDataAccessor; -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitData; -import com.enderio.conduits.api.ConduitDataType; -import com.enderio.conduits.api.ConduitMenuData; -import com.enderio.conduits.api.SlotType; -import com.enderio.conduits.api.screen.ConduitScreenExtension; -import com.enderio.base.client.gui.widget.DyeColorPickerWidget; -import com.enderio.base.client.gui.widget.RedstoneControlPickerWidget; -import com.enderio.conduits.client.gui.conduit.ConduitScreenExtensions; -import com.enderio.conduits.common.conduit.connection.ConnectionState; -import com.enderio.conduits.common.conduit.connection.DynamicConnectionState; -import com.enderio.base.api.misc.RedstoneControl; -import com.enderio.base.common.lang.EIOLang; -import com.enderio.conduits.common.conduit.ConduitBundle; -import com.enderio.conduits.common.conduit.ConduitGraphObject; -import com.enderio.conduits.common.init.ConduitLang; -import com.enderio.conduits.common.menu.ConduitMenu; -import com.enderio.conduits.common.network.C2SSetConduitConnectionState; -import com.enderio.conduits.common.network.C2SSetConduitExtendedData; -import com.enderio.core.client.gui.screen.EnderContainerScreen; -import com.enderio.core.client.gui.widgets.ToggleIconButton; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.core.Holder; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.item.DyeColor; -import net.neoforged.neoforge.network.PacketDistributor; -import org.jetbrains.annotations.Nullable; -import org.joml.Vector2i; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -public class ConduitScreen extends EnderContainerScreen { - - public static final ResourceLocation TEXTURE = EnderIO.loc("textures/gui/conduit.png"); - private static final int WIDTH = 206; - private static final int HEIGHT = 195; - - private final ClientConduitDataAccessor conduitDataAccessor = new ClientConduitDataAccessor(); - - public ConduitScreen(ConduitMenu pMenu, Inventory pPlayerInventory, Component title) { - super(pMenu, pPlayerInventory, title); - - this.imageWidth = WIDTH; - this.imageHeight = HEIGHT; - } - - @Override - public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { - //close and don't render if someone removed the conduit we are looking at or similar - if (!menu.stillValid(minecraft.player)) { - minecraft.player.closeContainer(); - } else { - super.render(guiGraphics, mouseX, mouseY, partialTicks); - } - } - - @Override - protected void renderBg(GuiGraphics guiGraphics, float partialTicks, int mouseX, int mouseY) { - guiGraphics.blit(TEXTURE, getGuiLeft(), getGuiTop(), 0, 0, imageWidth, imageHeight); - - ConduitMenuData data = getMenuData(); - guiGraphics.pose().pushPose(); - guiGraphics.pose().translate(getGuiLeft(), getGuiTop(), 0); - - if (data.showBarSeparator()) { - guiGraphics.blit(TEXTURE, 102, 7, 255, 0, 1, 97); - } - - for (SlotType type: SlotType.values()) { - if (type.isAvailableFor(data)) { - guiGraphics.blit(TEXTURE, type.getX()-1, type.getY()-1, 206, 0, 18, 18); - } - } - - guiGraphics.pose().popPose(); - } - - @Override - protected void renderLabels(GuiGraphics guiGraphics, int pMouseX, int pMouseY) { - ConduitMenuData data = getMenuData(); - - guiGraphics.drawString(this.font, ConduitLang.CONDUIT_INSERT, 22 + 16 + 2, 7 + 4, 4210752, false); - - if (data.showBothEnable()) { - guiGraphics.drawString(this.font, ConduitLang.CONDUIT_EXTRACT, 112 + 16 + 2, 7 + 4, 4210752, false); - } - - super.renderLabels(guiGraphics, pMouseX, pMouseY); - } - - @Override - protected void init() { - super.init(); - ConduitMenuData data = getMenuData(); - Vector2i pos = new Vector2i(22, 7).add(getGuiLeft(), getGuiTop()); - - addRenderableWidget( - ToggleIconButton.createCheckbox(pos.x(), pos.y(), - () -> getOnDynamic(dyn -> dyn.isInsert(), false), - bool -> actOnDynamic(dyn -> dyn.withEnabled(false, bool)))); - - if (data.showBothEnable()) { - addRenderableWidget( - ToggleIconButton.createCheckbox(pos.x() + 90, pos.y(), - () -> getOnDynamic(dyn -> dyn.isExtract(), false), - bool -> actOnDynamic(dyn -> dyn.withEnabled(true, bool)))); - } - - if (data.showColorInsert()) { - addRenderableWidget( - new DyeColorPickerWidget(pos.x(), pos.y() + 20, - () -> getOnDynamic(dyn -> dyn.insertChannel(), DyeColor.GREEN), - color -> actOnDynamic(dyn -> dyn.withColor(false, color)), - EIOLang.CONDUIT_CHANNEL)); - } - - if (data.showColorExtract()) { - addRenderableWidget( - new DyeColorPickerWidget(pos.x() + 90, pos.y() + 20, - () -> getOnDynamic(dyn -> dyn.extractChannel(), DyeColor.GREEN), - color -> actOnDynamic(dyn -> dyn.withColor(true, color)), - EIOLang.CONDUIT_CHANNEL)); - } - - if (data.showRedstoneExtract()) { - addRenderableWidget( - new RedstoneControlPickerWidget(pos.x() + 90, pos.y() + 40, - () -> getOnDynamic(dyn -> dyn.control(), RedstoneControl.ACTIVE_WITH_SIGNAL), - mode -> actOnDynamic(dyn -> dyn.withRedstoneMode(mode)), - EIOLang.REDSTONE_MODE)); - - addRenderableWidget( - new DyeColorPickerWidget(pos.x() + 90 + 20, pos.y() + 40, - () -> getOnDynamic(dyn -> dyn.redstoneChannel(), DyeColor.GREEN), - color -> actOnDynamic(dyn -> dyn.withRedstoneChannel(color)), - EIOLang.REDSTONE_CHANNEL)); - } - - addConduitScreenExtensionWidgets(); - addConduitSelectionButtons(); - } - - private void addConduitScreenExtensionWidgets() { - ConduitScreenExtension conduitScreenExtension = ConduitScreenExtensions.get(menu.getConduit().value().type()); - - if (conduitScreenExtension != null) { - conduitScreenExtension - .createWidgets(this, conduitDataAccessor, - this::sendExtendedConduitUpdate, menu::getDirection, - new Vector2i(22, 7).add(getGuiLeft(), getGuiTop())) - .forEach(this::addRenderableWidget); - } - } - - private void addConduitSelectionButtons() { - List>> validConnections = new ArrayList<>(); - for (Holder> type : getBundle().getConduits()) { - if (getConnectionState(type) instanceof DynamicConnectionState) { - validConnections.add(type); - } - } - - for (int i = 0; i < validConnections.size(); i++) { - Holder> connection = validConnections.get(i); - addRenderableWidget(new ConduitSelectionButton(getGuiLeft() + 206, getGuiTop() + 4 + 24*i, connection, - this::getConduit, this::setConduitType)); - } - } - - private void sendExtendedConduitUpdate() { - Holder> conduit = menu.getConduit(); - ConduitGraphObject node = getBundle().getNodeFor(conduit); - - PacketDistributor.sendToServer(new C2SSetConduitExtendedData( - menu.getBlockEntity().getBlockPos(), - menu.getConduit(), - node.conduitDataContainer())); - } - - private void actOnDynamic(Function map) { - if (getConnectionState() instanceof DynamicConnectionState dyn) { - PacketDistributor.sendToServer(new C2SSetConduitConnectionState( - getMenu().getBlockEntity().getBlockPos(), - getMenu().getDirection(), - getMenu().getConduit(), - map.apply(dyn) - )); - } - } - private T getOnDynamic(Function map, T defaultValue) { - return getConnectionState() instanceof DynamicConnectionState dyn ? map.apply(dyn) : defaultValue; - } - - public Holder> getConduit() { - return menu.getConduit(); - } - - public ConduitMenuData getMenuData() { - return getConduit().value().getMenuData(); - } - - private void setConduitType(Holder> conduit) { - menu.setConduit(conduit); - rebuildWidgets(); - } - - private ConnectionState getConnectionState() { - return getConnectionState(menu.getConduit()); - } - - private ConnectionState getConnectionState(Holder> type) { - return getBundle().getConnectionState(menu.getDirection(), type); - } - - private ConduitBundle getBundle() { - return menu.getBlockEntity().getBundle(); - } - - /** - * This is a simple passthrough to the current conduit node's data. - * This results in a much nicer API than that of the alternative approach of a conduit data accessor supplier. - */ - private class ClientConduitDataAccessor implements ConduitDataAccessor { - - private ConduitDataAccessor getCurrentDataAccessor() { - return getBundle().getNodeFor(menu.getConduit()); - } - - @Override - public boolean hasData(ConduitDataType type) { - return getCurrentDataAccessor().hasData(type); - } - - @Override - public > @Nullable T getData(ConduitDataType type) { - return getCurrentDataAccessor().getData(type); - } - - @Override - public > T getOrCreateData(ConduitDataType type) { - return getCurrentDataAccessor().getOrCreateData(type); - } - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/ConduitSelectionButton.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/NewConduitSelectionButton.java similarity index 53% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/gui/ConduitSelectionButton.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/gui/NewConduitSelectionButton.java index 95ad7e2f0f..53f49a0ee1 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/ConduitSelectionButton.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/NewConduitSelectionButton.java @@ -1,7 +1,11 @@ package com.enderio.conduits.client.gui; import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.client.gui.screen.ConduitScreen; import com.mojang.blaze3d.systems.RenderSystem; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.narration.NarrationElementOutput; @@ -10,46 +14,66 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; -import java.util.function.Consumer; -import java.util.function.Supplier; +public class NewConduitSelectionButton extends AbstractButton { + private final int conduitButtonIndex; + private final Supplier>> currentConduit; + private final Supplier>>> conduitListGetter; + private final Consumer onPressed; -public class ConduitSelectionButton extends AbstractButton { - private final Holder> conduit; - private final Supplier>> getter; - private final Consumer>> setter; - - public ConduitSelectionButton(int pX, int pY, Holder> conduit, Supplier>> getter, Consumer>> setter) { + public NewConduitSelectionButton(int pX, int pY, int conduitButtonIndex, + Supplier>> currentConduit, Supplier>>> conduitListGetter, + Consumer onPressed) { super(pX, pY, 21, 24, Component.empty()); - this.conduit = conduit; - this.getter = getter; - this.setter = setter; + this.conduitButtonIndex = conduitButtonIndex; + this.currentConduit = currentConduit; + this.conduitListGetter = conduitListGetter; + this.onPressed = onPressed; + } + + @Nullable + private Holder> getConduit() { + var list = conduitListGetter.get(); + if (conduitButtonIndex >= 0 && conduitButtonIndex < list.size()) { + return list.get(conduitButtonIndex); + } + + return null; } @Override protected boolean isValidClickButton(int pButton) { - return super.isValidClickButton(pButton) && getter.get() != conduit; + var conduit = getConduit(); + return super.isValidClickButton(pButton) && conduit != null && conduit != currentConduit.get(); } @Override public void onPress() { - setter.accept(conduit); + onPressed.accept(conduitButtonIndex); } @Override public void renderWidget(GuiGraphics guiGraphics, int pMouseX, int pMouseY, float pPartialTick) { + var conduit = getConduit(); + if (conduit == null) { + return; + } + RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); RenderSystem.enableDepthTest(); guiGraphics.blit(ConduitScreen.TEXTURE, getX(), getY(), 227, 0, this.width, this.height); - if (getter.get() == conduit) { + if (currentConduit.get() == conduit) { guiGraphics.blit(ConduitScreen.TEXTURE, getX() - 3, getY(), 224, 0, 3, this.height); } + // TODO: This shouldn't be a hard-coded path. ResourceLocation iconLocation = MissingTextureAtlasSprite.getLocation(); ResourceLocation conduitKey = conduit.unwrapKey().map(ResourceKey::location).orElse(null); if (conduitKey != null) { - iconLocation = ResourceLocation.fromNamespaceAndPath(conduitKey.getNamespace(), "conduit_icon/" + conduitKey.getPath()); + iconLocation = ResourceLocation.fromNamespaceAndPath(conduitKey.getNamespace(), + "conduit_icon/" + conduitKey.getPath()); } guiGraphics.blitSprite(iconLocation, getX() + 3, getY() + 6, 12, 12); @@ -61,8 +85,4 @@ public void renderWidget(GuiGraphics guiGraphics, int pMouseX, int pMouseY, floa @Override protected void updateWidgetNarration(NarrationElementOutput pNarrationElementOutput) { } - - public Holder> getConduit() { - return conduit; - } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/ConduitScreenExtensions.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/ConduitScreenExtensions.java deleted file mode 100644 index 1adb226ca5..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/ConduitScreenExtensions.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.enderio.conduits.client.gui.conduit; - -import com.enderio.conduits.api.ConduitType; -import com.enderio.conduits.api.screen.ConduitScreenExtension; -import com.enderio.conduits.api.screen.RegisterConduitScreenExtensionsEvent; -import me.liliandev.ensure.ensures.EnsureSide; -import net.neoforged.fml.ModLoader; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; - -public class ConduitScreenExtensions { - private static Map, ConduitScreenExtension> EXTENSIONS; - - @EnsureSide(EnsureSide.Side.CLIENT) - public static void init() { - var event = new RegisterConduitScreenExtensionsEvent(); - ModLoader.postEvent(event); - var factories = event.getExtensions(); - - EXTENSIONS = new HashMap<>(); - factories.forEach((t, f) -> EXTENSIONS.put(t, f.createExtension())); - } - - @EnsureSide(EnsureSide.Side.CLIENT) - @Nullable - public static ConduitScreenExtension get(ConduitType conduitType) { - return EXTENSIONS.get(conduitType); - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/FluidConduitScreenExtension.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/FluidConduitScreenExtension.java deleted file mode 100644 index df63618c01..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/FluidConduitScreenExtension.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.enderio.conduits.client.gui.conduit; - -import com.enderio.base.api.EnderIO; -import com.enderio.conduits.api.ConduitDataAccessor; -import com.enderio.conduits.api.screen.ConduitScreenExtension; -import com.enderio.base.common.lang.EIOLang; -import com.enderio.conduits.common.conduit.type.fluid.FluidConduitData; -import com.enderio.conduits.common.init.ConduitTypes; -import com.enderio.core.common.util.TooltipUtil; -import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.components.Tooltip; -import net.minecraft.client.gui.narration.NarrationElementOutput; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.renderer.texture.AbstractTexture; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.core.Direction; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.FastColor; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.Fluids; -import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions; -import org.joml.Vector2i; - -import java.util.List; -import java.util.function.Supplier; - -public final class FluidConduitScreenExtension implements ConduitScreenExtension { - - private static final ResourceLocation WIDGET_TEXTURE = EnderIO.loc("textures/gui/fluidbackground.png"); - - @Override - public List createWidgets(Screen screen, ConduitDataAccessor conduitDataAccessor, UpdateDispatcher updateConduitData, - Supplier direction, Vector2i widgetsStart) { - if (conduitDataAccessor.getOrCreateData(ConduitTypes.Data.FLUID.get()).lockedFluid().isSame(Fluids.EMPTY)) { - return List.of(); - } - return List.of( - new FluidWidget(widgetsStart.add(0, 20), - () -> conduitDataAccessor.getOrCreateData(ConduitTypes.Data.FLUID.get()).lockedFluid(), - () -> { - FluidConduitData data = conduitDataAccessor.getOrCreateData(ConduitTypes.Data.FLUID.get()); - data.setShouldReset(true); - updateConduitData.sendUpdate(); - }) - ); - } - - private static class FluidWidget extends AbstractWidget { - private final Runnable onPress; - private final Supplier currentFluid; - - FluidWidget(Vector2i pos, Supplier fluid, Runnable onPress) { - super(pos.x(), pos.y(), 14, 14, Component.empty()); - this.onPress = onPress; - this.currentFluid = fluid; - } - - @Override - public void updateWidgetNarration(NarrationElementOutput pNarrationElementOutput) { - } - - @Override - public void renderWidget(GuiGraphics guiGraphics, int pMouseX, int pMouseY, float pPartialTick) { - if (isHoveredOrFocused()) { - MutableComponent tooltip = EIOLang.FLUID_CONDUIT_CHANGE_FLUID1.copy(); - tooltip.append("\n").append(EIOLang.FLUID_CONDUIT_CHANGE_FLUID2); - if (!currentFluid.get().isSame(Fluids.EMPTY)) { - tooltip.append("\n").append(TooltipUtil.withArgs(EIOLang.FLUID_CONDUIT_CHANGE_FLUID3, currentFluid.get().getFluidType().getDescription())); - } - setTooltip(Tooltip.create(TooltipUtil.style(tooltip))); - } - - RenderSystem.enableBlend(); - RenderSystem.defaultBlendFunc(); - RenderSystem.enableDepthTest(); - guiGraphics.blit(WIDGET_TEXTURE, getX(), getY(), 0, 0, this.width, this.height); - if (currentFluid.get().isSame(Fluids.EMPTY)) { - return; - } - - IClientFluidTypeExtensions props = IClientFluidTypeExtensions.of(currentFluid.get()); - ResourceLocation still = props.getStillTexture(); - AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(TextureAtlas.LOCATION_BLOCKS); - if (texture instanceof TextureAtlas atlas) { - TextureAtlasSprite sprite = atlas.getSprite(still); - - int color = props.getTintColor(); - RenderSystem.setShaderColor( - FastColor.ARGB32.red(color) / 255.0F, - FastColor.ARGB32.green(color) / 255.0F, - FastColor.ARGB32.blue(color) / 255.0F, - FastColor.ARGB32.alpha(color) / 255.0F); - RenderSystem.enableBlend(); - - - int atlasWidth = (int)(sprite.contents().width() / (sprite.getU1() - sprite.getU0())); - int atlasHeight = (int)(sprite.contents().height() / (sprite.getV1() - sprite.getV0())); - - guiGraphics.blit(TextureAtlas.LOCATION_BLOCKS, getX() + 1, getY() + 1, 0, sprite.getU0()*atlasWidth, sprite.getV0()*atlasHeight, 12, 12, atlasWidth, atlasHeight); - - RenderSystem.setShaderColor(1, 1, 1, 1); - } - - RenderSystem.disableBlend(); - RenderSystem.disableDepthTest(); - } - - @Override - public void onClick(double pMouseX, double pMouseY) { - onPress.run(); - } - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/ItemConduitScreenExtension.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/ItemConduitScreenExtension.java deleted file mode 100644 index de46f0fe8c..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/ItemConduitScreenExtension.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.enderio.conduits.client.gui.conduit; - -import com.enderio.base.api.EnderIO; -import com.enderio.conduits.api.ConduitDataAccessor; -import com.enderio.conduits.api.screen.ConduitScreenExtension; -import com.enderio.base.common.lang.EIOLang; -import com.enderio.conduits.common.init.ConduitTypes; -import com.enderio.core.client.gui.widgets.ToggleIconButton; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import org.joml.Vector2i; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -public class ItemConduitScreenExtension implements ConduitScreenExtension { - - private static final ResourceLocation ICON_ROUND_ROBIN_ENABLED = EnderIO.loc("icon/round_robin_enabled"); - private static final ResourceLocation ICON_ROUND_ROBIN_DISABLED = EnderIO.loc("icon/round_robin_disabled"); - private static final ResourceLocation ICON_SELF_FEED_ENABLED = EnderIO.loc("icon/self_feed_enabled"); - private static final ResourceLocation ICON_SELF_FEED_DISABLED = EnderIO.loc("icon/self_feed_disabled"); - - @Override - public List createWidgets(Screen screen, ConduitDataAccessor conduitDataAccessor, UpdateDispatcher updateConduitData, - Supplier direction, Vector2i widgetsStart) { - List widgets = new ArrayList<>(); - - widgets.add(ToggleIconButton.of( - widgetsStart.x() + 110, - widgetsStart.y() + 20, - 16, - 16, - ICON_ROUND_ROBIN_ENABLED, - ICON_ROUND_ROBIN_DISABLED, - EIOLang.ROUND_ROBIN_ENABLED, - EIOLang.ROUND_ROBIN_DISABLED, - () -> conduitDataAccessor.getOrCreateData(ConduitTypes.Data.ITEM.get()).get(direction.get()).isRoundRobin, - bool -> { - var data = conduitDataAccessor.getOrCreateData(ConduitTypes.Data.ITEM.get()); - var sideData = data.compute(direction.get()); - sideData.isRoundRobin = bool; - updateConduitData.sendUpdate(); - })); - - widgets.add(ToggleIconButton.of( - widgetsStart.x() + 130, - widgetsStart.y() + 20, - 16, - 16, - ICON_SELF_FEED_ENABLED, - ICON_SELF_FEED_DISABLED, - EIOLang.SELF_FEED_ENABLED, - EIOLang.SELF_FEED_DISABLED, - () -> conduitDataAccessor.getOrCreateData(ConduitTypes.Data.ITEM.get()).get(direction.get()).isSelfFeed, - bool -> { - var data = conduitDataAccessor.getOrCreateData(ConduitTypes.Data.ITEM.get()); - var sideData = data.compute(direction.get()); - sideData.isSelfFeed = bool; - updateConduitData.sendUpdate(); - })); - return widgets; - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/ConduitScreen.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/ConduitScreen.java new file mode 100644 index 0000000000..f1c2e82667 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/ConduitScreen.java @@ -0,0 +1,239 @@ +package com.enderio.conduits.client.gui.screen; + +import com.enderio.base.api.EnderIO; +import com.enderio.base.api.misc.RedstoneControl; +import com.enderio.base.client.gui.widget.DyeColorPickerWidget; +import com.enderio.base.client.gui.widget.RedstoneControlPickerWidget; +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.bundle.SlotType; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.screen.ConduitMenuDataAccess; +import com.enderio.conduits.api.screen.ConduitScreenHelper; +import com.enderio.conduits.api.screen.ConduitScreenType; +import com.enderio.conduits.client.gui.NewConduitSelectionButton; +import com.enderio.conduits.client.gui.screen.types.ConduitScreenTypes; +import com.enderio.conduits.common.conduit.menu.ConduitMenu; +import com.enderio.conduits.common.init.ConduitLang; +import com.enderio.conduits.common.network.SetConduitConnectionConfigPacket; +import com.enderio.core.client.gui.screen.EnderContainerScreen; +import com.enderio.core.client.gui.widgets.ToggleIconButton; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Renderable; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.item.DyeColor; +import net.neoforged.neoforge.network.PacketDistributor; +import org.jetbrains.annotations.Nullable; + +public class ConduitScreen extends EnderContainerScreen { + public static final ResourceLocation TEXTURE = EnderIO.loc("textures/gui/conduit.png"); + private static final int WIDTH = 206; + private static final int HEIGHT = 195; + + private final ScreenHelper screenHelper = new ScreenHelper(); + + private final ConduitScreenTypeContainer screenTypeContainer; + + private final List preRenderActions = new ArrayList<>(); + + public ConduitScreen(ConduitMenu pMenu, Inventory pPlayerInventory, Component pTitle) { + super(pMenu, pPlayerInventory, pTitle); + +// this.shouldRenderLabels = true; + this.imageWidth = WIDTH; + this.imageHeight = HEIGHT; + + // Get the screen type for this conduit, if available. + this.screenTypeContainer = new ConduitScreenTypeContainer<>(menu.getSelectedConduit().value()); + } + + @Override + protected void init() { + super.init(); + preRenderActions.clear(); + + if (screenTypeContainer.hasScreenType()) { + screenTypeContainer.addWidgets(screenHelper); + } + + for (int i = 0; i < 9; i++) { + addRenderableWidget(new NewConduitSelectionButton(getGuiLeft() + 206, getGuiTop() + 4 + 24 * i, i, + menu::getSelectedConduit, menu::getConnectedConduits, + idx -> handleButtonPress(ConduitMenu.BUTTON_CHANGE_CONDUIT_START_ID + idx))); + } + } + + @Override + public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + preRenderActions.forEach(Runnable::run); + super.render(guiGraphics, mouseX, mouseY, partialTick); + } + + @Override + protected void renderBg(GuiGraphics guiGraphics, float v, int i, int i1) { + guiGraphics.blit(TEXTURE, getGuiLeft(), getGuiTop(), 0, 0, imageWidth, imageHeight); + +// if (menu.connectionConfigType().supportsIO()) { +// guiGraphics.blit(TEXTURE, getGuiLeft() + 102, getGuiTop() + 7, 255, 0, 1, 97); +// } + + // TODO + for (SlotType type : SlotType.values()) { +// if (type.isAvailableFor(data)) { +// guiGraphics.blit(TEXTURE, type.getX() - 1, type.getY() - 1, 206, 0, 18, 18); +// } + } + } + + @Override + protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) { + super.renderLabels(guiGraphics, mouseX, mouseY); + + if (screenTypeContainer.hasScreenType()) { + screenTypeContainer.renderLabels(guiGraphics, mouseX, mouseY); + } else { + guiGraphics.drawString(this.font, ConduitLang.CONDUIT_ERROR_NO_SCREEN_TYPE, 22, 7 + 4, 0xffff5733, false); + } + } + + // Due to the generics, the menu data access and screen type need to be + // contained here. + private class ConduitScreenTypeContainer { + private final ConduitMenuDataAccess dataAccess; + + @Nullable + private final ConduitScreenType screenType; + + public ConduitScreenTypeContainer(Conduit conduit) { + this.dataAccess = createDataAccess(menu.getBlockPos(), conduit); + this.screenType = ConduitScreenTypes.get(conduit.type()); + } + + public boolean hasScreenType() { + return screenType != null; + } + + public void addWidgets(ScreenHelper screenHelper) { + if (screenType != null) { + screenType.createScreenWidgets(screenHelper, getGuiLeft(), getGuiTop(), dataAccess); + } + } + + public void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) { + if (screenType != null) { + screenType.renderScreenLabels(guiGraphics, font, mouseX, mouseY); + } + } + + private , U extends ConnectionConfig> ConduitMenuDataAccess createDataAccess( + BlockPos pos, Conduit conduit) { + return new ConduitMenuDataAccess<>() { + @Override + public Conduit conduit() { + return conduit; + } + + @Override + public BlockPos getBlockPos() { + return pos; + } + + @Override + public U getConnectionConfig() { + return menu.connectionConfig(conduit.connectionConfigType()); + } + + @Override + public void updateConnectionConfig(java.util.function.Function configModifier) { + var newConfig = configModifier.apply(menu.connectionConfig(conduit.connectionConfigType())); + + PacketDistributor.sendToServer(new SetConduitConnectionConfigPacket(menu.containerId, newConfig)); + + // Update on the client so UI is immediately in sync + menu.setConnectionConfig(newConfig); + } + + @Override + public CompoundTag getExtraGuiData() { + return menu.extraGuiData(); + } + }; + } + } + + private class ScreenHelper implements ConduitScreenHelper { + + @Override + public AbstractWidget addCheckbox(int x, int y, Supplier getter, Consumer setter) { + var widget = ToggleIconButton.createCheckbox(x, y, getter, setter); + addRenderableWidget(widget); + return widget; + } + + @Override + public AbstractWidget addColorPicker(int x, int y, Component title, Supplier getter, + Consumer setter) { + var widget = new DyeColorPickerWidget(x, y, getter, setter, title); + addRenderableWidget(widget); + return widget; + } + + @Override + public AbstractWidget addRedstoneControlPicker(int x, int y, Component title, Supplier getter, + Consumer setter) { + var widget = new RedstoneControlPickerWidget(x, y, getter, setter, title); + addRenderableWidget(widget); + return widget; + } + + @Override + public AbstractWidget addToggleButton(int x, int y, int width, int height, Component enabledTitle, + Component disabledTitle, ResourceLocation enabledSprite, ResourceLocation disabledSprite, + Supplier getter, Consumer setter) { + + var widget = ToggleIconButton.of(x, y, width, height, enabledSprite, + disabledSprite, enabledTitle, disabledTitle, getter, setter); + addRenderableWidget(widget); + return widget; + } + + // Dynamic UI utilities + + @Override + public void addPreRenderAction(Runnable runnable) { + preRenderActions.add(runnable); + } + + // Custom widgets + + @Override + public W addWidget(W listener) { + return ConduitScreen.this.addWidget(listener); + } + + @Override + public W addRenderableOnly(W renderable) { + return ConduitScreen.this.addRenderableOnly(renderable); + } + + @Override + public W addRenderableWidget(W widget) { + return ConduitScreen.this.addRenderableWidget(widget); + } + + @Override + public void removeWidget(GuiEventListener listener) { + ConduitScreen.this.removeWidget(listener); + } + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/RedstoneCountFilterScreen.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/RedstoneCountFilterScreen.java similarity index 77% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/gui/RedstoneCountFilterScreen.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/RedstoneCountFilterScreen.java index 99e294a996..168b7278c7 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/RedstoneCountFilterScreen.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/RedstoneCountFilterScreen.java @@ -1,8 +1,9 @@ -package com.enderio.conduits.client.gui; +package com.enderio.conduits.client.gui.screen.filter; import com.enderio.base.api.EnderIO; import com.enderio.base.client.gui.widget.DyeColorPickerWidget; import com.enderio.base.common.lang.EIOLang; +import com.enderio.conduits.common.init.ConduitLang; import com.enderio.conduits.common.menu.RedstoneCountFilterMenu; import com.enderio.core.client.gui.screen.EIOScreen; import net.minecraft.client.gui.components.Button; @@ -13,7 +14,7 @@ import org.joml.Vector2i; public class RedstoneCountFilterScreen extends EIOScreen { - private static final Vector2i BG_SIZE = new Vector2i(183,201); + private static final Vector2i BG_SIZE = new Vector2i(183, 201); private static final ResourceLocation BG_TEXTURE = EnderIO.loc("textures/gui/40/item_filter.png"); public RedstoneCountFilterScreen(RedstoneCountFilterMenu pMenu, Inventory pPlayerInventory, Component pTitle) { @@ -24,11 +25,10 @@ public RedstoneCountFilterScreen(RedstoneCountFilterMenu pMenu, Inventory pPlaye protected void init() { super.init(); addRenderableWidget(new DyeColorPickerWidget(this.leftPos + 15, this.topPos + 30, - getMenu().getFilter()::getChannel, - getMenu()::setChannel, - EIOLang.REDSTONE_CHANNEL)); + getMenu().getFilter()::getChannel, getMenu()::setChannel, ConduitLang.REDSTONE_CHANNEL)); - EditBox pWidget = new EditBox(this.font, this.leftPos + 60, this.topPos + 20, 60, 20, Component.literal("" + getMenu().getFilter().getMaxCount())) { + EditBox pWidget = new EditBox(this.font, this.leftPos + 60, this.topPos + 20, 60, 20, + Component.literal("" + getMenu().getFilter().getMaxCount())) { @Override public boolean charTyped(char pCodePoint, int pModifiers) { return Character.isDigit(pCodePoint) && super.charTyped(pCodePoint, pModifiers); @@ -37,9 +37,9 @@ public boolean charTyped(char pCodePoint, int pModifiers) { pWidget.setValue("" + getMenu().getFilter().getMaxCount()); addRenderableWidget(pWidget); addRenderableWidget(Button.builder(EIOLang.CONFIRM, pButton -> getMenu().setCount(pWidget.getValue())) - .pos(this.leftPos + 60, this.topPos + 41) - .size(60, 20) - .build()); + .pos(this.leftPos + 60, this.topPos + 41) + .size(60, 20) + .build()); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/RedstoneDoubleChannelFilterScreen.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/RedstoneDoubleChannelFilterScreen.java similarity index 70% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/gui/RedstoneDoubleChannelFilterScreen.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/RedstoneDoubleChannelFilterScreen.java index e7ca60fe35..80b3e0d678 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/RedstoneDoubleChannelFilterScreen.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/RedstoneDoubleChannelFilterScreen.java @@ -1,8 +1,8 @@ -package com.enderio.conduits.client.gui; +package com.enderio.conduits.client.gui.screen.filter; import com.enderio.base.api.EnderIO; import com.enderio.base.client.gui.widget.DyeColorPickerWidget; -import com.enderio.base.common.lang.EIOLang; +import com.enderio.conduits.common.init.ConduitLang; import com.enderio.conduits.common.menu.RedstoneDoubleChannelFilterMenu; import com.enderio.core.client.gui.screen.EIOScreen; import net.minecraft.network.chat.Component; @@ -12,10 +12,11 @@ public class RedstoneDoubleChannelFilterScreen extends EIOScreen { - private static final Vector2i BG_SIZE = new Vector2i(183,201); + private static final Vector2i BG_SIZE = new Vector2i(183, 201); private static final ResourceLocation BG_TEXTURE = EnderIO.loc("textures/gui/40/item_filter.png"); - public RedstoneDoubleChannelFilterScreen(RedstoneDoubleChannelFilterMenu pMenu, Inventory pPlayerInventory, Component pTitle) { + public RedstoneDoubleChannelFilterScreen(RedstoneDoubleChannelFilterMenu pMenu, Inventory pPlayerInventory, + Component pTitle) { super(pMenu, pPlayerInventory, pTitle); } @@ -23,13 +24,9 @@ public RedstoneDoubleChannelFilterScreen(RedstoneDoubleChannelFilterMenu pMenu, protected void init() { super.init(); addRenderableWidget(new DyeColorPickerWidget(this.leftPos + 15, this.topPos + 30, - getMenu().getChannels()::getFirstChannel, - getMenu()::setFirstChannel, - EIOLang.REDSTONE_CHANNEL)); + getMenu().getChannels()::getFirstChannel, getMenu()::setFirstChannel, ConduitLang.REDSTONE_CHANNEL)); addRenderableWidget(new DyeColorPickerWidget(this.leftPos + 15 + 60, this.topPos + 30, - getMenu().getChannels()::getSecondChannel, - getMenu()::setSecondChannel, - EIOLang.REDSTONE_CHANNEL)); + getMenu().getChannels()::getSecondChannel, getMenu()::setSecondChannel, ConduitLang.REDSTONE_CHANNEL)); } @Override diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/RedstoneTimerFilterScreen.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/RedstoneTimerFilterScreen.java similarity index 82% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/gui/RedstoneTimerFilterScreen.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/RedstoneTimerFilterScreen.java index 747ad8d281..205d4e6d46 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/RedstoneTimerFilterScreen.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/RedstoneTimerFilterScreen.java @@ -1,4 +1,4 @@ -package com.enderio.conduits.client.gui; +package com.enderio.conduits.client.gui.screen.filter; import com.enderio.base.api.EnderIO; import com.enderio.base.common.lang.EIOLang; @@ -13,7 +13,7 @@ public class RedstoneTimerFilterScreen extends EIOScreen { - private static final Vector2i BG_SIZE = new Vector2i(183,201); + private static final Vector2i BG_SIZE = new Vector2i(183, 201); private static final ResourceLocation BG_TEXTURE = EnderIO.loc("textures/gui/40/item_filter.png"); public RedstoneTimerFilterScreen(RedstoneTimerFilterMenu pMenu, Inventory pPlayerInventory, Component pTitle) { @@ -23,7 +23,8 @@ public RedstoneTimerFilterScreen(RedstoneTimerFilterMenu pMenu, Inventory pPlaye @Override protected void init() { super.init(); - EditBox pWidget = new EditBox(this.font, this.leftPos + 60, this.topPos + 20, 60, 20, Component.literal("" + getMenu().getFilter().getMaxTicks())) { + EditBox pWidget = new EditBox(this.font, this.leftPos + 60, this.topPos + 20, 60, 20, + Component.literal("" + getMenu().getFilter().getMaxTicks())) { @Override public boolean charTyped(char pCodePoint, int pModifiers) { return Character.isDigit(pCodePoint) && super.charTyped(pCodePoint, pModifiers); @@ -32,9 +33,9 @@ public boolean charTyped(char pCodePoint, int pModifiers) { pWidget.setValue("" + getMenu().getFilter().getMaxTicks()); addRenderableWidget(pWidget); addRenderableWidget(Button.builder(EIOLang.CONFIRM, pButton -> getMenu().setTimer(pWidget.getValue())) - .pos(this.leftPos + 60, this.topPos + 41) - .size(60, 20) - .build()); + .pos(this.leftPos + 60, this.topPos + 41) + .size(60, 20) + .build()); } @Override diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/package-info.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/package-info.java new file mode 100644 index 0000000000..33456e90e5 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/filter/package-info.java @@ -0,0 +1,4 @@ +@javax.annotation.ParametersAreNonnullByDefault +@net.minecraft.MethodsReturnNonnullByDefault + +package com.enderio.conduits.client.gui.screen.filter; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ConduitScreenTypes.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ConduitScreenTypes.java new file mode 100644 index 0000000000..6f212f0c78 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ConduitScreenTypes.java @@ -0,0 +1,30 @@ +package com.enderio.conduits.client.gui.screen.types; + +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.ConduitType; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.screen.ConduitScreenType; +import com.enderio.conduits.api.screen.RegisterConduitScreenTypesEvent; +import java.util.Map; +import me.liliandev.ensure.ensures.EnsureSide; +import net.neoforged.fml.ModLoader; +import org.jetbrains.annotations.Nullable; + +public class ConduitScreenTypes { + private static Map, ConduitScreenType> SCREEN_TYPES; + + @EnsureSide(EnsureSide.Side.CLIENT) + public static void init() { + var event = new RegisterConduitScreenTypesEvent(); + ModLoader.postEvent(event); + SCREEN_TYPES = Map.copyOf(event.getScreenTypes()); + } + + @EnsureSide(EnsureSide.Side.CLIENT) + @Nullable + public static , U extends ConnectionConfig> ConduitScreenType get( + ConduitType conduitType) { + // noinspection unchecked + return (ConduitScreenType) SCREEN_TYPES.get(conduitType); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java new file mode 100644 index 0000000000..53c51ba8eb --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/EnergyConduitScreenType.java @@ -0,0 +1,45 @@ +package com.enderio.conduits.client.gui.screen.types; + +import com.enderio.base.common.lang.EIOLang; +import com.enderio.conduits.api.screen.ConduitMenuDataAccess; +import com.enderio.conduits.api.screen.ConduitScreenHelper; +import com.enderio.conduits.api.screen.ConduitScreenType; +import com.enderio.conduits.api.screen.IOConduitScreenType; +import com.enderio.conduits.common.conduit.type.energy.EnergyConduitConnectionConfig; +import com.enderio.conduits.common.init.ConduitLang; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; + +public class EnergyConduitScreenType extends IOConduitScreenType { + + @Override + public void createRightWidgets(ConduitScreenHelper screen, int startX, int startY, ConduitMenuDataAccess dataAccess) { + super.createRightWidgets(screen, startX, startY, dataAccess); + + // Redstone control + var redstoneChannelWidget = screen.addColorPicker(startX + 16 + 4, startY + 20, ConduitLang.REDSTONE_CHANNEL, + () -> dataAccess.getConnectionConfig().receiveRedstoneChannel(), + value -> dataAccess.updateConnectionConfig(config -> config.withReceiveRedstoneChannel(value))); + + // Only show the redstone widget when redstone control is sensitive to signals. + screen.addPreRenderAction(() -> redstoneChannelWidget.visible = dataAccess.getConnectionConfig() + .receiveRedstoneControl() + .isRedstoneSensitive()); + + screen.addRedstoneControlPicker(startX, startY + 20, EIOLang.REDSTONE_MODE, + () -> dataAccess.getConnectionConfig().receiveRedstoneControl(), + value -> dataAccess.updateConnectionConfig(config -> config.withReceiveRedstoneControl(value))); + + // TODO: Show redstone signal indicators using the extra NBT payload. + } + + @Override + protected EnergyConduitConnectionConfig setLeftEnabled(EnergyConduitConnectionConfig config, boolean isEnabled) { + return config.withIsSend(isEnabled); + } + + @Override + protected EnergyConduitConnectionConfig setRightEnabled(EnergyConduitConnectionConfig config, boolean isEnabled) { + return config.withIsReceive(isEnabled); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java new file mode 100644 index 0000000000..973b8830b6 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/FluidConduitScreenType.java @@ -0,0 +1,194 @@ +package com.enderio.conduits.client.gui.screen.types; + +import com.enderio.base.api.EnderIO; +import com.enderio.base.common.lang.EIOLang; +import com.enderio.conduits.api.screen.ConduitMenuDataAccess; +import com.enderio.conduits.api.screen.ConduitScreenHelper; +import com.enderio.conduits.api.screen.ConduitScreenType; +import com.enderio.conduits.api.screen.IOConduitScreenType; +import com.enderio.conduits.common.conduit.type.fluid.FluidConduit; +import com.enderio.conduits.common.conduit.type.fluid.FluidConduitConnectionConfig; +import com.enderio.conduits.common.init.ConduitLang; +import com.enderio.conduits.common.network.C2SClearLockedFluidPacket; +import com.enderio.core.common.util.TooltipUtil; +import com.mojang.blaze3d.systems.RenderSystem; +import java.util.function.Supplier; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.client.renderer.texture.AbstractTexture; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.FastColor; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.Fluids; +import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions; +import net.neoforged.neoforge.network.PacketDistributor; + +public class FluidConduitScreenType extends IOConduitScreenType { + + private static final ResourceLocation ICON_ROUND_ROBIN_ENABLED = EnderIO.loc("icon/round_robin_enabled"); + private static final ResourceLocation ICON_ROUND_ROBIN_DISABLED = EnderIO.loc("icon/round_robin_disabled"); + private static final ResourceLocation ICON_SELF_FEED_ENABLED = EnderIO.loc("icon/self_feed_enabled"); + private static final ResourceLocation ICON_SELF_FEED_DISABLED = EnderIO.loc("icon/self_feed_disabled"); + + @Override + public void createLeftWidgets(ConduitScreenHelper screen, int startX, int startY, ConduitMenuDataAccess dataAccess) { + super.createLeftWidgets(screen, startX, startY, dataAccess); + + // Locked fluid widget + if (dataAccess.conduit() instanceof FluidConduit fluidConduit && !fluidConduit.isMultiFluid()) { + screen.addRenderableWidget(new FluidWidget(startX, startY + 20, + () -> getLockedFluid(dataAccess), + () -> PacketDistributor.sendToServer(new C2SClearLockedFluidPacket(dataAccess.getBlockPos())))); + } else { + // Channel colors + screen.addColorPicker(startX, startY + 20, ConduitLang.CONDUIT_CHANNEL, + () -> dataAccess.getConnectionConfig().sendColor(), + value -> dataAccess.updateConnectionConfig(config -> config.withSendColor(value))); + } + } + + @Override + public void createRightWidgets(ConduitScreenHelper screen, int startX, int startY, ConduitMenuDataAccess dataAccess) { + super.createRightWidgets(screen, startX, startY, dataAccess); + + if (dataAccess.conduit() instanceof FluidConduit fluidConduit && fluidConduit.isMultiFluid()) { + // Channel colors + screen.addColorPicker(startX, startY + 20, ConduitLang.CONDUIT_CHANNEL, + () -> dataAccess.getConnectionConfig().receiveColor(), + value -> dataAccess.updateConnectionConfig(config -> config.withReceiveColor(value))); + } + + // TODO: Could be good fluid conduit features? + /* + * // Round robin screen.addToggleButton(90 + 16 + 4, 20, 16, 16, + * ConduitLang.ROUND_ROBIN_ENABLED, ConduitLang.ROUND_ROBIN_DISABLED, + * ICON_ROUND_ROBIN_ENABLED, ICON_ROUND_ROBIN_DISABLED, () -> + * dataAccess.getConnectionConfig().isRoundRobin(), value -> + * dataAccess.updateConnectionConfig(config -> config.withIsRoundRobin(value))); + * + * // Self feed screen.addToggleButton(90 + (16 + 4) * 2, 20, 16, 16, + * ConduitLang.SELF_FEED_ENABLED, ConduitLang.SELF_FEED_DISABLED, + * ICON_SELF_FEED_ENABLED, ICON_SELF_FEED_DISABLED, () -> + * dataAccess.getConnectionConfig().isSelfFeed(), value -> + * dataAccess.updateConnectionConfig(config -> config.withIsSelfFeed(value))); + */ + + // Redstone control + var redstoneChannelWidget = screen.addColorPicker(startX + 16 + 4, startY + 40, ConduitLang.REDSTONE_CHANNEL, + () -> dataAccess.getConnectionConfig().receiveRedstoneChannel(), + value -> dataAccess.updateConnectionConfig(config -> config.withReceiveRedstoneChannel(value))); + + // Only show the redstone widget when redstone control is sensitive to signals. + screen.addPreRenderAction(() -> redstoneChannelWidget.visible = dataAccess.getConnectionConfig() + .receiveRedstoneControl() + .isRedstoneSensitive()); + + screen.addRedstoneControlPicker(startX, startY + 40, EIOLang.REDSTONE_MODE, + () -> dataAccess.getConnectionConfig().receiveRedstoneControl(), + value -> dataAccess.updateConnectionConfig(config -> config.withReceiveRedstoneControl(value))); + + // TODO: Show redstone signal indicators using the extra NBT payload. + } + + @Override + protected FluidConduitConnectionConfig setLeftEnabled(FluidConduitConnectionConfig config, boolean isEnabled) { + return config.withIsSend(isEnabled); + } + + @Override + protected FluidConduitConnectionConfig setRightEnabled(FluidConduitConnectionConfig config, boolean isEnabled) { + return config.withIsReceive(isEnabled); + } + + private Fluid getLockedFluid(ConduitMenuDataAccess dataAccess) { + var tag = dataAccess.getExtraGuiData(); + if (tag == null) { + return Fluids.EMPTY; + } + + if (!tag.contains("LockedFluid")) { + return Fluids.EMPTY; + } + + return BuiltInRegistries.FLUID.get(ResourceLocation.parse(tag.getString("LockedFluid"))); + } + + private static class FluidWidget extends AbstractWidget { + private static final ResourceLocation WIDGET_TEXTURE = EnderIO.loc("textures/gui/fluidbackground.png"); + + private final Runnable onPress; + private final Supplier currentFluid; + + FluidWidget(int x, int y, Supplier fluid, Runnable onPress) { + super(x, y, 14, 14, Component.empty()); + this.onPress = onPress; + this.currentFluid = fluid; + } + + @Override + public void updateWidgetNarration(NarrationElementOutput pNarrationElementOutput) { + } + + @Override + public void renderWidget(GuiGraphics guiGraphics, int pMouseX, int pMouseY, float pPartialTick) { + if (isHoveredOrFocused()) { + MutableComponent tooltip = ConduitLang.FLUID_CONDUIT_CHANGE_FLUID1.copy(); + tooltip.append("\n").append(ConduitLang.FLUID_CONDUIT_CHANGE_FLUID2); + if (!currentFluid.get().isSame(Fluids.EMPTY)) { + tooltip.append("\n") + .append(TooltipUtil.withArgs(ConduitLang.FLUID_CONDUIT_CHANGE_FLUID3, + currentFluid.get().getFluidType().getDescription())); + } + setTooltip(Tooltip.create(TooltipUtil.style(tooltip))); + } + + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.enableDepthTest(); + guiGraphics.blit(WIDGET_TEXTURE, getX(), getY(), 0, 0, this.width, this.height); + if (currentFluid.get().isSame(Fluids.EMPTY)) { + return; + } + + IClientFluidTypeExtensions props = IClientFluidTypeExtensions.of(currentFluid.get()); + ResourceLocation still = props.getStillTexture(); + AbstractTexture texture = Minecraft.getInstance() + .getTextureManager() + .getTexture(TextureAtlas.LOCATION_BLOCKS); + if (texture instanceof TextureAtlas atlas) { + TextureAtlasSprite sprite = atlas.getSprite(still); + + int color = props.getTintColor(); + RenderSystem.setShaderColor(FastColor.ARGB32.red(color) / 255.0F, + FastColor.ARGB32.green(color) / 255.0F, FastColor.ARGB32.blue(color) / 255.0F, + FastColor.ARGB32.alpha(color) / 255.0F); + RenderSystem.enableBlend(); + + int atlasWidth = (int) (sprite.contents().width() / (sprite.getU1() - sprite.getU0())); + int atlasHeight = (int) (sprite.contents().height() / (sprite.getV1() - sprite.getV0())); + + guiGraphics.blit(TextureAtlas.LOCATION_BLOCKS, getX() + 1, getY() + 1, 0, sprite.getU0() * atlasWidth, + sprite.getV0() * atlasHeight, 12, 12, atlasWidth, atlasHeight); + + RenderSystem.setShaderColor(1, 1, 1, 1); + } + + RenderSystem.disableBlend(); + RenderSystem.disableDepthTest(); + } + + @Override + public void onClick(double pMouseX, double pMouseY) { + onPress.run(); + } + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java new file mode 100644 index 0000000000..a28e70b26d --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/ItemConduitScreenType.java @@ -0,0 +1,78 @@ +package com.enderio.conduits.client.gui.screen.types; + +import com.enderio.base.api.EnderIO; +import com.enderio.base.common.lang.EIOLang; +import com.enderio.conduits.api.screen.ConduitMenuDataAccess; +import com.enderio.conduits.api.screen.ConduitScreenHelper; +import com.enderio.conduits.api.screen.ConduitScreenType; +import com.enderio.conduits.api.screen.IOConduitScreenType; +import com.enderio.conduits.common.conduit.type.item.ItemConduitConnectionConfig; +import com.enderio.conduits.common.init.ConduitLang; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.resources.ResourceLocation; + +public class ItemConduitScreenType extends IOConduitScreenType { + + private static final ResourceLocation ICON_ROUND_ROBIN_ENABLED = EnderIO.loc("icon/round_robin_enabled"); + private static final ResourceLocation ICON_ROUND_ROBIN_DISABLED = EnderIO.loc("icon/round_robin_disabled"); + private static final ResourceLocation ICON_SELF_FEED_ENABLED = EnderIO.loc("icon/self_feed_enabled"); + private static final ResourceLocation ICON_SELF_FEED_DISABLED = EnderIO.loc("icon/self_feed_disabled"); + + @Override + public void createLeftWidgets(ConduitScreenHelper screen, int startX, int startY, ConduitMenuDataAccess dataAccess) { + super.createLeftWidgets(screen, startX, startY, dataAccess); + + // Channel color + screen.addColorPicker(startX, startY + 20, ConduitLang.CONDUIT_CHANNEL, () -> dataAccess.getConnectionConfig().sendColor(), + value -> dataAccess.updateConnectionConfig(config -> config.withSendColor(value))); + } + + @Override + public void createRightWidgets(ConduitScreenHelper screen, int startX, int startY, ConduitMenuDataAccess dataAccess) { + super.createRightWidgets(screen, startX, startY, dataAccess); + + // Channel color + screen.addColorPicker(startX, startY + 20, ConduitLang.CONDUIT_CHANNEL, + () -> dataAccess.getConnectionConfig().receiveColor(), + value -> dataAccess.updateConnectionConfig(config -> config.withReceiveColor(value))); + + // Round robin + screen.addToggleButton(startX + 16 + 4, startY + 20, 16, 16, ConduitLang.ROUND_ROBIN_ENABLED, + ConduitLang.ROUND_ROBIN_DISABLED, ICON_ROUND_ROBIN_ENABLED, ICON_ROUND_ROBIN_DISABLED, + () -> dataAccess.getConnectionConfig().isRoundRobin(), + value -> dataAccess.updateConnectionConfig(config -> config.withIsRoundRobin(value))); + + // Self feed + screen.addToggleButton(startX + (16 + 4) * 2, startY + 20, 16, 16, ConduitLang.SELF_FEED_ENABLED, + ConduitLang.SELF_FEED_DISABLED, ICON_SELF_FEED_ENABLED, ICON_SELF_FEED_DISABLED, + () -> dataAccess.getConnectionConfig().isSelfFeed(), + value -> dataAccess.updateConnectionConfig(config -> config.withIsSelfFeed(value))); + + // Redstone control + var redstoneChannelWidget = screen.addColorPicker(startX + 16 + 4, startY + 40, ConduitLang.REDSTONE_CHANNEL, + () -> dataAccess.getConnectionConfig().receiveRedstoneChannel(), + value -> dataAccess.updateConnectionConfig(config -> config.withReceiveRedstoneChannel(value))); + + // Only show the redstone widget when redstone control is sensitive to signals. + screen.addPreRenderAction(() -> redstoneChannelWidget.visible = dataAccess.getConnectionConfig() + .receiveRedstoneControl() + .isRedstoneSensitive()); + + screen.addRedstoneControlPicker(startX, startY + 40, EIOLang.REDSTONE_MODE, + () -> dataAccess.getConnectionConfig().receiveRedstoneControl(), + value -> dataAccess.updateConnectionConfig(config -> config.withReceiveRedstoneControl(value))); + + // TODO: Show redstone signal indicators using the extra NBT payload. + } + + @Override + protected ItemConduitConnectionConfig setLeftEnabled(ItemConduitConnectionConfig config, boolean isEnabled) { + return config.withIsSend(isEnabled); + } + + @Override + protected ItemConduitConnectionConfig setRightEnabled(ItemConduitConnectionConfig config, boolean isEnabled) { + return config.withIsReceive(isEnabled); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java new file mode 100644 index 0000000000..342aee92d5 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/RedstoneConduitScreenType.java @@ -0,0 +1,72 @@ +package com.enderio.conduits.client.gui.screen.types; + +import com.enderio.conduits.api.screen.ConduitMenuDataAccess; +import com.enderio.conduits.api.screen.ConduitScreenHelper; +import com.enderio.conduits.api.screen.IOConduitScreenType; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitConnectionConfig; +import com.enderio.conduits.common.init.ConduitLang; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; + +public class RedstoneConduitScreenType extends IOConduitScreenType { + + public RedstoneConduitScreenType() { + // TODO: Should be ctor params. + leftTitle = ConduitLang.CONDUIT_INPUT; + rightTitle = ConduitLang.CONDUIT_OUTPUT; + } + + @Override + public void createLeftWidgets(ConduitScreenHelper screen, int startX, int startY, + ConduitMenuDataAccess dataAccess) { + super.createLeftWidgets(screen, startX, startY, dataAccess); + + // Send channel + screen.addColorPicker(startX, startY + 20, ConduitLang.CONDUIT_CHANNEL, () -> dataAccess.getConnectionConfig().receiveColor(), + value -> dataAccess.updateConnectionConfig(config -> config.withReceiveColor(value))); + } + + @Override + public void createRightWidgets(ConduitScreenHelper screen, int startX, int startY, + ConduitMenuDataAccess dataAccess) { + super.createRightWidgets(screen, startX, startY, dataAccess); + + // Send channel + screen.addColorPicker(startX, startY + 20, ConduitLang.CONDUIT_CHANNEL, () -> dataAccess.getConnectionConfig().sendColor(), + value -> dataAccess.updateConnectionConfig(config -> config.withSendColor(value))); + + // Strong signal + screen.addCheckbox(startX, startY + 40, () -> dataAccess.getConnectionConfig().isStrongOutputSignal(), + value -> dataAccess.updateConnectionConfig(config -> config.withIsStrongOutputSignal(value))); + } + + @Override + public boolean getLeftEnabled(RedstoneConduitConnectionConfig config) { + return config.isReceive(); + } + + @Override + public boolean getRightEnabled(RedstoneConduitConnectionConfig config) { + return config.isSend(); + } + + @Override + protected RedstoneConduitConnectionConfig setLeftEnabled(RedstoneConduitConnectionConfig config, boolean isEnabled) { + return config.withIsReceive(isEnabled); + } + + @Override + protected RedstoneConduitConnectionConfig setRightEnabled(RedstoneConduitConnectionConfig config, boolean isEnabled) { + return config.withIsSend(isEnabled); + } + + @Override + public void renderLabels(GuiGraphics guiGraphics, int startX, int startY, Font font, int mouseX, int mouseY) { + super.renderLabels(guiGraphics, startX, startY, font, mouseX, mouseY); + + guiGraphics.drawString(font, ConduitLang.CONDUIT_REDSTONE_SIGNAL_COLOR, startX + PADDED_SLOT_SIZE, startY + 20 + 4, 4210752, false); + guiGraphics.drawString(font, ConduitLang.CONDUIT_REDSTONE_SIGNAL_COLOR, startX + RIGHT_START_X + PADDED_SLOT_SIZE, startY + 20 + 4, 4210752, false); + + guiGraphics.drawString(font, ConduitLang.CONDUIT_REDSTONE_STRONG_SIGNAL, startX + RIGHT_START_X + PADDED_SLOT_SIZE, startY + 40 + 4, 4210752, false); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/package-info.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/package-info.java new file mode 100644 index 0000000000..d4bb9667bb --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/screen/types/package-info.java @@ -0,0 +1,4 @@ +@javax.annotation.ParametersAreNonnullByDefault +@net.minecraft.MethodsReturnNonnullByDefault + +package com.enderio.conduits.client.gui.screen.types; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitItemModel.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/ConduitItemModel.java similarity index 94% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitItemModel.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/ConduitItemModel.java index 7fe168d40d..38f1bb609e 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitItemModel.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/ConduitItemModel.java @@ -1,4 +1,4 @@ -package com.enderio.conduits.client.model; +package com.enderio.conduits.client.model.conduit; import com.enderio.conduits.api.Conduit; import com.enderio.conduits.common.init.ConduitComponents; @@ -42,17 +42,17 @@ public ItemOverrides getOverrides() { public static class ConduitItemOverrides extends ItemOverrides { - private final Map>, BakedModel> CACHE = new HashMap<>(); + private final Map>, BakedModel> CACHE = new HashMap<>(); @Nullable @Override public BakedModel resolve(BakedModel pModel, ItemStack pStack, @Nullable ClientLevel pLevel, @Nullable LivingEntity pEntity, int pSeed) { - Holder> conduit = pStack.get(ConduitComponents.CONDUIT); + Holder> conduit = pStack.get(ConduitComponents.CONDUIT); return CACHE.computeIfAbsent(conduit, t -> createBakedModel(t, pModel)); } - private BakedModel createBakedModel(@Nullable Holder> conduit, BakedModel model) { + private BakedModel createBakedModel(@Nullable Holder> conduit, BakedModel model) { ResourceLocation conduitTexture = MissingTextureAtlasSprite.getLocation(); if (conduit != null) { conduitTexture = conduit.value().texture(); diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitItemModelLoader.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/ConduitItemModelLoader.java similarity index 87% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitItemModelLoader.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/ConduitItemModelLoader.java index 8ae3901a54..41e36bd161 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitItemModelLoader.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/ConduitItemModelLoader.java @@ -1,9 +1,12 @@ -package com.enderio.conduits.client.model; +package com.enderio.conduits.client.model.conduit; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; import net.minecraft.client.renderer.block.model.BlockElement; import net.minecraft.client.renderer.block.model.ItemOverrides; import net.minecraft.client.renderer.texture.TextureAtlasSprite; @@ -16,14 +19,11 @@ import net.neoforged.neoforge.client.model.geometry.IGeometryBakingContext; import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - public class ConduitItemModelLoader implements IGeometryLoader { @Override - public Geometry read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) throws JsonParseException { + public Geometry read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) + throws JsonParseException { if (!jsonObject.has("elements")) { throw new JsonParseException("An element model must have an \"elements\" member."); } @@ -43,8 +43,8 @@ public Geometry(List elements) { } @Override - public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, - ItemOverrides overrides) { + public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, + Function spriteGetter, ModelState modelState, ItemOverrides overrides) { return new ConduitItemModel(super.bake(context, baker, spriteGetter, modelState, overrides)); } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitGeometry.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleGeometry.java similarity index 61% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitGeometry.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleGeometry.java index 3ceefe06a2..274d91b09c 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitGeometry.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleGeometry.java @@ -1,8 +1,9 @@ -package com.enderio.conduits.client.model; +package com.enderio.conduits.client.model.conduit.bundle; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import java.util.function.Function; import net.minecraft.client.renderer.block.model.ItemOverrides; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.BakedModel; @@ -13,23 +14,22 @@ import net.neoforged.neoforge.client.model.geometry.IGeometryLoader; import net.neoforged.neoforge.client.model.geometry.IUnbakedGeometry; -import java.util.function.Function; - -public class ConduitGeometry implements IUnbakedGeometry { +public class ConduitBundleGeometry implements IUnbakedGeometry { - public ConduitGeometry() { + public ConduitBundleGeometry() { } @Override - public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, - ItemOverrides overrides) { - return new ConduitBlockModel(); + public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, + Function spriteGetter, ModelState modelState, ItemOverrides overrides) { + return new ConduitBundleModel(); } - public static class Loader implements IGeometryLoader { + public static class Loader implements IGeometryLoader { @Override - public ConduitGeometry read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) throws JsonParseException { - return new ConduitGeometry(); + public ConduitBundleGeometry read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) + throws JsonParseException { + return new ConduitBundleGeometry(); } } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitBlockModel.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java similarity index 66% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitBlockModel.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java index 795b9a5fb2..f10894f282 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/ConduitBlockModel.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleModel.java @@ -1,41 +1,21 @@ -package com.enderio.conduits.client.model; - -import static com.enderio.conduits.client.ConduitClientSetup.BOX; -import static com.enderio.conduits.client.ConduitClientSetup.CONDUIT_CONNECTION; -import static com.enderio.conduits.client.ConduitClientSetup.CONDUIT_CONNECTION_BOX; -import static com.enderio.conduits.client.ConduitClientSetup.CONDUIT_CONNECTOR; -import static com.enderio.conduits.client.ConduitClientSetup.CONDUIT_CORE; -import static com.enderio.conduits.client.ConduitClientSetup.CONDUIT_IO_IN; -import static com.enderio.conduits.client.ConduitClientSetup.CONDUIT_IO_IN_OUT; -import static com.enderio.conduits.client.ConduitClientSetup.CONDUIT_IO_OUT; -import static com.enderio.conduits.client.ConduitClientSetup.CONDUIT_IO_REDSTONE; -import static com.enderio.conduits.client.ConduitClientSetup.modelOf; - -import com.enderio.base.api.misc.RedstoneControl; +package com.enderio.conduits.client.model.conduit.bundle; + +import static com.enderio.conduits.client.ConduitClientSetup.*; + import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitNode; -import com.enderio.conduits.api.facade.FacadeType; -import com.enderio.conduits.api.model.ConduitCoreModelModifier; +import com.enderio.conduits.api.model.ConduitModelModifier; import com.enderio.conduits.client.ConduitFacadeColor; +import com.enderio.conduits.client.model.BoxTextureQuadTransformer; +import com.enderio.conduits.client.model.ColorQuadTransformer; +import com.enderio.conduits.client.model.ConduitTextureEmissiveQuadTransformer; import com.enderio.conduits.client.model.conduit.facades.FacadeHelper; -import com.enderio.conduits.client.model.conduit.modifier.ConduitCoreModelModifiers; +import com.enderio.conduits.client.model.conduit.modifier.ConduitModelModifiers; import com.enderio.conduits.common.Area; -import com.enderio.conduits.common.conduit.ConduitBundle; -import com.enderio.conduits.common.conduit.ConduitGraphObject; import com.enderio.conduits.common.conduit.OffsetHelper; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity; -import com.enderio.conduits.common.conduit.connection.ConnectionState; -import com.enderio.conduits.common.conduit.connection.DynamicConnectionState; import com.enderio.core.data.model.ModelHelper; import com.mojang.math.Axis; import com.mojang.math.Transformation; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; @@ -46,11 +26,11 @@ import net.minecraft.core.Direction; import net.minecraft.core.Holder; import net.minecraft.core.Vec3i; +import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.RandomSource; import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.SingleThreadedRandomSource; import net.neoforged.neoforge.client.ChunkRenderTypeSet; @@ -58,28 +38,33 @@ import net.neoforged.neoforge.client.model.IQuadTransformer; import net.neoforged.neoforge.client.model.QuadTransformers; import net.neoforged.neoforge.client.model.data.ModelData; +import net.neoforged.neoforge.client.model.data.ModelProperty; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.joml.Quaternionf; import org.joml.Vector3f; -public class ConduitBlockModel implements IDynamicBakedModel { +public class ConduitBundleModel implements IDynamicBakedModel { + + public static final ModelProperty FACADE_MODEL_DATA = new ModelProperty<>(); + public static final ModelProperty FACADE_RENDERTYPE = new ModelProperty<>(); @Override public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand, ModelData extraData, @Nullable RenderType renderType) { List quads = new ArrayList<>(); - ConduitBundle conduitBundle = extraData.get(ConduitBundleBlockEntity.BUNDLE_MODEL_PROPERTY); - ModelData data = extraData.get(ConduitBundleBlockEntity.FACADE_MODEL_DATA); + ModelData data = extraData.get(FACADE_MODEL_DATA); - if (conduitBundle != null) { + ConduitBundleRenderState bundleState = extraData.get(ConduitBundleRenderState.PROPERTY); + + if (bundleState != null) { if (FacadeHelper.areFacadesVisible()) { IQuadTransformer transformer = quad -> quad.tintIndex = ConduitFacadeColor .moveTintIndex(quad.getTintIndex()); - Optional facadeOpt = conduitBundle.facade(); - if (facadeOpt.isPresent()) { - BlockState facade = facadeOpt.get().defaultBlockState(); + + if (bundleState.hasFacade()) { + BlockState facade = bundleState.facade(); var model = Minecraft.getInstance().getBlockRenderer().getBlockModel(facade); var facadeQuads = model.getQuads(facade, side, rand, data, renderType); @@ -89,22 +74,18 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction } // If the facade should hide the conduits, escape early. - if (conduitBundle.hasFacade()) { - boolean areConduitsHidden = conduitBundle.facadeType() - .map(FacadeType::doesHideConduits) - .orElse(false); - - if (areConduitsHidden) { + if (bundleState.hasFacade()) { + if (bundleState.doesFacadeHideConduits()) { return quads; } } } - Direction.Axis axis = OffsetHelper.findMainAxis(conduitBundle); - Map>, List> offsets = new HashMap<>(); + Direction.Axis axis = bundleState.mainAxis(); + Map>, List> offsets = new HashMap<>(); for (Direction direction : Direction.values()) { - boolean isEnd = conduitBundle.isConnectionEnd(direction); + boolean isEnd = bundleState.isConnectionEndpoint(direction); Direction preRotation = rotateDirection(direction, side); IQuadTransformer rotation = QuadTransformers.applying(rotateTransformation(direction)); @@ -113,42 +94,43 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction modelOf(CONDUIT_CONNECTOR).getQuads(state, preRotation, rand, extraData, renderType))); } - var connectedTypes = conduitBundle.getConnectedConduits(direction); + var connectedTypes = bundleState.getConnectedConduits(direction); for (int i = 0; i < connectedTypes.size(); i++) { - Holder> conduit = connectedTypes.get(i); - ConduitGraphObject node = conduitBundle.getNodeFor(conduit); + Holder> conduit = connectedTypes.get(i); + CompoundTag extraWorldData = bundleState.getExtraWorldData(conduit); Vec3i offset = OffsetHelper.translationFor(direction.getAxis(), OffsetHelper.offsetConduit(i, connectedTypes.size())); offsets.computeIfAbsent(conduit, ignored -> new ArrayList<>()).add(offset); IQuadTransformer rotationTranslation = rotation .andThen(QuadTransformers.applying(translateTransformation(offset))); - quads.addAll(new ConduitTextureEmissiveQuadTransformer(sprite(conduitBundle, conduit), 0) + quads.addAll(new ConduitTextureEmissiveQuadTransformer(sprite(bundleState.getTexture(conduit)), 0) .andThen(rotationTranslation) .process(modelOf(CONDUIT_CONNECTION).getQuads(state, preRotation, rand, extraData, renderType))); - ConduitCoreModelModifier conduitCoreModifier = ConduitCoreModelModifiers + ConduitModelModifier conduitModelModifier = ConduitModelModifiers .getModifier(conduit.value().type()); - if (conduitCoreModifier != null) { - quads.addAll(rotationTranslation.process(conduitCoreModifier.createConnectionQuads(conduit, - node, side, direction, rand, renderType))); + if (conduitModelModifier != null) { + quads.addAll(rotationTranslation.process(conduitModelModifier.createConnectionQuads(conduit, + extraWorldData, side, direction, rand, renderType))); } if (isEnd) { quads.addAll(rotationTranslation.process(modelOf(CONDUIT_CONNECTION_BOX).getQuads(state, preRotation, rand, extraData, renderType))); - ConnectionState connectionState = conduitBundle.getConnectionState(direction, conduit); - if (connectionState instanceof DynamicConnectionState dyn) { - IQuadTransformer color = rotationTranslation - .andThen(new ColorQuadTransformer(dyn.insertChannel(), dyn.extractChannel())); + var connectionState = bundleState.getConnectionState(direction, conduit); + if (connectionState != null) { + IQuadTransformer color = rotationTranslation.andThen(new ColorQuadTransformer( + connectionState.inputChannel(), connectionState.outputChannel())); + BakedModel model = null; - if (dyn.isExtract() && dyn.isInsert()) { + if (connectionState.canInput() && connectionState.canOutput()) { model = modelOf(CONDUIT_IO_IN_OUT); - } else if (dyn.isInsert()) { + } else if (connectionState.canInput()) { model = modelOf(CONDUIT_IO_IN); - } else if (dyn.isExtract()) { + } else if (connectionState.canOutput()) { model = modelOf(CONDUIT_IO_OUT); } @@ -157,23 +139,44 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction color.process(model.getQuads(state, preRotation, rand, extraData, renderType))); } - if (dyn.control() == RedstoneControl.ACTIVE_WITH_SIGNAL - || dyn.control() == RedstoneControl.ACTIVE_WITHOUT_SIGNAL) { + // TODO: Need support for dual-color redstone control. + if (connectionState.isRedstoneSensitive()) { quads.addAll(rotationTranslation - .andThen(new ColorQuadTransformer(null, dyn.redstoneChannel())) + .andThen(new ColorQuadTransformer(null, connectionState.redstoneChannel())) .process(modelOf(CONDUIT_IO_REDSTONE).getQuads(state, preRotation, rand, extraData, renderType))); + + // TODO: Use this to render two redstone signal colours? +// // Shrink the size +// var scale = new Vector3f(1, 0.5f, 1); +// +// // move into position +// var transformation = new Transformation(new Vector3f(0, 4 / 32f, 0), null, scale, null); +// var transformation1 = new Transformation(new Vector3f(0, 5 / 32f, 0), null, scale, null); +// +// quads.addAll(QuadTransformers.applying(transformation) +// .andThen(rotationTranslation) +// .andThen(new ColorQuadTransformer(null, connectionState.receiveRedstoneChannel())) +// .process(modelOf(CONDUIT_IO_REDSTONE).getQuads(state, preRotation, rand, +// extraData, renderType))); +// +// quads.addAll(QuadTransformers.applying(transformation1) +// .andThen(rotationTranslation) +//// .andThen(QuadTransformers.applying(translateTransformation(normal.mul(-1 / 16f)))) +// .andThen(new ColorQuadTransformer(null, DyeColor.GREEN)) +// .process(modelOf(CONDUIT_IO_REDSTONE).getQuads(state, preRotation, rand, +// extraData, renderType))); } } } } } - var allTypes = conduitBundle.getConduits(); + var allTypes = bundleState.conduits(); @Nullable Area box = null; - Map>, Integer> notRendered = new HashMap<>(); - List>> rendered = new ArrayList<>(); + Map>, Integer> notRendered = new HashMap<>(); + List>> rendered = new ArrayList<>(); for (int i = 0; i < allTypes.size(); i++) { var type = allTypes.get(i); @Nullable @@ -208,22 +211,22 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction box.makeContain(duplicatePosition); } } - for (Holder> toRender : rendered) { + for (Holder> toRender : rendered) { List offsetsForType = offsets.get(toRender); if (box == null || !box.contains(offsetsForType.getFirst())) { - quads.addAll(new ConduitTextureEmissiveQuadTransformer(sprite(conduitBundle, toRender), 0) + quads.addAll(new ConduitTextureEmissiveQuadTransformer(sprite(bundleState.getTexture(toRender)), 0) .andThen(QuadTransformers.applying(translateTransformation(offsetsForType.getFirst()))) .process(modelOf(CONDUIT_CORE).getQuads(state, side, rand, extraData, renderType))); } } if (box != null) { - for (Map.Entry>, Integer> notRenderedEntry : notRendered.entrySet()) { + for (Map.Entry>, Integer> notRenderedEntry : notRendered.entrySet()) { Vec3i offset = OffsetHelper.translationFor(axis, OffsetHelper.offsetConduit(notRenderedEntry.getValue(), allTypes.size())); if (!box.contains(offset)) { quads.addAll(new ConduitTextureEmissiveQuadTransformer( - sprite(conduitBundle, notRenderedEntry.getKey()), 0) + sprite(bundleState.getTexture(notRenderedEntry.getKey())), 0) .andThen(QuadTransformers.applying(translateTransformation(offset))) .process(modelOf(CONDUIT_CORE).getQuads(state, side, rand, extraData, renderType))); @@ -234,9 +237,9 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction .andThen(QuadTransformers.applying(translateTransformation(box.getMin()))) .process(modelOf(BOX).getQuads(state, side, rand, extraData, renderType))); } else { - for (Map.Entry>, Integer> notRenderedEntry : notRendered.entrySet()) { + for (Map.Entry>, Integer> notRenderedEntry : notRendered.entrySet()) { quads.addAll(new ConduitTextureEmissiveQuadTransformer( - sprite(conduitBundle, notRenderedEntry.getKey()), 0).andThen( + sprite(bundleState.getTexture(notRenderedEntry.getKey())), 0).andThen( QuadTransformers.applying(translateTransformation(OffsetHelper.translationFor(axis, OffsetHelper.offsetConduit(notRenderedEntry.getValue(), allTypes.size()))))) .process(modelOf(CONDUIT_CORE).getQuads(state, side, rand, extraData, renderType))); @@ -287,6 +290,10 @@ private static Transformation translateTransformation(Vec3i offset) { return new Transformation(scale(offset, 3 / 16f), null, null, null); } + private static Transformation translateTransformation(Vector3f offset) { + return new Transformation(offset, null, null, null); + } + private static Vector3f scale(Vec3i vector, float scaler) { return new Vector3f(vector.getX() * scaler, vector.getY() * scaler, vector.getZ() * scaler); } @@ -318,18 +325,26 @@ public TextureAtlasSprite getParticleIcon() { @Override public TextureAtlasSprite getParticleIcon(ModelData data) { - ConduitBundle conduitBundle = data.get(ConduitBundleBlockEntity.BUNDLE_MODEL_PROPERTY); // TODO temp particle - // fix - if (conduitBundle == null || conduitBundle.getConduits().isEmpty()) { + // This is only used for facades. + ConduitBundleRenderState bundleState = data.get(ConduitBundleRenderState.PROPERTY); + + if (bundleState == null) { return ModelHelper.getMissingTexture(); } - if (conduitBundle.hasFacade()) { + + if (bundleState.hasFacade() && FacadeHelper.areFacadesVisible()) { return Minecraft.getInstance() .getBlockRenderer() - .getBlockModel(conduitBundle.facade().get().defaultBlockState()) - .getParticleIcon(data.get(ConduitBundleBlockEntity.FACADE_MODEL_DATA)); + .getBlockModel(bundleState.facade()) + .getParticleIcon(data.get(FACADE_MODEL_DATA)); + } + + // Shouldn't be called anymore, but sensible fallback to have: + if (bundleState.conduits().isEmpty()) { + return ModelHelper.getMissingTexture(); } - return sprite(conduitBundle, conduitBundle.getConduits().getFirst()); + + return sprite(bundleState.getTexture(bundleState.conduits().getFirst())); } @Override @@ -340,7 +355,7 @@ public ItemOverrides getOverrides() { @Override public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull RandomSource rand, @NotNull ModelData data) { - ChunkRenderTypeSet facadeRenderTypes = data.get(ConduitBundleBlockEntity.FACADE_RENDERTYPE); + ChunkRenderTypeSet facadeRenderTypes = data.get(FACADE_RENDERTYPE); ChunkRenderTypeSet renderTypes = ChunkRenderTypeSet.of(RenderType.cutout()); if (facadeRenderTypes != null) { renderTypes = ChunkRenderTypeSet.union(renderTypes, facadeRenderTypes); @@ -352,22 +367,21 @@ public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull Ran public ModelData getModelData(BlockAndTintGetter level, BlockPos pos, BlockState state, ModelData modelData) { ModelData data = IDynamicBakedModel.super.getModelData(level, pos, state, modelData); ModelData.Builder builder = data.derive(); - ConduitBundle conduitBundle = data.get(ConduitBundleBlockEntity.BUNDLE_MODEL_PROPERTY); - if (conduitBundle != null && conduitBundle.hasFacade()) { - BlockState blockState = conduitBundle.facade().get().defaultBlockState(); + + ConduitBundleRenderState bundleState = data.get(ConduitBundleRenderState.PROPERTY); + if (bundleState != null && bundleState.hasFacade()) { + BlockState blockState = bundleState.facade(); BakedModel blockModel = Minecraft.getInstance().getBlockRenderer().getBlockModel(blockState); ModelData facadeData = blockModel.getModelData(level, pos, blockState, ModelData.EMPTY); - builder.with(ConduitBundleBlockEntity.FACADE_MODEL_DATA, facadeData); - builder.with(ConduitBundleBlockEntity.FACADE_RENDERTYPE, blockModel.getRenderTypes(blockState, + builder.with(FACADE_MODEL_DATA, facadeData); + builder.with(FACADE_RENDERTYPE, blockModel.getRenderTypes(blockState, new SingleThreadedRandomSource(state.getSeed(pos)), facadeData)); } return builder.build(); } - private static TextureAtlasSprite sprite(ConduitBundle conduitBundle, Holder> type) { - ConduitNode node = conduitBundle.getNodeFor(type); - ResourceLocation textureLocation = type.value().getTexture(node); - return Minecraft.getInstance().getModelManager().getAtlas(InventoryMenu.BLOCK_ATLAS).getSprite(textureLocation); + private static TextureAtlasSprite sprite(ResourceLocation location) { + return Minecraft.getInstance().getModelManager().getAtlas(InventoryMenu.BLOCK_ATLAS).getSprite(location); } private static boolean isMissingModel(BakedModel model) { diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleRenderState.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleRenderState.java new file mode 100644 index 0000000000..dd0f3f41b4 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitBundleRenderState.java @@ -0,0 +1,120 @@ +package com.enderio.conduits.client.model.conduit.bundle; + +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.bundle.ConduitBundleReader; +import com.enderio.conduits.api.connection.ConnectionStatus; +import com.enderio.conduits.common.conduit.OffsetHelper; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import me.liliandev.ensure.ensures.EnsureSide; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.client.model.data.ModelProperty; +import org.jetbrains.annotations.Nullable; + +public class ConduitBundleRenderState { + public static final ModelProperty PROPERTY = new ModelProperty<>(); + + private Direction.Axis mainAxis; + private List>> conduits; + private Map>, CompoundTag> extraWorldData; + private Map>>> conduitsByDirection; + private Map>, ConduitConnectionRenderState>> conduitConnections; + + private boolean hasFacade; + private BlockState facadeBlockstate; + private boolean doesFacadeHideConduits; + + @EnsureSide(EnsureSide.Side.CLIENT) + public static ConduitBundleRenderState of(ConduitBundleReader bundle) { + var renderState = new ConduitBundleRenderState(); + + renderState.mainAxis = OffsetHelper.findMainAxis(bundle); + renderState.conduits = List.copyOf(bundle.getConduits()); + + renderState.extraWorldData = new HashMap<>(); + for (var conduit : renderState.conduits) { + var tag = bundle.getConduitExtraWorldData(conduit); + if (tag != null) { + renderState.extraWorldData.put(conduit, tag.copy()); + } + } + + renderState.conduitsByDirection = new HashMap<>(); + for (var side : Direction.values()) { + renderState.conduitsByDirection.put(side, bundle.getConnectedConduits(side)); + } + + renderState.conduitConnections = new HashMap<>(); + for (var side : Direction.values()) { + HashMap>, ConduitConnectionRenderState> conduits = new HashMap<>(); + for (var conduit : renderState.conduits) { + if (bundle.getConnectionStatus(side, conduit) == ConnectionStatus.CONNECTED_BLOCK) { + var connectionConfig = bundle.getConnectionConfig(side, conduit); + var connectionRenderState = ConduitConnectionRenderState.of(conduit, connectionConfig); + conduits.put(conduit, connectionRenderState); + } + } + + renderState.conduitConnections.put(side, conduits); + } + + renderState.hasFacade = bundle.hasFacade(); + if (renderState.hasFacade) { + renderState.facadeBlockstate = bundle.getFacadeBlock().defaultBlockState(); + renderState.doesFacadeHideConduits = bundle.getFacadeType().doesHideConduits(); + } else { + renderState.facadeBlockstate = Blocks.AIR.defaultBlockState(); + renderState.doesFacadeHideConduits = false; + } + + return renderState; + } + + public List>> conduits() { + return conduits; + } + + @Nullable + public CompoundTag getExtraWorldData(Holder> conduit) { + return extraWorldData.get(conduit); + } + + public List>> getConnectedConduits(Direction side) { + return conduitsByDirection.getOrDefault(side, List.of()); + } + + public boolean isConnectionEndpoint(Direction side) { + return !conduitConnections.get(side).isEmpty(); + } + + public ConduitConnectionRenderState getConnectionState(Direction side, Holder> conduit) { + return conduitConnections.get(side).get(conduit); + } + + public Direction.Axis mainAxis() { + return mainAxis; + } + + public ResourceLocation getTexture(Holder> conduit) { + return conduit.value().getTexture(getExtraWorldData(conduit)); + } + + public boolean hasFacade() { + return hasFacade; + } + + public BlockState facade() { + return facadeBlockstate; + } + + public boolean doesFacadeHideConduits() { + return doesFacadeHideConduits; + } + +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java new file mode 100644 index 0000000000..e584553cfd --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/bundle/ConduitConnectionRenderState.java @@ -0,0 +1,47 @@ +package com.enderio.conduits.client.model.conduit.bundle; + +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.connection.config.IOConnectionConfig; +import com.enderio.conduits.api.connection.config.RedstoneSensitiveConnectionConfig; +import me.liliandev.ensure.ensures.EnsureSide; +import net.minecraft.core.Holder; +import net.minecraft.world.item.DyeColor; + +public record ConduitConnectionRenderState(boolean canInput, DyeColor inputChannel, boolean canOutput, + DyeColor outputChannel, boolean isRedstoneSensitive, DyeColor redstoneChannel) { + + public static ConduitConnectionRenderState fake() { + return new ConduitConnectionRenderState(false, DyeColor.GREEN, false, DyeColor.GREEN, false, DyeColor.RED); + } + + @EnsureSide(EnsureSide.Side.CLIENT) + public static ConduitConnectionRenderState of(Holder> conduit, ConnectionConfig connectionConfig) { + boolean canInput = false; + boolean canOutput = false; + DyeColor inputChannel = DyeColor.GREEN; + DyeColor outputChannel = DyeColor.GREEN; + if (connectionConfig instanceof IOConnectionConfig ioConnectionConfig) { + // TODO: Tidy the language here. + canInput = ioConnectionConfig.isSend(); + canOutput = ioConnectionConfig.isReceive(); + inputChannel = ioConnectionConfig.sendColor(); + outputChannel = ioConnectionConfig.receiveColor(); + } + + boolean isRedstoneSensitive = false; + DyeColor redstoneChannel = DyeColor.RED; + + if (connectionConfig instanceof RedstoneSensitiveConnectionConfig redstoneSensitiveConfig) { + // TODO: Support for multiple colours + var channelColors = redstoneSensitiveConfig.getRedstoneSignalColors(); + if (!channelColors.isEmpty()) { + isRedstoneSensitive = true; + redstoneChannel = channelColors.getFirst(); + } + } + + return new ConduitConnectionRenderState(canInput, inputChannel, canOutput, outputChannel, isRedstoneSensitive, + redstoneChannel); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/FacadeItemGeometry.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/facades/FacadeItemGeometry.java similarity index 97% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/model/FacadeItemGeometry.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/facades/FacadeItemGeometry.java index bb4f1a6814..479bb4d615 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/FacadeItemGeometry.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/facades/FacadeItemGeometry.java @@ -1,4 +1,4 @@ -package com.enderio.conduits.client.model; +package com.enderio.conduits.client.model.conduit.facades; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonObject; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/FacadeItemModel.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/facades/FacadeItemModel.java similarity index 94% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/model/FacadeItemModel.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/facades/FacadeItemModel.java index 01a9a7fbfa..ea48d198b6 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/FacadeItemModel.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/facades/FacadeItemModel.java @@ -1,4 +1,4 @@ -package com.enderio.conduits.client.model; +package com.enderio.conduits.client.model.conduit.facades; import static com.enderio.conduits.client.ConduitClientSetup.modelOf; @@ -100,10 +100,12 @@ public List getRenderTypes(ItemStack itemStack, boolean fabulous) { if (paintData == null) { return List.of(RenderType.cutout()); } + + var paintStack = paintData.paint().asItem().getDefaultInstance(); return Minecraft.getInstance() .getItemRenderer() - .getModel(paintData.paint().asItem().getDefaultInstance(), null, null, 0) - .getRenderTypes(itemStack, fabulous); + .getModel(paintStack, null, null, 0) + .getRenderTypes(paintStack, fabulous); } @Override diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/ConduitCoreModelModifiers.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/ConduitModelModifiers.java similarity index 61% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/ConduitCoreModelModifiers.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/ConduitModelModifiers.java index 7e831c205f..6623cc3d2d 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/ConduitCoreModelModifiers.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/ConduitModelModifiers.java @@ -1,24 +1,23 @@ package com.enderio.conduits.client.model.conduit.modifier; import com.enderio.conduits.api.ConduitType; -import com.enderio.conduits.api.model.ConduitCoreModelModifier; -import com.enderio.conduits.api.model.RegisterConduitCoreModelModifiersEvent; -import me.liliandev.ensure.ensures.EnsureSide; -import net.minecraft.client.resources.model.ModelResourceLocation; -import net.neoforged.fml.ModLoader; -import org.jetbrains.annotations.Nullable; - +import com.enderio.conduits.api.model.ConduitModelModifier; +import com.enderio.conduits.api.model.RegisterConduitModelModifiersEvent; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import me.liliandev.ensure.ensures.EnsureSide; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.neoforged.fml.ModLoader; +import org.jetbrains.annotations.Nullable; -public class ConduitCoreModelModifiers { - private static Map, ConduitCoreModelModifier> MODIFIERS; +public class ConduitModelModifiers { + private static Map, ConduitModelModifier> MODIFIERS; @EnsureSide(EnsureSide.Side.CLIENT) public static void init() { - var event = new RegisterConduitCoreModelModifiersEvent(); + var event = new RegisterConduitModelModifiersEvent(); ModLoader.postEvent(event); var factories = event.getModifiers(); @@ -28,12 +27,15 @@ public static void init() { @EnsureSide(EnsureSide.Side.CLIENT) @Nullable - public static ConduitCoreModelModifier getModifier(ConduitType type) { + public static ConduitModelModifier getModifier(ConduitType type) { return MODIFIERS.get(type); } @EnsureSide(EnsureSide.Side.CLIENT) public static Set getAllModelDependencies() { - return MODIFIERS.values().stream().flatMap(modifier -> modifier.getModelDependencies().stream()).collect(Collectors.toSet()); + return MODIFIERS.values() + .stream() + .flatMap(modifier -> modifier.getModelDependencies().stream()) + .collect(Collectors.toSet()); } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/FluidConduitCoreModelModifier.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/FluidConduitModelModifier.java similarity index 62% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/FluidConduitCoreModelModifier.java rename to enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/FluidConduitModelModifier.java index aae49e0196..4fe465bfb9 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/FluidConduitCoreModelModifier.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/model/conduit/modifier/FluidConduitModelModifier.java @@ -2,12 +2,10 @@ import com.enderio.base.api.EnderIO; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitNode; -import com.enderio.conduits.api.model.ConduitCoreModelModifier; +import com.enderio.conduits.api.model.ConduitModelModifier; import com.enderio.conduits.common.conduit.type.fluid.FluidConduit; -import com.enderio.conduits.common.conduit.type.fluid.FluidConduitData; -import com.enderio.conduits.common.init.ConduitTypes; import com.enderio.core.client.RenderUtil; +import java.util.List; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; @@ -15,6 +13,9 @@ import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.core.Direction; import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; import net.minecraft.util.RandomSource; import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.level.block.Blocks; @@ -25,24 +26,33 @@ import net.neoforged.neoforge.client.model.data.ModelData; import org.jetbrains.annotations.Nullable; -import java.util.List; - -public class FluidConduitCoreModelModifier implements ConduitCoreModelModifier { +public class FluidConduitModelModifier implements ConduitModelModifier { - private static final ModelResourceLocation FLUID_MODEL = ModelResourceLocation.standalone(EnderIO.loc("block/extra/fluids")); + private static final ModelResourceLocation FLUID_MODEL = ModelResourceLocation + .standalone(EnderIO.loc("block/extra/fluids")); @Override - public List createConnectionQuads(Holder> conduit, ConduitNode node, @Nullable Direction facing, Direction connectionDirection, RandomSource rand, - @Nullable RenderType type) { - if (!(conduit.value() instanceof FluidConduit fluidConduit && fluidConduit.isMultiFluid())) { + public List createConnectionQuads(Holder> conduit, @Nullable CompoundTag extraWorldData, + @Nullable Direction facing, Direction connectionDirection, RandomSource rand, @Nullable RenderType type) { + if (!(conduit.value() instanceof FluidConduit fluidConduit)) { + return List.of(); + } + + if (fluidConduit.isMultiFluid()) { + return List.of(); + } + + if (extraWorldData == null || !extraWorldData.contains("LockedFluid")) { return List.of(); } - FluidConduitData data = node.getData(ConduitTypes.Data.FLUID.get()); + ResourceLocation lockedFluidId = ResourceLocation.parse(extraWorldData.getString("LockedFluid")); + Fluid lockedFluid = BuiltInRegistries.FLUID.get(lockedFluidId); - if (data != null && !data.lockedFluid().isSame(Fluids.EMPTY)) { - return new FluidPaintQuadTransformer(data.lockedFluid()) - .process(Minecraft.getInstance().getModelManager().getModel(FLUID_MODEL) + if (!lockedFluid.isSame(Fluids.EMPTY)) { + return new FluidPaintQuadTransformer(lockedFluid).process(Minecraft.getInstance() + .getModelManager() + .getModel(FLUID_MODEL) .getQuads(Blocks.COBBLESTONE.defaultBlockState(), facing, rand, ModelData.EMPTY, type)); } @@ -58,12 +68,15 @@ private record FluidPaintQuadTransformer(Fluid fluid) implements IQuadTransforme @Override public void processInPlace(BakedQuad quad) { IClientFluidTypeExtensions clientExtension = IClientFluidTypeExtensions.of(fluid); - TextureAtlasSprite sprite = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS) - .apply(clientExtension.getStillTexture()); + TextureAtlasSprite sprite = Minecraft.getInstance() + .getTextureAtlas(InventoryMenu.BLOCK_ATLAS) + .apply(clientExtension.getStillTexture()); for (int i = 0; i < 4; i++) { float[] uv0 = RenderUtil.unpackVertices(quad.getVertices(), i, IQuadTransformer.UV0, 2); - uv0[0] = (uv0[0] - quad.getSprite().getU0()) * sprite.contents().width() / quad.getSprite().contents().height() + sprite.getU0(); - uv0[1] = (uv0[1] - quad.getSprite().getV0()) * sprite.contents().width() / quad.getSprite().contents().height() + sprite.getV0(); + uv0[0] = (uv0[0] - quad.getSprite().getU0()) * sprite.contents().width() + / quad.getSprite().contents().height() + sprite.getU0(); + uv0[1] = (uv0[1] - quad.getSprite().getV0()) * sprite.contents().width() + / quad.getSprite().contents().height() + sprite.getV0(); int[] packedTextureData = RenderUtil.packUV(uv0[0], uv0[1]); quad.getVertices()[IQuadTransformer.UV0 + i * IQuadTransformer.STRIDE] = packedTextureData[0]; quad.getVertices()[IQuadTransformer.UV0 + 1 + i * IQuadTransformer.STRIDE] = packedTextureData[1]; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/particle/ConduitBreakParticle.java b/enderio-conduits/src/main/java/com/enderio/conduits/client/particle/ConduitBreakParticle.java index ce816d49d8..915c3645b6 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/particle/ConduitBreakParticle.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/client/particle/ConduitBreakParticle.java @@ -1,7 +1,8 @@ package com.enderio.conduits.client.particle; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.common.conduit.ConduitShape; +import com.enderio.conduits.common.conduit.bundle.ConduitShape; +import java.util.List; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.ParticleEngine; @@ -9,23 +10,25 @@ import net.minecraft.client.particle.TextureSheetParticle; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.inventory.InventoryMenu; -import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; - -import java.util.List; +import net.minecraft.world.phys.shapes.VoxelShape; public class ConduitBreakParticle extends TextureSheetParticle { private final BlockPos pos; private final float uo; private final float vo; - public ConduitBreakParticle(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, BlockPos pos, ResourceLocation texture) { + public ConduitBreakParticle(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, + double zSpeed, BlockPos pos, ResourceLocation texture) { super(level, x, y, z, xSpeed, ySpeed, zSpeed); this.pos = pos; - this.setSprite(Minecraft.getInstance().getModelManager().getAtlas(InventoryMenu.BLOCK_ATLAS).getSprite(texture)); + this.setSprite( + Minecraft.getInstance().getModelManager().getAtlas(InventoryMenu.BLOCK_ATLAS).getSprite(texture)); this.gravity = 1.0F; this.rCol = 0.6F; this.gCol = 0.6F; @@ -61,18 +64,23 @@ public int getLightColor(float partialTick) { return i == 0 && this.level.hasChunkAt(this.pos) ? LevelRenderer.getLightColor(this.level, this.pos) : i; } - public static void addDestroyEffects(BlockPos pos, Conduit conduit) { - Level level = Minecraft.getInstance().level; + public static void addDestroyEffects(BlockPos pos, BlockState state, Conduit conduit) { + ClientLevel level = Minecraft.getInstance().level; + if (level == null) { + return; + } + ParticleEngine engine = Minecraft.getInstance().particleEngine; - List boxes = ConduitShape.CONNECTION.toAabbs(); - double countMult = 1D / boxes.size(); - boxes.forEach(aabb -> { - double sizeX = Math.min(1D, aabb.maxX - aabb.minX); - double sizeY = Math.min(1D, aabb.maxY - aabb.minY); - double sizeZ = Math.min(1D, aabb.maxZ - aabb.minZ); - int xCount = Math.max(2, Mth.ceil(sizeX / 0.25D * countMult)); - int yCount = Math.max(2, Mth.ceil(sizeY / 0.25D * countMult)); - int zCount = Math.max(2, Mth.ceil(sizeZ / 0.25D * countMult)); +// List boxes = ConduitShape.CONNECTION.toAabbs(); + VoxelShape shape = state.getShape(level, pos); + + shape.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> { + double sizeX = Math.min(1D, maxX - minX); + double sizeY = Math.min(1D, maxY - minY); + double sizeZ = Math.min(1D, maxZ - minZ); + int xCount = Math.max(2, Mth.ceil(sizeX / 0.25D)); + int yCount = Math.max(2, Mth.ceil(sizeY / 0.25D)); + int zCount = Math.max(2, Mth.ceil(sizeZ / 0.25D)); for (int iX = 0; iX < xCount; ++iX) { for (int iY = 0; iY < yCount; ++iY) { @@ -80,13 +88,60 @@ public static void addDestroyEffects(BlockPos pos, Conduit conduit) { double offX = ((double) iX + 0.5D) / (double) xCount; double offY = ((double) iY + 0.5D) / (double) yCount; double offZ = ((double) iZ + 0.5D) / (double) zCount; - double x = pos.getX() + offX * sizeX + aabb.minX; - double y = pos.getY() + offY * sizeY + aabb.minY; - double z = pos.getZ() + offZ * sizeZ + aabb.minZ; - engine.add(new ConduitBreakParticle((ClientLevel) level, x, y, z, offX - 0.5D, offY - 0.5D, offZ - 0.5D, pos, conduit.texture())); + double x = pos.getX() + offX * sizeX + minX; + double y = pos.getY() + offY * sizeY + minY; + double z = pos.getZ() + offZ * sizeZ + minZ; + engine.add(new ConduitBreakParticle(level, x, y, z, offX - 0.5D, offY - 0.5D, offZ - 0.5D, pos, + conduit.texture())); } } } }); } + + public static void addCrackEffects(BlockPos pos, BlockState state, Conduit conduit, Direction side) { + ClientLevel level = Minecraft.getInstance().level; + if (level == null) { + return; + } + + ParticleEngine engine = Minecraft.getInstance().particleEngine; + List boxes = ConduitShape.CONNECTION.toAabbs(); + double countMult = 1D / boxes.size(); + + int i = pos.getX(); + int j = pos.getY(); + int k = pos.getZ(); + float f = 0.1F; + AABB aabb = state.getShape(level, pos).bounds(); + double x = (double) i + level.getRandom().nextDouble() * (aabb.maxX - aabb.minX - 0.2F) + 0.1F + aabb.minX; + double y = (double) j + level.getRandom().nextDouble() * (aabb.maxY - aabb.minY - 0.2F) + 0.1F + aabb.minY; + double z = (double) k + level.getRandom().nextDouble() * (aabb.maxZ - aabb.minZ - 0.2F) + 0.1F + aabb.minZ; + if (side == Direction.DOWN) { + y = (double) j + aabb.minY - 0.1F; + } + + if (side == Direction.UP) { + y = (double) j + aabb.maxY + 0.1F; + } + + if (side == Direction.NORTH) { + z = (double) k + aabb.minZ - 0.1F; + } + + if (side == Direction.SOUTH) { + z = (double) k + aabb.maxZ + 0.1F; + } + + if (side == Direction.WEST) { + x = (double) i + aabb.minX - 0.1F; + } + + if (side == Direction.EAST) { + x = (double) i + aabb.maxX + 0.1F; + } + + engine.add(new ConduitBreakParticle(level, x, y, z, 0.0D, 0.0D, 0.0D, pos, conduit.texture()).setPower(0.2F) + .scale(0.6F)); + } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/components/ExtractionSpeedUpgrade.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/components/ExtractionSpeedUpgrade.java deleted file mode 100644 index 67a4b990b4..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/components/ExtractionSpeedUpgrade.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.enderio.conduits.common.components; - -import com.enderio.conduits.api.upgrade.ConduitUpgrade; -import net.minecraft.core.component.DataComponentType; -import net.minecraft.world.item.ItemStack; - -import java.util.function.Supplier; - -public final class ExtractionSpeedUpgrade implements ConduitUpgrade { - private final Supplier> componentType; - private final ItemStack itemStack; - - public ExtractionSpeedUpgrade(Supplier> componentType, ItemStack itemStack) { - this.componentType = componentType; - this.itemStack = itemStack; - } - - public int tier() { - return itemStack.getOrDefault(componentType.get(), 0); - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitA11yManager.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitA11yManager.java new file mode 100644 index 0000000000..f74d43eef2 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitA11yManager.java @@ -0,0 +1,39 @@ +package com.enderio.conduits.common.conduit; + +import com.enderio.conduits.EnderIOConduits; +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.common.init.ConduitComponents; +import net.minecraft.core.Holder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.event.entity.living.LivingEquipmentChangeEvent; +import org.jetbrains.annotations.Nullable; + +/** + * A11Y tools for conduit block behaviours. + */ +@EventBusSubscriber(/* value = Dist.CLIENT, */modid = EnderIOConduits.MODULE_MOD_ID, bus = EventBusSubscriber.Bus.GAME) +public class ConduitA11yManager { + + private static Holder> heldConduit; + + @Nullable + public static Holder> getHeldConduit() { + return heldConduit; + } + + @SubscribeEvent + public static void onEquipmentChanged(LivingEquipmentChangeEvent event) { + if (event.getEntity() instanceof Player player) { + // Only does the main hand + ItemStack mainItem = player.getMainHandItem(); + if (mainItem.has(ConduitComponents.CONDUIT)) { + heldConduit = mainItem.get(ConduitComponents.CONDUIT); + } else { + heldConduit = null; + } + } + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitApiImpl.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitApiImpl.java index 795d75ac10..3d0fe35d8c 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitApiImpl.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitApiImpl.java @@ -10,12 +10,17 @@ public class ConduitApiImpl implements ConduitApi { @Override - public ItemStack getStackForType(Holder> conduit, int count) { + public ItemStack getStackForType(Holder> conduit, int count) { return ConduitBlockItem.getStackFor(conduit, count); } @Override - public Ingredient getIngredientForType(Holder> conduit) { + public Ingredient getIngredientForType(Holder> conduit) { return ConduitIngredient.of(conduit); } + + @Override + public int getConduitSortIndex(Holder> conduit) { + return ConduitSorter.getSortIndex(conduit); + } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java index 4363fbb3f9..cfd8057018 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBlockItem.java @@ -37,7 +37,7 @@ public ConduitBlockItem(Block block, Properties properties) { super(block, properties); } - public static ItemStack getStackFor(Holder> conduit, int count) { + public static ItemStack getStackFor(Holder> conduit, int count) { var stack = new ItemStack(ConduitBlocks.CONDUIT.asItem(), count); stack.set(ConduitComponents.CONDUIT, conduit); return stack; @@ -45,7 +45,7 @@ public static ItemStack getStackFor(Holder> conduit, int count) { @Override public Component getName(ItemStack pStack) { - Holder> conduit = pStack.get(ConduitComponents.CONDUIT); + Holder> conduit = pStack.get(ConduitComponents.CONDUIT); if (conduit == null) { return super.getName(pStack); } @@ -66,11 +66,8 @@ public InteractionResult place(BlockPlaceContext context) { @Nullable Player player = context.getPlayer(); BlockPos blockpos = context.getClickedPos(); - ItemStack itemstack = context.getItemInHand(); - Holder> conduit = itemstack.get(ConduitComponents.CONDUIT); - - // Pass through to existing block. + // Allow placing from the edge of an adjacent block BlockState blockState = level.getBlockState(blockpos); if (!blockState.canBeReplaced()) { // noinspection DataFlowIssue @@ -86,7 +83,7 @@ public InteractionResult place(BlockPlaceContext context) { @Override public void appendHoverText(ItemStack stack, TooltipContext context, List tooltipComponents, TooltipFlag tooltipFlag) { - Holder> conduit = stack.get(ConduitComponents.CONDUIT); + Holder> conduit = stack.get(ConduitComponents.CONDUIT); if (conduit != null) { conduit.value().addToTooltip(context, tooltipComponents::add, tooltipFlag); @@ -133,7 +130,7 @@ public static void addToCreativeTabs(BuildCreativeModeTabContentsEvent event) { } } - private static > int compareConduitTo(Conduit o1, Conduit o2) { + private static > int compareConduitTo(Conduit o1, Conduit o2) { return o1.compareTo((T) o2); } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBundle.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBundle.java deleted file mode 100644 index 4314525c32..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitBundle.java +++ /dev/null @@ -1,558 +0,0 @@ -package com.enderio.conduits.common.conduit; - -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitCapabilities; -import com.enderio.conduits.api.SlotType; -import com.enderio.conduits.api.facade.FacadeType; -import com.enderio.conduits.common.conduit.connection.ConnectionState; -import com.enderio.conduits.common.conduit.connection.DynamicConnectionState; -import com.enderio.conduits.common.conduit.connection.StaticConnectionStates; -import com.enderio.core.common.network.NetworkDataSlot; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import me.liliandev.ensure.ensures.EnsureSide; -import net.minecraft.Util; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.neoforged.fml.loading.FMLLoader; -import net.neoforged.fml.util.thread.EffectiveSide; -import org.jetbrains.annotations.Nullable; - -public final class ConduitBundle { - - // Do not change this value unless you fix the OffsetHelper - public static final int MAX_CONDUITS = 9; - - public static Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - BlockPos.CODEC.fieldOf("pos").forGetter(i -> i.pos), - Conduit.CODEC.listOf().fieldOf("conduits").forGetter(i -> i.conduits), - Codec.unboundedMap(Direction.CODEC, ConduitConnection.CODEC) - .fieldOf("connections") - .forGetter(i -> i.connections), - ItemStack.OPTIONAL_CODEC.optionalFieldOf("facade", ItemStack.EMPTY).forGetter(i -> i.facadeItem), - Codec.unboundedMap(Conduit.CODEC, ConduitGraphObject.CODEC).fieldOf("nodes").forGetter(i -> i.conduitNodes)) - .apply(instance, ConduitBundle::new)); - - public static StreamCodec STREAM_CODEC = StreamCodec.composite( - BlockPos.STREAM_CODEC, i -> i.pos, Conduit.STREAM_CODEC.apply(ByteBufCodecs.list()), i -> i.conduits, - ByteBufCodecs.map(HashMap::new, Direction.STREAM_CODEC, ConduitConnection.STREAM_CODEC), i -> i.connections, - ItemStack.OPTIONAL_STREAM_CODEC, i -> i.facadeItem, - ByteBufCodecs.map(HashMap::new, Conduit.STREAM_CODEC, ConduitGraphObject.STREAM_CODEC), i -> i.conduitNodes, - ConduitBundle::new); - - public static NetworkDataSlot.CodecType DATA_SLOT_TYPE = new NetworkDataSlot.CodecType<>(CODEC, - STREAM_CODEC); - - private final Map connections = new EnumMap<>(Direction.class); - private final List>> conduits = new ArrayList<>(); - - // fill back after world save - private final Map>, ConduitGraphObject> conduitNodes = new HashMap<>(); - private final BlockPos pos; - - private ItemStack facadeItem = ItemStack.EMPTY; - - @Nullable - private Runnable onChangedRunnable; - - public ConduitBundle(Runnable onChanged, BlockPos pos) { - this.onChangedRunnable = onChanged; - for (Direction value : Direction.values()) { - connections.put(value, new ConduitConnection()); - } - this.pos = pos; - } - - private ConduitBundle(BlockPos pos, List>> conduits, - Map connections, ItemStack facadeItem, - Map>, ConduitGraphObject> conduitNodes) { - - this.pos = pos; - this.conduits.addAll(conduits); - this.connections.putAll(connections); - this.conduitNodes.putAll(conduitNodes); - this.facadeItem = facadeItem; - } - - // TODO: I kind of want to get rid of this. - public void setOnChangedRunnable(Runnable onChangedRunnable) { - this.onChangedRunnable = onChangedRunnable; - } - - public void onChanged() { - if (onChangedRunnable != null) { - onChangedRunnable.run(); - } - } - - /** - * @return an action containing the conduit that is now not in this bundle - */ - public RightClickAction addConduit(Level level, Holder> conduit, Player player) { - if (conduits.size() == MAX_CONDUITS) { - return new RightClickAction.Blocked(); - } - - if (conduits.contains(conduit)) { - return new RightClickAction.Blocked(); - } - - // New node - ConduitGraphObject node = new ConduitGraphObject(pos); - - // upgrade a conduit - Optional>> first = conduits.stream() - .filter(existingConduit -> existingConduit.value().canBeReplacedBy(conduit)) - .findFirst(); - if (first.isPresent()) { - int index = conduits.indexOf(first.get()); - conduits.set(index, conduit); - - ConduitGraphObject prevNode = conduitNodes.remove(first.get()); - - if (prevNode != null) { - node = new ConduitGraphObject(pos, prevNode.conduitDataContainer()); // new node with old data - conduit.value().onRemoved(prevNode, level, pos); - if (!level.isClientSide() && prevNode.getGraph() != null) { - prevNode.getGraph().remove(prevNode); - } - } - - conduitNodes.put(conduit, node); - conduit.value().onCreated(node, level, pos, player); - onChanged(); - - return new RightClickAction.Upgrade(first.get()); - } - - // some conduit says no (like higher energy conduit) - if (conduits.stream() - .anyMatch(existingConduit -> !existingConduit.value().canBeInSameBundle(conduit) - || !conduit.value().canBeInSameBundle(existingConduit))) { - return new RightClickAction.Blocked(); - } - - // sort the list, so order is consistent - int id = ConduitSorter.getSortIndex(conduit); - var addBefore = conduits.stream().filter(existing -> ConduitSorter.getSortIndex(existing) > id).findFirst(); - if (addBefore.isPresent()) { - var value = conduits.indexOf(addBefore.get()); - conduits.add(value, conduit); - conduitNodes.put(conduit, node); - - conduit.value().onCreated(node, level, pos, player); - - for (Direction direction : Direction.values()) { - connections.get(direction).addType(value); - } - } else { - conduits.add(conduit); - conduitNodes.put(conduit, node); - if (conduits.size() != 1) { - // NeoForge contains a patch that calls onLoad after the conduit has been placed - // if it's the first one, so onCreated would be called twice. it's easier to - // detect here - conduit.value().onCreated(node, level, pos, player); - } - } - - onChanged(); - return new RightClickAction.Insert(); - } - - public void onLoad(Level level, BlockPos pos) { - for (Holder> conduit : conduits) { - var node = getNodeFor(conduit); - conduit.value().onCreated(node, level, pos, null); - } - } - - /** - * @return if this bundle is empty and the block has to be removed - * @throws IllegalArgumentException if this conduit is not in the conduit bundle and we are in dev env - */ - public boolean removeConduit(Level level, Holder> conduit) { - int index = conduits.indexOf(conduit); - if (index == -1) { - if (!FMLLoader.isProduction()) { - throw new IllegalArgumentException( - "Conduit: " + conduit.getRegisteredName() + " is not present in conduit bundle " - + Arrays.toString(conduits.stream().map(Holder::getRegisteredName).toArray())); - } - - return conduits.isEmpty(); - } - - for (Direction direction : Direction.values()) { - connections.get(direction).removeType(index); - } - - if (EffectiveSide.get().isServer()) { - var node = getNodeForTypeExact(conduit); - if (node != null) { - removeNode(level, conduit, node); - } - } - - conduits.remove(index); - onChanged(); - return conduits.isEmpty(); - } - - // endregion - - public List>> getConduits() { - return conduits; - } - - // region Connections - - public List>> getConnectedConduits(Direction direction) { - return connections.get(direction).getConnectedTypes(this); - } - - // Not a fan of this. - @Deprecated(forRemoval = true) - public ConnectionState getConnectionState(Direction direction, int index) { - return connections.get(direction).getConnectionState(index); - } - - public ConnectionState getConnectionState(Direction direction, Holder> conduit) { - return connections.get(direction).getConnectionState(getConduitIndex(conduit)); - } - - public void setConnectionState(Direction direction, Holder> conduit, ConnectionState state) { - connections.get(direction).setConnectionState(getConduitIndex(conduit), state); - onChanged(); - } - - public boolean isConnectionEnd(Direction direction) { - return connections.get(direction).isEnd(); - } - - // Not a fan of this. - @Deprecated(forRemoval = true) - public void disableConduit(Direction direction, int index) { - connections.get(direction).disableType(index); - onChanged(); - } - - public void disableConduit(Direction direction, Holder> conduit) { - disableConduit(direction, getConduitIndex(conduit)); - } - - public ItemStack getConnectionItem(Direction direction, int conduitIndex, SlotType slotType) { - return connections.get(direction).getItem(slotType, conduitIndex); - } - - public ItemStack getConnectionItem(Direction direction, Holder> conduit, SlotType slotType) { - return getConnectionItem(direction, getConduitIndex(conduit), slotType); - } - - public void setConnectionItem(Direction direction, int conduitIndex, SlotType slotType, ItemStack itemStack) { - connections.get(direction).setItem(slotType, conduitIndex, itemStack); - onChanged(); - } - - public void setConnectionItem(Direction direction, Holder> conduit, SlotType slotType, - ItemStack itemStack) { - setConnectionItem(direction, getConduitIndex(conduit), slotType, itemStack); - } - - // endregion - - // region Facades - - public boolean hasFacade() { - return !facadeItem.isEmpty() && facadeItem.getCapability(ConduitCapabilities.CONDUIT_FACADE_PROVIDER) != null; - } - - public ItemStack facadeItem() { - return facadeItem.copy(); - } - - public Optional facade() { - if (!hasFacade()) { - return Optional.empty(); - } - - return Optional.of( - Objects.requireNonNull(facadeItem.getCapability(ConduitCapabilities.CONDUIT_FACADE_PROVIDER)).block()); - } - - public Optional facadeType() { - if (!hasFacade()) { - return Optional.empty(); - } - - return Optional.of( - Objects.requireNonNull(facadeItem.getCapability(ConduitCapabilities.CONDUIT_FACADE_PROVIDER)).type()); - } - - public void facade(ItemStack facadeItem) { - this.facadeItem = facadeItem.copyWithCount(1); - onChanged(); - } - - public void clearFacade() { - facadeItem = ItemStack.EMPTY; - onChanged(); - } - - // endregion - - public void connectTo(Level level, BlockPos pos, Direction direction, Holder> conduit, boolean end) { - connections.get(direction) - .connectTo(level, pos, getNodeFor(conduit), direction, conduit, getConduitIndex(conduit), end); - onChanged(); - } - - public boolean disconnectFrom(Direction direction, Holder> conduit) { - for (int i = 0; i < conduits.size(); i++) { - if (conduit.value().canConnectTo(conduits.get(i))) { - connections.get(direction).tryDisconnect(i); - onChanged(); - return true; - } - } - return false; - } - - @Nullable - public ConduitGraphObject getNodeForTypeExact(Holder> conduit) { - return conduitNodes.get(conduit); - } - - public ConduitGraphObject getNodeFor(Holder> conduit) { - for (var entry : conduitNodes.entrySet()) { - if (entry.getKey().value().canConnectTo(conduit)) { - return conduitNodes.get(entry.getKey()); - } - } - - throw new IllegalStateException("no node matching original conduit"); - } - - public void setNodeFor(Holder> conduit, ConduitGraphObject node) { - conduitNodes.put(conduit, node); - for (var direction : Direction.values()) { - ConduitConnection connection = connections.get(direction); - int index = conduits.indexOf(conduit); - if (index >= 0) { - var state = connection.getConnectionState(index); - if (state instanceof DynamicConnectionState dynamicState) { - node.pushState(direction, dynamicState); - } - } - } - } - - private void removeNode(Level level, Holder> conduit, ConduitGraphObject node) { - conduit.value().onRemoved(node, level, pos); - if (node.getGraph() != null) { - node.getGraph().remove(node); - } - - conduitNodes.remove(conduit); - } - - public boolean hasType(Holder> conduitToFind) { - for (var conduit : conduits) { - if (conduit.value().canConnectTo(conduitToFind)) { - return true; - } - } - - return false; - } - - public int getConduitIndex(Holder> conduit) { - for (int i = 0; i < conduits.size(); i++) { - if (conduits.get(i).value().canConnectTo(conduit)) { - return i; - } - } - throw new IllegalStateException("no matching conduit in bundle"); - } - - @Override - public int hashCode() { - int hash = Objects.hash(connections, conduits, facadeItem); - - // Manually hash the map, using hashContents instead of hashCode to avoid - // breaking the graph. - for (var entry : conduitNodes.entrySet()) { - hash = 31 * hash + entry.getKey().hashCode(); - hash = 31 * hash + entry.getValue().hashContents(); - } - - return hash; - } - - public Tag save(HolderLookup.Provider lookupProvider) { - return CODEC.encodeStart(lookupProvider.createSerializationContext(NbtOps.INSTANCE), this).getOrThrow(); - } - - public static ConduitBundle parse(HolderLookup.Provider lookupProvider, Tag tag) { - return CODEC.decode(lookupProvider.createSerializationContext(NbtOps.INSTANCE), tag).getOrThrow().getFirst(); - } - - @EnsureSide(EnsureSide.Side.CLIENT) - public ConduitBundle deepCopy() { - var bundle = new ConduitBundle(() -> { - }, pos); - bundle.conduits.addAll(conduits); - connections.forEach((dir, connection) -> bundle.connections.put(dir, connection.deepCopy())); - conduitNodes.forEach((conduit, node) -> bundle.setNodeFor(conduit, node.deepCopy())); - bundle.facadeItem = facadeItem.copy(); - return bundle; - } - - // TODO: Clean this up - private static final class ConduitConnection { - - public static Codec CODEC = ConnectionState.CODEC.listOf(0, MAX_CONDUITS) - .xmap(ConduitConnection::new, i -> Arrays.stream(i.connectionStates).toList()); - - public static StreamCodec STREAM_CODEC = ConnectionState.STREAM_CODEC - .apply(ByteBufCodecs.list()) - .map(ConduitConnection::new, i -> Arrays.stream(i.connectionStates).toList()); - - private final ConnectionState[] connectionStates = Util.make(() -> { - var states = new ConnectionState[MAX_CONDUITS]; - Arrays.fill(states, StaticConnectionStates.DISCONNECTED); - return states; - }); - - ConduitConnection() { - } - - private ConduitConnection(List connectionStates) { - if (connectionStates.size() > MAX_CONDUITS) { - throw new IllegalArgumentException( - "Cannot store more than " + MAX_CONDUITS + " conduit types per bundle."); - } - - for (var i = 0; i < connectionStates.size(); i++) { - this.connectionStates[i] = connectionStates.get(i); - } - } - - /** - * shift all behind that one to the back and set that index to null - */ - public void addType(int index) { - for (int i = MAX_CONDUITS - 1; i > index; i--) { - connectionStates[i] = connectionStates[i - 1]; - } - connectionStates[index] = StaticConnectionStates.DISCONNECTED; - } - - public void connectTo(Level level, BlockPos pos, ConduitGraphObject conduitGraphObject, Direction direction, - Holder> type, int typeIndex, boolean end) { - if (end) { - var state = DynamicConnectionState.defaultConnection(level, pos, direction, type); - connectionStates[typeIndex] = state; - conduitGraphObject.pushState(direction, state); - } else { - connectionStates[typeIndex] = StaticConnectionStates.CONNECTED; - } - } - - public void tryDisconnect(int typeIndex) { - if (connectionStates[typeIndex] != StaticConnectionStates.DISABLED) { - connectionStates[typeIndex] = StaticConnectionStates.DISCONNECTED; - } - } - - // TODO: Come back and review use of the term "Type" here. - - /** - * remove entry and shift all behind one to the front - */ - public void removeType(int index) { - connectionStates[index] = StaticConnectionStates.DISCONNECTED; - for (int i = index + 1; i < MAX_CONDUITS; i++) { - connectionStates[i - 1] = connectionStates[i]; - } - connectionStates[MAX_CONDUITS - 1] = StaticConnectionStates.DISCONNECTED; - } - - public void disconnectType(int index) { - connectionStates[index] = StaticConnectionStates.DISCONNECTED; - } - - public void disableType(int index) { - connectionStates[index] = StaticConnectionStates.DISABLED; - } - - public boolean isEnd() { - return Arrays.stream(connectionStates).anyMatch(DynamicConnectionState.class::isInstance); - } - - public List>> getConnectedTypes(ConduitBundle bundle) { - List>> connected = new ArrayList<>(); - for (int i = 0; i < connectionStates.length; i++) { - if (connectionStates[i].isConnection()) { - connected.add(bundle.getConduits().get(i)); - } - } - - return connected; - } - - public ConduitConnection deepCopy() { - ConduitConnection connection = new ConduitConnection(); - // connection states are not mutable (enum/record), so reference is fine - System.arraycopy(connectionStates, 0, connection.connectionStates, 0, MAX_CONDUITS); - return connection; - } - - public ConnectionState getConnectionState(int index) { - return connectionStates[index]; - } - - public void setConnectionState(int i, ConnectionState state) { - connectionStates[i] = state; - } - - public ItemStack getItem(SlotType type, int conduitIndex) { - if (connectionStates[conduitIndex] instanceof DynamicConnectionState dynamicConnectionState) { - return dynamicConnectionState.getItem(type); - } - - return ItemStack.EMPTY; - } - - public void setItem(SlotType type, int conduitIndex, ItemStack stack) { - if (connectionStates[conduitIndex] instanceof DynamicConnectionState dynamicConnectionState) { - connectionStates[conduitIndex] = dynamicConnectionState.withItem(type, stack); - } - } - - @Override - public int hashCode() { - // return i++; - return Objects.hash((Object[]) connectionStates); - } - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitGraphObject.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitGraphObject.java deleted file mode 100644 index dc992c0868..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitGraphObject.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.enderio.conduits.common.conduit; - -import com.enderio.base.api.UseOnly; -import com.enderio.base.api.filter.ResourceFilter; -import com.enderio.base.common.init.EIOCapabilities; -import com.enderio.conduits.api.ConduitCapabilities; -import com.enderio.conduits.api.ConduitData; -import com.enderio.conduits.api.ConduitDataType; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNode; -import com.enderio.conduits.api.upgrade.ConduitUpgrade; -import com.enderio.conduits.common.conduit.connection.DynamicConnectionState; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.gigaherz.graph3.Graph; -import dev.gigaherz.graph3.GraphObject; -import java.util.EnumMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.neoforged.fml.LogicalSide; -import org.jetbrains.annotations.Nullable; - -public class ConduitGraphObject implements GraphObject, ConduitNode { - - public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance - .group(BlockPos.CODEC.fieldOf("pos").forGetter(ConduitGraphObject::getPos), - ConduitDataContainer.CODEC.fieldOf("data").forGetter(i -> i.conduitDataContainer)) - .apply(instance, ConduitGraphObject::new)); - - public static final StreamCodec STREAM_CODEC = StreamCodec.composite( - BlockPos.STREAM_CODEC, ConduitGraphObject::getPos, ConduitDataContainer.STREAM_CODEC, - i -> i.conduitDataContainer, ConduitGraphObject::new); - - private final BlockPos pos; - - @Nullable - private Graph graph = null; - @Nullable - private WrappedConduitNetwork wrappedGraph = null; - - private final Map ioStates = new EnumMap<>(Direction.class); - private final ConduitDataContainer conduitDataContainer; - private final Map connectionStates = new EnumMap<>(Direction.class); - - public ConduitGraphObject(BlockPos pos) { - this.pos = pos; - this.conduitDataContainer = new ConduitDataContainer(); - } - - public ConduitGraphObject(BlockPos pos, ConduitDataContainer conduitDataContainer) { - this.pos = pos; - this.conduitDataContainer = conduitDataContainer; - } - - @Nullable - @Override - public Graph getGraph() { - return graph; - } - - @Override - public void setGraph(@Nullable Graph graph) { - this.graph = graph; - this.wrappedGraph = graph == null ? null : new WrappedConduitNetwork(graph); - } - - @Nullable - @Override - public ConduitNetwork getParentGraph() { - return wrappedGraph; - } - - public void pushState(Direction direction, DynamicConnectionState connectionState) { - this.connectionStates.put(direction, connectionState); - ioStates.put(direction, - IOState.of(connectionState.isInsert() ? connectionState.insertChannel() : null, - connectionState.isExtract() ? connectionState.extractChannel() : null, - connectionState.control(), connectionState.redstoneChannel())); - } - - public Optional getIOState(Direction direction) { - return Optional.ofNullable(ioStates.get(direction)); - } - - public void clearState(Direction direction) { - ioStates.remove(direction); - } - - public BlockPos getPos() { - return pos; - } - - // region Conduit Data - - // We're implementing ConduitDataAccessor for ease here, but we just pass - // through to the container. - - @Override - public boolean hasData(ConduitDataType type) { - return conduitDataContainer.hasData(type); - } - - @Override - public > @Nullable T getData(ConduitDataType type) { - return conduitDataContainer.getData(type); - } - - @Override - public > T getOrCreateData(ConduitDataType type) { - return conduitDataContainer.getOrCreateData(type); - } - - public ConduitDataContainer conduitDataContainer() { - return conduitDataContainer; - } - - public void handleClientChanges(ConduitDataContainer clientDataContainer) { - conduitDataContainer.handleClientChanges(clientDataContainer); - } - - // endregion - - @Override - public @Nullable ConduitUpgrade getUpgrade(Direction direction) { - return connectionStates.get(direction).upgradeExtract().getCapability(ConduitCapabilities.CONDUIT_UPGRADE); - } - - @Override - public @Nullable ResourceFilter getExtractFilter(Direction direction) { - return connectionStates.get(direction).filterExtract().getCapability(EIOCapabilities.Filter.ITEM); - } - - @Override - public @Nullable ResourceFilter getInsertFilter(Direction direction) { - return connectionStates.get(direction).filterInsert().getCapability(EIOCapabilities.Filter.ITEM); - } - - @UseOnly(LogicalSide.CLIENT) - public ConduitGraphObject deepCopy() { - return new ConduitGraphObject(pos, conduitDataContainer.deepCopy()); - } - - // Separate method to avoid breaking the graph - public int hashContents() { - return Objects.hash(pos, conduitDataContainer, ioStates, connectionStates); - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitGraphUtility.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitGraphUtility.java deleted file mode 100644 index c9ae43652d..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitGraphUtility.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.enderio.conduits.common.conduit; - -import com.enderio.conduits.api.Conduit; -import dev.gigaherz.graph3.Graph; -import dev.gigaherz.graph3.GraphObject; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.CompoundTag; - -import java.util.List; - -public class ConduitGraphUtility { - - public static void integrate(Holder> conduit, GraphObject graphObject, - List> neighbours) { - Graph.integrate(graphObject, neighbours, Graph::new, g -> ConduitGraphContext.createNetworkContext()); - } - - public static void integrateWithLoad(Holder> conduit, GraphObject graphObject, - List> neighbours, HolderLookup.Provider lookupProvider, CompoundTag contextTag) { - Graph.integrate(graphObject, neighbours, Graph::new, g -> ConduitGraphContext.loadNetworkContext(conduit, lookupProvider, contextTag)); - } - - public static void connect(Holder> conduit, GraphObject graphObject, - GraphObject neighbour) { - Graph.connect(graphObject, neighbour, Graph::new, g -> ConduitGraphContext.createNetworkContext()); - } - -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java index 4e8e07bd9f..f3e98b4275 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSavedData.java @@ -4,9 +4,12 @@ import com.enderio.conduits.api.Conduit; import com.enderio.conduits.api.EnderIOConduitsRegistries; import com.enderio.conduits.api.ticker.ConduitTicker; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; -import com.enderio.conduits.common.init.ConduitTypes; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlockEntity; +import com.enderio.conduits.common.conduit.graph.ConduitGraphContext; +import com.enderio.conduits.common.conduit.graph.ConduitGraphObject; +import com.enderio.conduits.common.conduit.graph.ConduitGraphUtility; +import com.enderio.conduits.common.conduit.graph.WrappedConduitNetwork; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import com.enderio.conduits.common.init.Conduits; import com.mojang.datafixers.util.Pair; import com.mojang.logging.LogUtils; @@ -32,6 +35,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.DyeColor; import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; import net.minecraft.world.level.saveddata.SavedData; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; @@ -42,10 +46,10 @@ @EventBusSubscriber(modid = EnderIOConduits.MODULE_MOD_ID) public class ConduitSavedData extends SavedData { - private final Map>, List>> networks = new HashMap<>(); + private final Map>, List>> networks = new HashMap<>(); - // Used to find the NodeIdentifier(s) of a conduit when it is loaded - private final Map>, Map>> deserializedNodes = new HashMap<>(); + // Used to find the ConduitGraphObject(s) of a conduit when it is loaded + private final Map>, Map>> deserializedNodes = new HashMap<>(); private static final Logger LOGGER = LogUtils.getLogger(); @@ -71,12 +75,12 @@ private ConduitSavedData(CompoundTag nbt, HolderLookup.Provider lookupProvider) ListTag graphsTag = nbt.getList(KEY_GRAPHS, Tag.TAG_COMPOUND); for (Tag tag : graphsTag) { CompoundTag typedGraphTag = (CompoundTag) tag; - ResourceKey> conduitKey = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + ResourceKey> conduitKey = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, ResourceLocation.parse(typedGraphTag.getString(KEY_TYPE))); var registry = lookupProvider.lookupOrThrow(EnderIOConduitsRegistries.Keys.CONDUIT); - Optional>> conduit = registry.get(conduitKey); + Optional>> conduit = registry.get(conduitKey); if (conduit.isPresent()) { ListTag graphsForTypeTag = typedGraphTag.getList(KEY_GRAPHS, Tag.TAG_COMPOUND); @@ -87,7 +91,8 @@ private ConduitSavedData(CompoundTag nbt, HolderLookup.Provider lookupProvider) } } - private void deserializeGraphs(HolderLookup.Provider lookupProvider, Holder> conduit, ListTag graphs) { + private void deserializeGraphs(HolderLookup.Provider lookupProvider, Holder> conduit, + ListTag graphs) { for (Tag tag1 : graphs) { CompoundTag graphTag = (CompoundTag) tag1; @@ -159,7 +164,7 @@ private void deserializeGraphs(HolderLookup.Provider lookupProvider, Holder> type = entry.getKey(); + Holder> type = entry.getKey(); List> graphs = entry.getValue(); if (graphs.isEmpty() || !type.isBound()) { continue; @@ -244,7 +249,7 @@ private static CompoundTag serializeGraph(HolderLookup.Provider lookupProvider, // endregion - private void merge(Holder> conduit, GraphObject object, + private void merge(Holder> conduit, GraphObject object, List> connections) { var filteredConnections = connections.stream() .filter(pair -> (pair.getFirst() == object || pair.getSecond() == object)) @@ -265,7 +270,7 @@ private void merge(Holder> conduit, GraphObject } @Nullable - public ConduitGraphObject takeUnloadedNodeIdentifier(Holder> conduit, BlockPos pos) { + public ConduitGraphObject takeUnloadedNodeIdentifier(Holder> conduit, BlockPos pos) { ChunkPos chunkPos = new ChunkPos(pos); Map> typeMap = deserializedNodes.get(conduit); @@ -273,6 +278,7 @@ public ConduitGraphObject takeUnloadedNodeIdentifier(Holder> conduit, LOGGER.warn("Conduit data is missing!"); return null; } + Map chunkMap = typeMap.get(chunkPos); if (chunkMap == null) { LOGGER.warn("Conduit data is missing!"); @@ -292,7 +298,7 @@ public ConduitGraphObject takeUnloadedNodeIdentifier(Holder> conduit, return node; } - public void putUnloadedNodeIdentifier(Holder> conduit, BlockPos pos, ConduitGraphObject node) { + public void putUnloadedNodeIdentifier(Holder> conduit, BlockPos pos, ConduitGraphObject node) { ChunkPos chunkPos = new ChunkPos(pos); Map> typeMap = deserializedNodes.computeIfAbsent(conduit, k -> new HashMap<>()); @@ -320,7 +326,7 @@ private void tick(ServerLevel serverLevel) { || graph.getObjects().iterator().next().getGraph() != graph); } - Registry> conduitRegistry = serverLevel.registryAccess() + Registry> conduitRegistry = serverLevel.registryAccess() .registryOrThrow(EnderIOConduitsRegistries.Keys.CONDUIT); for (var entry : networks.entrySet()) { @@ -334,10 +340,11 @@ private void tick(ServerLevel serverLevel) { } } - private > void tickConduitGraph(ServerLevel serverLevel, Holder> conduit, + private > void tickConduitGraph(ServerLevel serverLevel, Holder> conduit, int conduitId, ConduitTicker ticker, Graph graph) { int conduitTickRate = conduit.value().graphTickRate(); + // TODO: Offsets for networks so they don't all tick on the same tick. if (serverLevel.getGameTime() % conduitTickRate == conduitId % conduitTickRate) { // noinspection unchecked ticker.tickGraph(serverLevel, (T) conduit.value(), new WrappedConduitNetwork(graph), @@ -345,34 +352,38 @@ private > void tickConduitGraph(ServerLevel serverLevel, Ho } } - private static boolean isRedstoneActive(ServerLevel serverLevel, BlockPos pos, DyeColor color) { - if (!serverLevel.isLoaded(pos) || !serverLevel.shouldTickBlocksAt(pos)) { + public static boolean isRedstoneActive(Level level, BlockPos pos, DyeColor color) { + if (!level.isLoaded(pos) || !level.shouldTickBlocksAt(pos)) { return false; } - if (!(serverLevel.getBlockEntity(pos) instanceof ConduitBundleBlockEntity blockEntity)) { + if (!(level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduitBundle)) { return false; } - // TODO: Decouple from hard-coded REDSTONE conduit. - var registry = serverLevel.holderLookup(EnderIOConduitsRegistries.Keys.CONDUIT); + // TODO: Decouple from hard-coded REDSTONE conduit like we have for the block. + var registry = level.holderLookup(EnderIOConduitsRegistries.Keys.CONDUIT); var redstoneConduit = registry.get(Conduits.REDSTONE); - if (redstoneConduit.isEmpty() || !blockEntity.getBundle().getConduits().contains(redstoneConduit.get())) { + if (redstoneConduit.isEmpty() || !conduitBundle.getConduits().contains(redstoneConduit.get())) { + return false; + } + + var node = conduitBundle.getConduitNode(redstoneConduit.get()); + if (node.getNetwork() == null) { return false; } - var node = blockEntity.getBundle().getNodeFor(redstoneConduit.get()); - RedstoneConduitData data = node.getData(ConduitTypes.Data.REDSTONE.get()); - return data != null && data.isActive(color); + var context = node.getNetwork().getContext(RedstoneConduitNetworkContext.TYPE); + return context != null && context.isActive(color); } - public static void addPotentialGraph(Holder> conduit, Graph graph, + public static void addPotentialGraph(Holder> conduit, Graph graph, ServerLevel level) { get(level).addPotentialGraph(conduit, graph); } - private void addPotentialGraph(Holder> conduit, Graph graph) { + private void addPotentialGraph(Holder> conduit, Graph graph) { if (!networks.computeIfAbsent(conduit, unused -> new ArrayList<>()).contains(graph)) { networks.get(conduit).add(graph); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitShape.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitShape.java deleted file mode 100644 index a95e2b6da2..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitShape.java +++ /dev/null @@ -1,188 +0,0 @@ -package com.enderio.conduits.common.conduit; - -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.common.Area; -import com.enderio.conduits.common.conduit.connection.DynamicConnectionState; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.Vec3i; -import net.minecraft.util.Mth; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.phys.HitResult; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.phys.shapes.BooleanOp; -import net.minecraft.world.phys.shapes.Shapes; -import net.minecraft.world.phys.shapes.VoxelShape; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class ConduitShape { - private final Map>, VoxelShape> conduitShapes = new HashMap<>(); - private final Map directionShapes = new HashMap<>(); - private static final VoxelShape CONNECTOR = Block.box(2.5f, 2.5, 15f, 13.5f, 13.5f, 16f); - public static final VoxelShape CONNECTION = Block.box(6.5f, 6.5f, 9.5, 9.5f, 9.5f, 16); - private static final VoxelShape CORE = Block.box(6.5f, 6.5f, 6.5f, 9.5f, 9.5f, 9.5f); - private VoxelShape totalShape = CORE; - - public ConduitShape() { - - } - - public void updateConduit(ConduitBundle bundle) { - this.conduitShapes.clear(); - this.directionShapes.clear(); - for (Holder> conduit : bundle.getConduits()) { - updateShapeForConduit(bundle, conduit); - } - updateTotalShape(); - } - - public VoxelShape getShapeFromHit(BlockPos pos, HitResult result) { - return Optional.ofNullable(this.conduitShapes.get(getConduit(pos, result))).orElse(Shapes.empty()); - } - - @Nullable - public Holder> getConduit(BlockPos pos, HitResult result) { - return getLookUpValue(conduitShapes, pos, result); - } - - @Nullable - public Direction getDirection(BlockPos pos, HitResult result) { - return getLookUpValue(directionShapes, pos, result); - } - - @Nullable - private T getLookUpValue(Map shapes, BlockPos pos, HitResult result) { - for (Map.Entry entry : shapes.entrySet()) { - Vec3 vec3 = result.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ()); - Optional point = entry.getValue().closestPointTo(vec3); - if (point.isEmpty()) { - continue; - } - - if (point.get().closerThan(vec3, Mth.EPSILON)) { // can't be 0 due to double - return entry.getKey(); - } - } - - return null; - } - - private void updateTotalShape() { - this.totalShape = Shapes.empty(); - this.conduitShapes.values().forEach(s -> this.totalShape = Shapes.joinUnoptimized(this.totalShape, s, BooleanOp.OR)); - totalShape.optimize(); - } - - public VoxelShape getTotalShape() { - return this.totalShape; - } - - private void updateShapeForConduit(ConduitBundle conduitBundle, Holder> conduit) { - VoxelShape conduitShape = Shapes.empty(); - Direction.Axis axis = OffsetHelper.findMainAxis(conduitBundle); - Map>, List> offsets = new HashMap<>(); - for (Direction direction : Direction.values()) { - VoxelShape directionShape = directionShapes.getOrDefault(direction, Shapes.empty()); - if (conduitBundle.getConnectionState(direction, conduit) instanceof DynamicConnectionState) { - VoxelShape connectorShape = rotateVoxelShape(CONNECTOR, direction); - directionShape = Shapes.joinUnoptimized(directionShape, connectorShape, BooleanOp.OR); - conduitShape = Shapes.joinUnoptimized(conduitShape, connectorShape, BooleanOp.OR); - } - var connectedTypes = conduitBundle.getConnectedConduits(direction); - if (connectedTypes.contains(conduit)) { - Vec3i offset = OffsetHelper.translationFor(direction.getAxis(), - OffsetHelper.offsetConduit(connectedTypes.indexOf(conduit), connectedTypes.size())); - offsets.computeIfAbsent(conduit, ignored -> new ArrayList<>()).add(offset); - VoxelShape connectionShape = rotateVoxelShape(CONNECTION, direction).move(offset.getX() * 3f / 16f, offset.getY() * 3f / 16f, - offset.getZ() * 3f / 16f); - directionShape = Shapes.joinUnoptimized(directionShape, connectionShape, BooleanOp.OR); - conduitShape = Shapes.joinUnoptimized(conduitShape, connectionShape, BooleanOp.OR); - } - directionShapes.put(direction, directionShape.optimize()); - } - - var allConduits = conduitBundle.getConduits(); - @Nullable Area box = null; - @Nullable Holder> notRendered = null; - int i = allConduits.indexOf(conduit); - if (i == -1) { - conduitShapes.put(conduit, Shapes.block()); - return; - } - - var type = allConduits.get(i); - @Nullable List offsetsForConduit = offsets.get(type); - if (offsetsForConduit != null) { - //all are pointing to the same xyz reference meaning that we can draw the core - if (offsetsForConduit.stream().distinct().count() != 1) { - box = new Area(offsetsForConduit.toArray(new Vec3i[0])); - } - } else { - notRendered = type; - } - - if (offsetsForConduit != null && (box == null || !box.contains(offsetsForConduit.get(0)))) { - conduitShape = Shapes.joinUnoptimized(conduitShape, - CORE.move(offsetsForConduit.get(0).getX() * 3f / 16f, offsetsForConduit.get(0).getY() * 3f / 16f, offsetsForConduit.get(0).getZ() * 3f / 16f), - BooleanOp.OR); - } - - if (box != null) { - if (notRendered != null) { - Vec3i offset = OffsetHelper.translationFor(axis, OffsetHelper.offsetConduit(i, allConduits.size())); - if (!box.contains(offset)) { - conduitShape = Shapes.joinUnoptimized(conduitShape, CORE.move(offset.getX() * 3f / 16f, offset.getY() * 3f / 16f, offset.getZ() * 3f / 16f), - BooleanOp.OR); - } - } - - conduitShape = Shapes.joinUnoptimized(conduitShape, CORE.move(box.getMin().getX() * 3f / 16f, box.getMin().getY() * 3f / 16f, box.getMin().getZ() * 3f / 16f), - BooleanOp.OR); - } else { - if (notRendered != null) { - Vec3i offset = OffsetHelper.translationFor(axis, OffsetHelper.offsetConduit(i, allConduits.size())); - conduitShape = Shapes.joinUnoptimized(conduitShape, CORE.move(offset.getX() * 3f / 16f, offset.getY() * 3f / 16f, offset.getZ() * 3f / 16f), BooleanOp.OR); - } - } - - conduitShapes.put(conduit, conduitShape.optimize()); - } - - /** - * Rotates a VoxelShape around the center to the specified Direction, Origin is SOUTH - * - * @param toRotate - * @param direction - * @return the rotated VoxelShape - */ - public static VoxelShape rotateVoxelShape(VoxelShape toRotate, Direction direction) { - VoxelShape[] buffer = new VoxelShape[] { toRotate, Shapes.empty() }; - if (direction.get2DDataValue() == -1) { - if (direction == Direction.DOWN) { - buffer[0].forAllBoxes( - (minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = Shapes.or(buffer[1], Shapes.box(minX, 1 - maxZ, minY, maxX, 1 - minZ, maxY))); - } else { - buffer[0].forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = Shapes.or(buffer[1], Shapes.box(minX, minZ, minY, maxX, maxZ, maxY))); - } - - return buffer[1]; - } - - for (int i = 0; i < (direction.get2DDataValue()) % 4; i++) { - buffer[0].forAllBoxes( - (minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = Shapes.or(buffer[1], Shapes.box(1 - maxZ, minY, minX, 1 - minZ, maxY, maxX))); - buffer[0] = buffer[1]; - buffer[1] = Shapes.empty(); - } - - return buffer[0]; - } - -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSorter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSorter.java index ee70b50b3f..d5610b55f2 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSorter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitSorter.java @@ -1,9 +1,13 @@ package com.enderio.conduits.common.conduit; +import com.enderio.conduits.EnderIOConduits; +import com.enderio.conduits.api.Conduit; import com.enderio.conduits.api.ConduitType; import com.enderio.conduits.api.EnderIOConduitsRegistries; -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.EnderIOConduits; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.neoforged.bus.api.SubscribeEvent; @@ -11,11 +15,6 @@ import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent; import net.neoforged.neoforge.event.server.ServerStartedEvent; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; - /** * This class is used to sort conduit types for display. * This is needed, so upgrading conduits doesn't require shifting of types, but just recalculating the current connection @@ -23,51 +22,56 @@ @SuppressWarnings("unused") @EventBusSubscriber(modid = EnderIOConduits.MODULE_MOD_ID, bus = EventBusSubscriber.Bus.GAME) public class ConduitSorter { - private static final List>> SORTED_CONDUITS = new ArrayList<>(); + private static final List>> SORTED_CONDUITS = new ArrayList<>(); @SubscribeEvent public static void serverSortTypes(ServerStartedEvent event) { - var conduitRegistry = event.getServer().registryAccess().registryOrThrow(EnderIOConduitsRegistries.Keys.CONDUIT); + var conduitRegistry = event.getServer() + .registryAccess() + .registryOrThrow(EnderIOConduitsRegistries.Keys.CONDUIT); sortTypes(conduitRegistry); } @SubscribeEvent public static void clientSortTypes(ClientPlayerNetworkEvent.LoggingIn event) { - var conduitRegistry = event.getPlayer().registryAccess().registryOrThrow(EnderIOConduitsRegistries.Keys.CONDUIT); + var conduitRegistry = event.getPlayer() + .registryAccess() + .registryOrThrow(EnderIOConduitsRegistries.Keys.CONDUIT); sortTypes(conduitRegistry); } - private static void sortTypes(Registry> registry) { + private static void sortTypes(Registry> registry) { SORTED_CONDUITS.clear(); // Group like types together. - List> conduitTypes = EnderIOConduitsRegistries.CONDUIT_TYPE - .stream() - .sorted(Comparator.comparing(i -> Objects.requireNonNull(EnderIOConduitsRegistries.CONDUIT_TYPE.getKey(i)).toString())) - .toList(); + List> conduitTypes = EnderIOConduitsRegistries.CONDUIT_TYPE.stream() + .sorted(Comparator.comparing( + i -> Objects.requireNonNull(EnderIOConduitsRegistries.CONDUIT_TYPE.getKey(i)).toString())) + .toList(); - List>> sortedConduits = new ArrayList<>(); + List>> sortedConduits = new ArrayList<>(); for (ConduitType conduitType : conduitTypes) { - sortedConduits.addAll(gatherConduitsForType(registry, conduitType)); + sortedConduits.addAll(gatherConduitsForType(registry, conduitType)); } SORTED_CONDUITS.addAll(sortedConduits); } - private static > List>> gatherConduitsForType(Registry> registry, ConduitType conduitType) { + private static > List>> gatherConduitsForType( + Registry> registry, ConduitType conduitType) { return registry.holders() - .filter(i -> i.value().type() == conduitType) - // Group by tier, then by name - .sorted(new Comparator>>() { - @Override - public int compare(Holder> o1, Holder> o2) { - return ((T)o1.value()).compareTo((T)o2.value()); - } - }.thenComparing(Holder::getRegisteredName)) - .map(i -> (Holder>)i) - .toList(); + .filter(i -> i.value().type() == conduitType) + // Group by tier, then by name + .sorted(new Comparator>>() { + @Override + public int compare(Holder> o1, Holder> o2) { + return ((T) o1.value()).compareTo((T) o2.value()); + } + }.thenComparing(Holder::getRegisteredName)) + .map(i -> (Holder>) i) + .toList(); } - public static int getSortIndex(Holder> conduit) { + public static int getSortIndex(Holder> conduit) { return SORTED_CONDUITS.indexOf(conduit); } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/OffsetHelper.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/OffsetHelper.java index 08349f53de..e1d83900d2 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/OffsetHelper.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/OffsetHelper.java @@ -1,6 +1,7 @@ package com.enderio.conduits.common.conduit; import com.enderio.base.common.util.ThrowableUtil; +import com.enderio.conduits.api.bundle.ConduitBundleReader; import com.mojang.logging.LogUtils; import java.util.ArrayList; import java.util.HashMap; @@ -112,7 +113,7 @@ public static Vec3i translationFor(Direction.Axis axis, Vector2i offset) { }; } - public static Direction.Axis findMainAxis(ConduitBundle bundle) { + public static Direction.Axis findMainAxis(ConduitBundleReader bundle) { List connectedDirs = new ArrayList<>(); for (Direction dir : Direction.values()) { if (!bundle.getConnectedConduits(dir).isEmpty()) { diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/RightClickAction.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/RightClickAction.java deleted file mode 100644 index 1b41b593fb..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/RightClickAction.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.enderio.conduits.common.conduit; - -import com.enderio.conduits.api.Conduit; -import net.minecraft.core.Holder; - -public sealed interface RightClickAction permits RightClickAction.Upgrade, RightClickAction.Blocked, RightClickAction.Insert{ - record Upgrade(Holder> replacedConduit) implements RightClickAction { - @Override - public String toString() { - return "Upgrade[" + replacedConduit.getRegisteredName() + "]"; - } - } - - final class Insert implements RightClickAction { - @Override - public String toString() { - return "Insert"; - } - } - - final class Blocked implements RightClickAction { - @Override - public String toString() { - return "Blocked"; - } - } - - default boolean hasChanged() { - return !(this instanceof Blocked); - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/SlotData.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/SlotData.java index 80980e3321..280ba383d2 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/SlotData.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/SlotData.java @@ -1,6 +1,6 @@ package com.enderio.conduits.common.conduit; -import com.enderio.conduits.api.SlotType; +import com.enderio.conduits.api.bundle.SlotType; import net.minecraft.core.Direction; public record SlotData(Direction direction, int conduitIndex, SlotType slotType) { @@ -10,6 +10,6 @@ public static SlotData of(int slot) { } public int slotIndex() { - return direction.ordinal()*3*9 + conduitIndex * 3 + slotType.ordinal(); + return direction.ordinal() * 3 * 9 + conduitIndex * 3 + slotType.ordinal(); } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/block/ConduitBundleBlock.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/block/ConduitBundleBlock.java deleted file mode 100644 index 0fce50c121..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/block/ConduitBundleBlock.java +++ /dev/null @@ -1,722 +0,0 @@ -package com.enderio.conduits.common.conduit.block; - -import com.enderio.base.common.init.EIOCapabilities; -import com.enderio.base.common.tag.EIOTags; -import com.enderio.conduits.EnderIOConduits; -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitCapabilities; -import com.enderio.conduits.client.model.conduit.facades.FacadeHelper; -import com.enderio.conduits.common.conduit.ConduitBlockItem; -import com.enderio.conduits.common.conduit.ConduitBundle; -import com.enderio.conduits.common.conduit.ConduitGraphObject; -import com.enderio.conduits.common.conduit.ConduitSavedData; -import com.enderio.conduits.common.conduit.RightClickAction; -import com.enderio.conduits.common.conduit.connection.ConnectionState; -import com.enderio.conduits.common.conduit.connection.DynamicConnectionState; -import com.enderio.conduits.common.conduit.connection.StaticConnectionStates; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; -import com.enderio.conduits.common.init.ConduitBlockEntities; -import com.enderio.conduits.common.init.ConduitComponents; -import com.enderio.conduits.common.init.ConduitTypes; -import com.enderio.conduits.common.init.Conduits; -import com.enderio.conduits.common.redstone.RedstoneInsertFilter; -import java.util.Objects; -import java.util.Optional; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.ItemInteractionResult; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.ClipContext; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.block.SimpleWaterloggedBlock; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityTicker; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; -import net.minecraft.world.level.block.state.properties.BooleanProperty; -import net.minecraft.world.level.gameevent.GameEvent; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.level.material.Fluids; -import net.minecraft.world.level.material.PushReaction; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.HitResult; -import net.minecraft.world.phys.shapes.CollisionContext; -import net.minecraft.world.phys.shapes.Shapes; -import net.minecraft.world.phys.shapes.VoxelShape; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; -import org.jetbrains.annotations.Nullable; - -@EventBusSubscriber(modid = EnderIOConduits.MODULE_MOD_ID) -public class ConduitBundleBlock extends Block implements EntityBlock, SimpleWaterloggedBlock { - - public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; - - public ConduitBundleBlock(Properties properties) { - super(properties); - registerDefaultState(getStateDefinition().any().setValue(WATERLOGGED, false)); - } - - @Nullable - @Override - public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { - return ConduitBlockEntities.CONDUIT.create(pos, state); - } - - @Override - public PushReaction getPistonPushReaction(BlockState pState) { - return PushReaction.BLOCK; - } - - @Override - public boolean canBeReplaced(BlockState pState, Fluid pFluid) { - return false; - } - - // region Water-logging - - @Override - public BlockState getStateForPlacement(BlockPlaceContext context) { - return defaultBlockState().setValue(WATERLOGGED, - context.getLevel().getFluidState(context.getClickedPos()).getType() == Fluids.WATER); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(WATERLOGGED); - } - - @Override - public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, - BlockPos currentPos, BlockPos neighborPos) { - if (state.getValue(WATERLOGGED)) { - level.scheduleTick(currentPos, Fluids.WATER, Fluids.WATER.getTickDelay(level)); - } - - if (level.getBlockEntity(currentPos) instanceof ConduitBundleBlockEntity conduit) { - conduit.updateShape(); - } - - return super.updateShape(state, direction, neighborState, level, currentPos, neighborPos); - } - - @Override - public FluidState getFluidState(BlockState state) { - return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state); - } - - // endregion - - @Override - public void neighborChanged(BlockState state, Level level, BlockPos pos, Block block, BlockPos fromPos, - boolean isMoving) { - if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduit) { - conduit.updateConnections(level, pos, fromPos, true); - } - - super.neighborChanged(state, level, pos, block, fromPos, isMoving); - } - - @Override - public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { - return getBundleShape(level, pos, true); - } - - @Override - protected VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, - CollisionContext context) { - return getBundleShape(level, pos, false); - } - - private VoxelShape getBundleShape(BlockGetter level, BlockPos pos, boolean canHideFacade) { - if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduit) { - if (conduit.getBundle().hasFacade() && (!canHideFacade || FacadeHelper.areFacadesVisible())) { - return Shapes.block(); - } - - // Ensure if a bundle is bugged with 0 conduits that it can be broken. - if (!conduit.getBundle().getConduits().isEmpty()) { - return conduit.getShape().getTotalShape(); - } - } - - return Shapes.block(); - } - - @Override - protected VoxelShape getVisualShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { - return super.getVisualShape(state, level, pos, context); - } - - // region Block Interaction - - @Override - protected ItemInteractionResult useItemOn(ItemStack itemStack, BlockState state, Level level, BlockPos pos, - Player player, InteractionHand interactionHand, BlockHitResult hit) { - - if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity blockEntity) { - var interactionResult = addConduit(blockEntity, player, itemStack, level.isClientSide()); - if (interactionResult.isPresent()) { - return interactionResult.get(); - } - - interactionResult = handleYeta(blockEntity, player, itemStack, hit, level.isClientSide()); - if (interactionResult.isPresent()) { - return interactionResult.get(); - } - - interactionResult = handleFacade(blockEntity, player, itemStack, hit, level.isClientSide()); - if (interactionResult.isPresent()) { - return interactionResult.get(); - } - } - - return super.useItemOn(itemStack, state, level, pos, player, interactionHand, hit); - } - - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, - BlockHitResult hit) { - BlockEntity be = level.getBlockEntity(pos); - if (be instanceof ConduitBundleBlockEntity conduit) { - Optional interactionResult = handleScreen(conduit, player, hit, level.isClientSide()); - - if (interactionResult.isPresent()) { - return interactionResult.get(); - } - } - - return super.useWithoutItem(state, level, pos, player, hit); - } - - private Optional addConduit(ConduitBundleBlockEntity blockEntity, Player player, - ItemStack stack, boolean isClientSide) { - if (!(stack.getItem() instanceof ConduitBlockItem)) { - return Optional.empty(); - } - - Holder> conduit = stack.get(ConduitComponents.CONDUIT); - if (conduit == null) { - return Optional.empty(); - } - - RightClickAction action = blockEntity.addType(conduit, player); - - ItemInteractionResult result; - - if (action instanceof RightClickAction.Upgrade upgradeAction) { - if (!player.getAbilities().instabuild) { - stack.shrink(1); - player.getInventory() - .placeItemBackInInventory(ConduitBlockItem.getStackFor(upgradeAction.replacedConduit(), 1)); - } - result = ItemInteractionResult.sidedSuccess(isClientSide); - } else if (action instanceof RightClickAction.Insert) { - if (!player.getAbilities().instabuild) { - stack.shrink(1); - } - - result = ItemInteractionResult.sidedSuccess(isClientSide); - } else { - result = ItemInteractionResult.FAIL; - } - - if (result != ItemInteractionResult.FAIL) { - Level level = blockEntity.getLevel(); - BlockPos blockpos = blockEntity.getBlockPos(); - - BlockState blockState = level.getBlockState(blockpos); - SoundType soundtype = blockState.getSoundType(level, blockpos, player); - level.playSound(player, blockpos, soundtype.getPlaceSound(), SoundSource.BLOCKS, - (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F); - level.gameEvent(GameEvent.BLOCK_PLACE, blockpos, GameEvent.Context.of(player, blockState)); - } - - return Optional.of(result); - } - - private Optional handleYeta(ConduitBundleBlockEntity blockEntity, Player player, - ItemStack stack, BlockHitResult hit, boolean isClientSide) { - if (stack.is(EIOTags.Items.WRENCH)) { - Holder> conduit = blockEntity.getShape().getConduit(hit.getBlockPos(), hit); - Direction direction = blockEntity.getShape().getDirection(hit.getBlockPos(), hit); - if (conduit == null) { - return Optional.empty(); - } - - if (isClientSide) { - return Optional.of(ItemInteractionResult.sidedSuccess(isClientSide)); - } - - internalHandleYeta(conduit, direction, blockEntity, hit); - return Optional.of(ItemInteractionResult.sidedSuccess(isClientSide)); - } - - return Optional.empty(); - } - - private void internalHandleYeta(Holder> conduit, @Nullable Direction direction, - ConduitBundleBlockEntity blockEntity, BlockHitResult hit) { - ConduitBundle bundle = blockEntity.getBundle(); - - if (direction != null) { - ConnectionState connectionState = bundle.getConnectionState(direction, conduit); - - if (connectionState instanceof DynamicConnectionState dyn) { - bundle.getNodeFor(conduit).clearState(direction); - blockEntity.dropConnectionItems(dyn); - bundle.setConnectionState(direction, conduit, StaticConnectionStates.DISABLED); - blockEntity.updateShape(); - blockEntity.onConnectionsUpdated(conduit); - } else { - bundle.setConnectionState(direction, conduit, StaticConnectionStates.DISABLED); - blockEntity.updateShape(); - blockEntity.onConnectionsUpdated(conduit); - - if (blockEntity.getLevel() - .getBlockEntity(blockEntity.getBlockPos() - .relative(direction)) instanceof ConduitBundleBlockEntity other) { - Direction oppositeDirection = direction.getOpposite(); - - bundle.setConnectionState(oppositeDirection, conduit, StaticConnectionStates.DISABLED); - other.updateShape(); - other.onConnectionsUpdated(conduit); - ConduitGraphObject thisNode = bundle.getNodeFor(conduit); - ConduitGraphObject otherNode = other.getBundle().getNodeFor(conduit); - thisNode.getGraph().removeSingleEdge(thisNode, otherNode); - thisNode.getGraph().removeSingleEdge(otherNode, thisNode); - ConduitSavedData.addPotentialGraph(conduit, thisNode.getGraph(), - (ServerLevel) blockEntity.getLevel()); - ConduitSavedData.addPotentialGraph(conduit, otherNode.getGraph(), (ServerLevel) other.getLevel()); - } - } - } else { - ConnectionState connectionState = bundle.getConnectionState(hit.getDirection(), conduit); - - if (!connectionState.isConnection()) { - blockEntity.tryConnectTo(hit.getDirection(), conduit, true, true, true); - } - } - } - - @SubscribeEvent - public static void handleShiftYeta(PlayerInteractEvent.RightClickBlock event) { - if (event.getItemStack().is(EIOTags.Items.WRENCH) - && event.getLevel().getBlockEntity(event.getPos()) instanceof ConduitBundleBlockEntity blockEntity - && event.getEntity().isSteppingCarefully()) { - - Holder> conduit = blockEntity.getShape().getConduit(event.getPos(), event.getHitVec()); - if (conduit != null) { - blockEntity.removeTypeAndDelete(event.getEntity(), conduit); - event.setCanceled(true); - } - } - } - - private Optional handleFacade(ConduitBundleBlockEntity blockEntity, Player player, - ItemStack stack, BlockHitResult hit, boolean isClientSide) { - var facade = stack.getCapability(ConduitCapabilities.CONDUIT_FACADE_PROVIDER); - if (facade == null || !facade.isValid()) { - return Optional.empty(); - } - - if (blockEntity.getBundle().hasFacade()) { - return Optional.of(ItemInteractionResult.FAIL); - } - - Level level = blockEntity.getLevel(); - BlockPos blockpos = blockEntity.getBlockPos(); - - int lightLevelBefore = level.getLightEmission(blockpos); - - blockEntity.getBundle().facade(stack); - if (!player.getAbilities().instabuild) { - stack.shrink(1); - } - - BlockState blockState = level.getBlockState(blockpos); - - SoundType soundtype = blockState.getSoundType(level, blockpos, player); - level.playSound(player, blockpos, soundtype.getPlaceSound(), SoundSource.BLOCKS, - (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F); - level.gameEvent(GameEvent.BLOCK_PLACE, blockpos, GameEvent.Context.of(player, blockState)); - - // Handle light update - if (lightLevelBefore != level.getLightEmission(blockpos)) { - level.getLightEngine().checkBlock(blockpos); - } - - return Optional.of(ItemInteractionResult.sidedSuccess(isClientSide)); - } - - private Optional handleScreen(ConduitBundleBlockEntity blockEntity, Player player, - BlockHitResult hit, boolean isClientSide) { - Optional openInformation = getOpenInformation(blockEntity, hit); - if (openInformation.isPresent()) { - if (player instanceof ServerPlayer serverPlayer) { - serverPlayer.openMenu( - blockEntity.menuProvider(openInformation.get().direction(), openInformation.get().conduit()), - buf -> { - buf.writeBlockPos(blockEntity.getBlockPos()); - buf.writeEnum(openInformation.get().direction()); - Conduit.STREAM_CODEC.encode(buf, openInformation.get().conduit()); - }); - } - - return Optional.of(InteractionResult.sidedSuccess(isClientSide)); - } - - return Optional.empty(); - } - - private Optional getOpenInformation(ConduitBundleBlockEntity blockEntity, BlockHitResult hit) { - Holder> conduit = blockEntity.getShape().getConduit(hit.getBlockPos(), hit); - Direction direction = blockEntity.getShape().getDirection(hit.getBlockPos(), hit); - - if (direction != null && conduit != null) { - if (canBeOrIsValidConnection(blockEntity, conduit, direction)) { - return Optional.of(new OpenInformation(direction, conduit)); - } - } - - if (conduit != null) { - direction = hit.getDirection(); - if (canBeValidConnection(blockEntity, conduit, direction)) { - return Optional.of(new OpenInformation(direction, conduit)); - } - } - - if (conduit != null) { - for (Direction potential : Direction.values()) { - if (canBeValidConnection(blockEntity, conduit, potential)) { - return Optional.of(new OpenInformation(potential, conduit)); - } - } - } - - ConduitBundle bundle = blockEntity.getBundle(); - - // fallback - for (Direction potential : Direction.values()) { - if (bundle.isConnectionEnd(potential)) { - for (Holder> potentialType : bundle.getConduits()) { - if (bundle.getConnectionState(potential, potentialType) instanceof DynamicConnectionState) { - return Optional.of(new OpenInformation(potential, potentialType)); - } - } - throw new IllegalStateException("couldn't find connection even though it should be present"); - } - } - - for (Direction potential : Direction.values()) { - if (!(blockEntity.getLevel() - .getBlockEntity( - blockEntity.getBlockPos().relative(potential)) instanceof ConduitBundleBlockEntity)) { - for (Holder> potentialType : bundle.getConduits()) { - if (canBeValidConnection(blockEntity, potentialType, potential)) { - return Optional.of(new OpenInformation(potential, potentialType)); - } - } - } - } - - return Optional.empty(); - } - - // endregion - - public static boolean canBeOrIsValidConnection(ConduitBundleBlockEntity blockEntity, Holder> conduit, - Direction direction) { - ConduitBundle bundle = blockEntity.getBundle(); - - return bundle.getConnectionState(direction, conduit) instanceof DynamicConnectionState - || canBeValidConnection(blockEntity, conduit, direction); - } - - public static boolean canBeValidConnection(ConduitBundleBlockEntity blockEntity, Holder> conduit, - Direction direction) { - ConduitBundle bundle = blockEntity.getBundle(); - ConnectionState connectionState = bundle.getConnectionState(direction, conduit); - return connectionState instanceof StaticConnectionStates state && state == StaticConnectionStates.DISABLED - && !(blockEntity.getLevel() - .getBlockEntity( - blockEntity.getBlockPos().relative(direction)) instanceof ConduitBundleBlockEntity); - } - - @Override - public ItemStack getCloneItemStack(BlockState state, HitResult target, LevelReader level, BlockPos pos, - Player player) { - if (level instanceof Level realLevel - && state.getOptionalValue(BlockStateProperties.WATERLOGGED).orElse(false)) { - var hitResult = Item.getPlayerPOVHitResult(realLevel, player, ClipContext.Fluid.NONE); - if (hitResult.getType() == HitResult.Type.MISS) { - return Items.AIR.getDefaultInstance(); - } - - if (hitResult.getBlockPos().equals(pos)) { - target = hitResult; - } else { - return level.getBlockState(hitResult.getBlockPos()) - .getCloneItemStack(hitResult, level, hitResult.getBlockPos(), player); - } - } - - if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity blockEntity) { - Optional facade = blockEntity.getBundle().facade(); - if (facade.isPresent() && FacadeHelper.areFacadesVisible()) { - return facade.get().asItem().getDefaultInstance(); - } - - Holder> conduit = blockEntity.getShape().getConduit(pos, target); - if (conduit == null) { - conduit = blockEntity.getBundle().getConduits().getFirst(); - } - - return ConduitBlockItem.getStackFor(conduit, 1); - } - - return super.getCloneItemStack(state, target, level, pos, player); - } - - @Nullable - @Override - public BlockEntityTicker getTicker(Level level, BlockState state, - BlockEntityType blockEntityType) { - return (level1, pos, state1, blockEntity) -> { - if (blockEntity instanceof ConduitBundleBlockEntity conduitBundleBlockEntity) { - conduitBundleBlockEntity.everyTick(); - } - }; - } - - // region Place and destroy logic - - @Override - public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, - ItemStack stack) { - Holder> conduit = stack.get(ConduitComponents.CONDUIT); - if (conduit == null) { - return; - } - - if (!(placer instanceof Player player)) { - return; - } - - if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity blockEntity) { - blockEntity.addType(conduit, player); - if (level.isClientSide()) { - blockEntity.updateClient(); - } - } - } - - @Override - public boolean onDestroyedByPlayer(BlockState state, Level level, BlockPos pos, Player player, boolean willHarvest, - FluidState fluid) { - HitResult hit = player.pick(player.blockInteractionRange() + 5, 1, false); - if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity blockEntity) { - - if (blockEntity.getBundle().hasFacade() && FacadeHelper.areFacadesVisible()) { - SoundType soundtype = state.getSoundType(level, pos, player); - level.playSound(player, pos, soundtype.getBreakSound(), SoundSource.BLOCKS, - (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F); - - if (!player.getAbilities().instabuild) { - blockEntity.dropFacadeItem(); - } - - int lightLevelBefore = level.getLightEmission(pos); - - blockEntity.getBundle().clearFacade(); - - // Handle light update - if (lightLevelBefore != level.getLightEmission(pos)) { - level.getLightEngine().checkBlock(pos); - } - } else { - Holder> conduit = blockEntity.getShape() - .getConduit(((BlockHitResult) hit).getBlockPos(), hit); - if (conduit == null) { - if (!blockEntity.getBundle().getConduits().isEmpty()) { - level.playSound(player, pos, SoundEvents.GENERIC_SMALL_FALL, SoundSource.BLOCKS, 1F, 1F); - return false; - } - return true; - } - - SoundType soundtype = state.getSoundType(level, pos, player); - level.playSound(player, pos, soundtype.getBreakSound(), SoundSource.BLOCKS, - (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F); - - if (blockEntity.removeType(conduit, !player.getAbilities().instabuild)) { - return super.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluid); - } - } - - level.gameEvent(GameEvent.BLOCK_DESTROY, pos, GameEvent.Context.of(player, state)); - return false; - } - - // No block entity, get rid of it immediately - return true; - } - - // endregion - - // region Redstone - - //@formatter:off - @Override - public boolean canConnectRedstone(BlockState state, BlockGetter level, BlockPos pos, @Nullable Direction direction) { - if (direction == null) { - return false; - } - - if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduitBundleBlockEntity && conduitBundleBlockEntity.getLevel() != null) { - Holder> redstoneConduit = conduitBundleBlockEntity.getLevel().holderOrThrow(Conduits.REDSTONE); - ConduitBundle conduitBundle = conduitBundleBlockEntity.getBundle(); - - return conduitBundle.getConduits().contains(redstoneConduit) && - conduitBundle.getConnectionState(direction.getOpposite(), redstoneConduit) instanceof DynamicConnectionState; - } - - return false; - } - - @SuppressWarnings("deprecation") - @Override - public int getSignal(BlockState pBlockState, BlockGetter level, BlockPos pos, Direction direction) { - if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduitBundleBlockEntity && conduitBundleBlockEntity.getLevel() != null) { - // TODO: Need to decouple this from the holder registry. Probably need to find conduits by their "type" instead. - Holder> redstoneConduit = conduitBundleBlockEntity.getLevel().holderOrThrow(Conduits.REDSTONE); - ConduitBundle conduitBundle = conduitBundleBlockEntity.getBundle(); - - if (!conduitBundle.getConduits().contains(redstoneConduit)) { - return 0; - } - - if (!(conduitBundle.getConnectionState(direction.getOpposite(), redstoneConduit) instanceof DynamicConnectionState dyn)) { - return 0; - } - - if (!dyn.isInsert()) { - return 0; - } - - if (!conduitBundle.getNodeFor(redstoneConduit).hasData(ConduitTypes.Data.REDSTONE.get())) { - return 0; - } - - RedstoneConduitData data = conduitBundle.getNodeFor(redstoneConduit).getData(ConduitTypes.Data.REDSTONE.get()); - return getSignalOutput(dyn, Objects.requireNonNull(data)); - } - - return 0; - } - - // TODO: Redstone conduit strong signals. - @Override - protected int getDirectSignal(BlockState pState, BlockGetter pLevel, BlockPos pPos, Direction pDirection) { - return super.getDirectSignal(pState, pLevel, pPos, pDirection); - } - - //@formatter:on - - private int getSignalOutput(DynamicConnectionState connectionState, RedstoneConduitData data) { - if (connectionState.filterInsert() - .getCapability(EIOCapabilities.Filter.ITEM) instanceof RedstoneInsertFilter filter) { - return filter.getOutputSignal(data, connectionState.insertChannel()); - } - return data.getSignal(connectionState.insertChannel()); - } - - // endregion - - private Optional getFacadeBlock(BlockGetter level, BlockPos pos) { - if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduit) { - return conduit.getBundle().facade(); - } - - return Optional.empty(); - } - - @Override - public BlockState getAppearance(BlockState state, BlockAndTintGetter level, BlockPos pos, Direction side, - @Nullable BlockState queryState, @Nullable BlockPos queryPos) { - - Optional facade = getFacadeBlock(level, pos); - if (facade.isPresent()) { - return facade.get().defaultBlockState(); - } - - return super.getAppearance(state, level, pos, side, queryState, queryPos); - } - - @Override - public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) { - Optional facade = getFacadeBlock(level, pos); - if (facade.isPresent()) { - return facade.get().getLightEmission(facade.get().defaultBlockState(), level, pos); - } - - return super.getLightEmission(state, level, pos); - } - - @Override - public float getFriction(BlockState state, LevelReader level, BlockPos pos, @Nullable Entity entity) { - Optional facade = getFacadeBlock(level, pos); - if (facade.isPresent()) { - return facade.get().getFriction(facade.get().defaultBlockState(), level, pos, entity); - } - - return super.getFriction(state, level, pos, entity); - } - - @Override - public SoundType getSoundType(BlockState state, LevelReader level, BlockPos pos, @Nullable Entity entity) { - Optional facade = getFacadeBlock(level, pos); - if (facade.isPresent()) { - return facade.get().getSoundType(facade.get().defaultBlockState(), level, pos, entity); - } - - return super.getSoundType(state, level, pos, entity); - } - - @Override - public boolean supportsExternalFaceHiding(BlockState state) { - return true; - } - - private record OpenInformation(Direction direction, Holder> conduit) { - } - - @Override - protected void spawnDestroyParticles(Level level, Player player, BlockPos pos, BlockState state) { - - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/block/ConduitBundleBlockEntity.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/block/ConduitBundleBlockEntity.java deleted file mode 100644 index 9966529db4..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/block/ConduitBundleBlockEntity.java +++ /dev/null @@ -1,897 +0,0 @@ -package com.enderio.conduits.common.conduit.block; - -import com.enderio.base.api.UseOnly; -import com.enderio.base.api.filter.ResourceFilter; -import com.enderio.base.common.init.EIOCapabilities; -import com.enderio.conduits.ConduitNBTKeys; -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitCapabilities; -import com.enderio.conduits.api.ConduitMenuData; -import com.enderio.conduits.api.ConduitNode; -import com.enderio.conduits.api.SlotType; -import com.enderio.conduits.api.upgrade.ConduitUpgrade; -import com.enderio.conduits.client.particle.ConduitBreakParticle; -import com.enderio.conduits.common.conduit.ConduitBlockItem; -import com.enderio.conduits.common.conduit.ConduitBundle; -import com.enderio.conduits.common.conduit.ConduitDataContainer; -import com.enderio.conduits.common.conduit.ConduitGraphContext; -import com.enderio.conduits.common.conduit.ConduitGraphObject; -import com.enderio.conduits.common.conduit.ConduitGraphUtility; -import com.enderio.conduits.common.conduit.ConduitSavedData; -import com.enderio.conduits.common.conduit.ConduitShape; -import com.enderio.conduits.common.conduit.RightClickAction; -import com.enderio.conduits.common.conduit.SlotData; -import com.enderio.conduits.common.conduit.connection.ConnectionState; -import com.enderio.conduits.common.conduit.connection.DynamicConnectionState; -import com.enderio.conduits.common.conduit.connection.StaticConnectionStates; -import com.enderio.conduits.common.init.ConduitBlockEntities; -import com.enderio.conduits.common.menu.ConduitMenu; -import com.enderio.core.common.blockentity.EnderBlockEntity; -import dev.gigaherz.graph3.Graph; -import dev.gigaherz.graph3.GraphObject; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import me.liliandev.ensure.ensures.EnsureSide; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.MenuProvider; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.fml.LogicalSide; -import net.neoforged.neoforge.capabilities.BlockCapability; -import net.neoforged.neoforge.capabilities.ICapabilityProvider; -import net.neoforged.neoforge.client.ChunkRenderTypeSet; -import net.neoforged.neoforge.client.model.data.ModelData; -import net.neoforged.neoforge.client.model.data.ModelProperty; -import net.neoforged.neoforge.common.util.INBTSerializable; -import net.neoforged.neoforge.items.IItemHandler; -import net.neoforged.neoforge.items.IItemHandlerModifiable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class ConduitBundleBlockEntity extends EnderBlockEntity { - - public static final ModelProperty BUNDLE_MODEL_PROPERTY = new ModelProperty<>(); - public static final ModelProperty FACADE_MODEL_DATA = new ModelProperty<>(); - public static final ModelProperty FACADE_RENDERTYPE = new ModelProperty<>(); - public static final String CONDUIT_INV_KEY = "ConduitInv"; - - @UseOnly(LogicalSide.CLIENT) - public static final Map FACADES = new HashMap<>(); - - private final ConduitShape shape = new ConduitShape(); - - private ConduitBundle bundle; - @UseOnly(LogicalSide.CLIENT) - private ConduitBundle clientBundle; - - private UpdateState checkConnection = UpdateState.NONE; - - private final Map>, ConduitGraphObject> lazyNodes = new HashMap<>(); - private ListTag lazyNodeNBT = new ListTag(); - private ConduitItemHandler conduitItemHandler = new ConduitItemHandler(); - - public ConduitBundleBlockEntity(BlockPos worldPosition, BlockState blockState) { - super(ConduitBlockEntities.CONDUIT.get(), worldPosition, blockState); - bundle = new ConduitBundle(this::scheduleTick, worldPosition); - - addDataSlot(ConduitBundle.DATA_SLOT_TYPE.create(this::getBundle, b -> bundle = b)); - addAfterSyncRunnable(this::updateClient); - } - - public ConduitBundle getBundle() { - return bundle; - } - - public ConduitShape getShape() { - return shape; - } - - public void updateShape() { - shape.updateConduit(bundle); - } - - public void updateClient() { - if (level != null && level.isClientSide) { - clientBundle = bundle.deepCopy(); - updateShape(); - requestModelDataUpdate(); - level.setBlocksDirty(getBlockPos(), Blocks.AIR.defaultBlockState(), getBlockState()); - if (bundle.hasFacade()) { - FACADES.put(worldPosition, bundle.facade().get().defaultBlockState()); - } else { - FACADES.remove(worldPosition); - } - } - } - - // region Network Sync - - /** - * Handle a connection state update from the client. - */ - @EnsureSide(EnsureSide.Side.SERVER) - public void handleConnectionStateUpdate(Direction direction, Holder> conduit, - DynamicConnectionState connectionState) { - // Sanity check, the client shouldn't do this, but just to make sure there's no - // confusion. - if (bundle.getConnectionState(direction, conduit) instanceof DynamicConnectionState) { - bundle.setConnectionState(direction, conduit, connectionState); - - // Update node IO state. - var node = bundle.getNodeFor(conduit); - node.pushState(direction, connectionState); - - // Proxied capabilities are likely to have changed. - level.invalidateCapabilities(worldPosition); - } - - updateClient(); - onConnectionsUpdated(conduit); - } - - @EnsureSide(EnsureSide.Side.SERVER) - public void handleConduitDataUpdate(Holder> conduit, ConduitDataContainer clientDataContainer) { - var node = getBundle().getNodeFor(conduit); - node.handleClientChanges(clientDataContainer); - } - - // endregion - - private void scheduleTick() { - setChanged(); - } - - @Override - public void onLoad() { - updateShape(); - - if (level instanceof ServerLevel serverLevel) { - sync(); - bundle.onLoad(level, getBlockPos()); - for (var entry : lazyNodes.entrySet()) { - Holder> conduit = entry.getKey(); - ConduitGraphObject node = entry.getValue(); - loadNode(serverLevel, conduit, node); - } - } - - // Now that the BE is loaded, update the blocklight. - if (bundle.hasFacade()) { - level.getLightEngine().checkBlock(worldPosition); - } - } - - private void loadNode(ServerLevel serverLevel, Holder> conduit, ConduitGraphObject node) { - - Graph graph = Objects.requireNonNull(node.getGraph()); - - for (Direction dir : Direction.values()) { - tryConnectTo(dir, conduit, false, false, false) - .ifPresent(otherNode -> ConduitGraphUtility.connect(conduit, node, otherNode)); - } - - for (GraphObject object : node.getGraph().getObjects()) { - if (object instanceof ConduitGraphObject otherNode) { - conduit.value().onConnectTo(node, otherNode); - } - } - - ConduitSavedData.addPotentialGraph(conduit, graph, serverLevel); - } - - public boolean stillValid(Player pPlayer) { - if (level == null || level.getBlockEntity(this.worldPosition) != this) { - return false; - } - - return pPlayer.canInteractWithBlock(this.worldPosition, 1.5); - } - - @Override - public void onChunkUnloaded() { - super.onChunkUnloaded(); - if (level instanceof ServerLevel serverLevel) { - ConduitSavedData savedData = ConduitSavedData.get(serverLevel); - bundle.getConduits().forEach(type -> onChunkUnloaded(savedData, type)); - } else { - FACADES.remove(worldPosition); - } - } - - private void onChunkUnloaded(ConduitSavedData savedData, Holder> conduit) { - var node = bundle.getNodeFor(conduit); - conduit.value().onRemoved(node, level, getBlockPos()); - savedData.putUnloadedNodeIdentifier(conduit, this.worldPosition, node); - } - - @Override - public void setRemoved() { - super.setRemoved(); - if (level != null && level.isClientSide) { - FACADES.remove(worldPosition); - } - } - - public void everyTick() { - if (level != null && !level.isClientSide) { - serverTick(); - checkConnection = checkConnection.next(); - if (checkConnection.isInitialized()) { - updateConnections(level, worldPosition, null, false); - } - } - } - - public void updateConnections(Level level, BlockPos pos, @Nullable BlockPos fromPos, boolean shouldActivate) { - for (Direction direction : Direction.values()) { - if (fromPos == null || !(level.getBlockEntity(fromPos) instanceof ConduitBundleBlockEntity)) { - for (Holder> conduit : bundle.getConduits()) { - if (shouldActivate && conduit.value().hasConnectionDelay()) { - checkConnection = checkConnection.activate(); - continue; - } - - ConnectionState connectionState = bundle.getConnectionState(direction, conduit); - if (connectionState instanceof DynamicConnectionState dyn) { - if (!conduit.value().canForceConnectTo(level, pos, direction)) { - bundle.getNodeFor(conduit).clearState(direction); - dropConnectionItems(dyn); - bundle.setConnectionState(direction, conduit, StaticConnectionStates.DISCONNECTED); - updateShape(); - onConnectionsUpdated(conduit); - } - } else if (connectionState == StaticConnectionStates.DISCONNECTED) { - tryConnectTo(direction, conduit, true, true, false); - } - } - } - } - - updateShape(); - } - - @Override - protected void saveAdditional(CompoundTag tag, HolderLookup.Provider lookupProvider) { - super.saveAdditional(tag, lookupProvider); - tag.put(ConduitNBTKeys.CONDUIT_BUNDLE, bundle.save(lookupProvider)); - - ListTag listTag = new ListTag(); - for (Holder> conduit : bundle.getConduits()) { - var data = bundle.getNodeFor(conduit).conduitDataContainer(); - listTag.add(data.save(lookupProvider)); - } - - tag.put(ConduitNBTKeys.CONDUIT_EXTRA_DATA, listTag); - tag.put(CONDUIT_INV_KEY, conduitItemHandler.serializeNBT(lookupProvider)); - } - - @Override - public void loadAdditional(CompoundTag tag, HolderLookup.Provider lookupProvider) { - super.loadAdditional(tag, lookupProvider); - - // loadAdditional is now called by the sync system - // ideally we'll need presence checks to find bundle issues, but for now we're - // making this safe. - - if (tag.contains(ConduitNBTKeys.CONDUIT_BUNDLE)) { - bundle = ConduitBundle.parse(lookupProvider, tag.getCompound(ConduitNBTKeys.CONDUIT_BUNDLE)); - bundle.setOnChangedRunnable(this::scheduleTick); - } - - if (tag.contains(ConduitNBTKeys.CONDUIT_EXTRA_DATA)) { - lazyNodeNBT = tag.getList(ConduitNBTKeys.CONDUIT_EXTRA_DATA, Tag.TAG_COMPOUND); - } - - if (tag.contains(CONDUIT_INV_KEY)) { - conduitItemHandler.deserializeNBT(lookupProvider, tag.getCompound(CONDUIT_INV_KEY)); - } - } - - @Override - public void setLevel(Level pLevel) { - super.setLevel(pLevel); - - if (level.isClientSide()) { - clientBundle = bundle.deepCopy(); - } else { - loadFromSavedData(); - } - } - - @Override - public ModelData getModelData() { - return ModelData.builder().with(BUNDLE_MODEL_PROPERTY, clientBundle).build(); - } - - public boolean hasType(Holder> conduit) { - return bundle.hasType(conduit); - } - - public RightClickAction addType(Holder> conduit, Player player) { - RightClickAction action = bundle.addConduit(level, conduit, player); - - // something has changed - if (action.hasChanged()) { - List> nodes = new ArrayList<>(); - for (Direction dir : Direction.values()) { - tryConnectTo(dir, conduit, false, false, false).ifPresent(nodes::add); - } - - if (level instanceof ServerLevel serverLevel) { - ConduitGraphObject thisNode = Objects.requireNonNull(bundle.getNodeForTypeExact(conduit), - "no node found in conduit"); - ConduitGraphUtility.integrate(conduit, thisNode, nodes); - - for (GraphObject object : thisNode.getGraph().getObjects()) { - if (object instanceof ConduitGraphObject node) { - conduit.value().onConnectTo(thisNode, node); - } - } - - ConduitSavedData.addPotentialGraph(conduit, Objects.requireNonNull(thisNode.getGraph()), serverLevel); - } - - if (action instanceof RightClickAction.Upgrade upgrade - && !upgrade.replacedConduit().value().canConnectTo(conduit)) { - removeNeighborConnections(upgrade.replacedConduit()); - } - - // Update neighbors - level.updateNeighborsAt(getBlockPos(), getBlockState().getBlock()); - - updateShape(); - } - - return action; - } - - public Optional> tryConnectTo(Direction dir, Holder> conduit, - boolean forceMerge, boolean shouldMergeGraph, boolean forceConnect) { - if (level.getBlockEntity(getBlockPos().relative(dir)) instanceof ConduitBundleBlockEntity neighborBlockEntity - && neighborBlockEntity.connectTo(dir.getOpposite(), conduit, bundle.getNodeFor(conduit), forceMerge)) { - - connect(dir, conduit); - onConnectionsUpdated(conduit); - neighborBlockEntity.onConnectionsUpdated(conduit); - - ConduitBundle adjacentBundle = neighborBlockEntity.getBundle(); - - ConduitGraphObject firstNode = adjacentBundle.getNodeFor(conduit); - ConduitGraphObject secondNode = bundle.getNodeFor(conduit); - - conduit.value().onConnectTo(firstNode, secondNode); - - if (firstNode.getParentGraph() != null) { - for (var node : firstNode.getParentGraph().getNodes()) { - if (node != firstNode) { - conduit.value().onConnectTo(firstNode, node); - } - } - } - - if (secondNode.getParentGraph() != null && firstNode.getParentGraph() != secondNode.getParentGraph()) { - for (var node : secondNode.getParentGraph().getNodes()) { - if (node != secondNode) { - conduit.value().onConnectTo(secondNode, node); - } - } - } - - if (shouldMergeGraph) { - ConduitGraphUtility.connect(conduit, bundle.getNodeFor(conduit), adjacentBundle.getNodeFor(conduit)); - } - - return Optional.of(adjacentBundle.getNodeFor(conduit)); - } else if (conduit.value().canConnectTo(level, getBlockPos(), dir) - || (forceConnect && conduit.value().canForceConnectTo(level, getBlockPos(), dir))) { - if (bundle.getConnectionState(dir, conduit) instanceof DynamicConnectionState dyn && dyn.isConnection()) { // Already - // connected - onConnectionsUpdated(conduit); - return Optional.empty(); - } - connectEnd(dir, conduit); - onConnectionsUpdated(conduit); - } else { - this.disconnect(dir, conduit); - } - - return Optional.empty(); - } - - public void onConnectionsUpdated(Holder> conduit) { - if (level != null && !level.isClientSide) { - var node = getBundle().getNodeFor(conduit); - - Set connectedSides = Arrays.stream(Direction.values()) - .filter(direction -> bundle.getConnectionState(direction, - conduit) != StaticConnectionStates.DISABLED) - .collect(Collectors.toSet()); - - conduit.value().onConnectionsUpdated(node, level, getBlockPos(), connectedSides); - } - } - - /** - * sets block to air if this is the last conduit - */ - public void removeTypeAndDelete(Player player, Holder> conduit) { - if (removeType(conduit, !player.getAbilities().instabuild)) { - level.setBlock(getBlockPos(), getBlockState().getFluidState().createLegacyBlock(), - level.isClientSide ? Block.UPDATE_ALL_IMMEDIATE : Block.UPDATE_ALL); - } - } - - /** - * Remove a conduit from the bundle. - * @param conduit The conduit to remove. - * @param shouldDrop Whether the conduit item should drop for this conduit. - * @return Whether the block should now be completely removed. - */ - public boolean removeType(Holder> conduit, boolean shouldDrop) { - if (shouldDrop && !level.isClientSide()) { - dropItem(ConduitBlockItem.getStackFor(conduit, 1)); - for (Direction dir : Direction.values()) { - if (bundle.getConnectionState(dir, conduit) instanceof DynamicConnectionState dyn) { - dropConnectionItems(dyn); - } - } - } - - boolean shouldRemove = bundle.removeConduit(level, conduit); - removeNeighborConnections(conduit); - updateShape(); - - if (level.isClientSide) { - ConduitBreakParticle.addDestroyEffects(getBlockPos(), conduit.value()); - } - - if (bundle.hasFacade() && shouldRemove) { - dropFacadeItem(); - } - - return shouldRemove; - } - - public void updateEmptyDynConnection() { - for (Direction dir : Direction.values()) { - for (int i = 0; i < ConduitBundle.MAX_CONDUITS; i++) { - if (bundle.getConnectionState(dir, i) instanceof DynamicConnectionState dynState - && dynState.isEmpty()) { - dropConnectionItems(dynState); - bundle.disableConduit(dir, i); - } - } - } - } - - public void dropConnectionItems(DynamicConnectionState dyn) { - for (SlotType slotType : SlotType.values()) { - ItemStack item = dyn.getItem(slotType); - if (!item.isEmpty()) { - dropItem(item); - } - } - } - - public void dropFacadeItem() { - if (!bundle.hasFacade()) { - throw new IllegalStateException("Cannot drop facade item because no facade has been set"); - } - - dropItem(bundle.facadeItem()); - } - - /** - * Drop an item on the ground by this block. - */ - private void dropItem(ItemStack stack) { - level.addFreshEntity( - new ItemEntity(level, getBlockPos().getX(), getBlockPos().getY(), getBlockPos().getZ(), stack)); - } - - /** - * Removes connections to neigbouring bundles to the given conduit. - * @param conduit The conduit in this conduit that should be disconnected from other conduits. - */ - public void removeNeighborConnections(Holder> conduit) { - for (Direction dir : Direction.values()) { - if (level.getBlockEntity( - getBlockPos().relative(dir)) instanceof ConduitBundleBlockEntity neighborBlockEntity) { - neighborBlockEntity.disconnect(dir.getOpposite(), conduit); - } - } - - if (level instanceof ServerLevel serverLevel) { - for (Direction dir : Direction.values()) { - BlockEntity blockEntity = level.getBlockEntity(getBlockPos().relative(dir)); - if (blockEntity instanceof ConduitBundleBlockEntity neighborBlockEntity - && neighborBlockEntity.hasType(conduit)) { - Optional.of(neighborBlockEntity.bundle.getNodeFor(conduit)) - .map(ConduitGraphObject::getGraph) - .filter(Objects::nonNull) - .ifPresent(graph -> ConduitSavedData.addPotentialGraph(conduit, graph, serverLevel)); - } - } - } - } - - // region Serialization - - @UseOnly(LogicalSide.SERVER) - private void loadFromSavedData() { - if (!(level instanceof ServerLevel serverLevel)) { - return; - } - - ConduitSavedData savedData = ConduitSavedData.get(serverLevel); - for (int i = 0; i < bundle.getConduits().size(); i++) { - Holder> type = bundle.getConduits().get(i); - loadConduitFromSavedData(savedData, type, i); - } - - lazyNodeNBT.clear(); - } - - @UseOnly(LogicalSide.SERVER) - private void loadConduitFromSavedData(ConduitSavedData savedData, Holder> conduit, int typeIndex) { - if (level == null) { - return; - } - - ConduitGraphObject node = savedData.takeUnloadedNodeIdentifier(conduit, this.worldPosition); - if (node == null && bundle.getNodeForTypeExact(conduit) == null) { - ConduitDataContainer dataContainer = null; - if (typeIndex < lazyNodeNBT.size()) { - dataContainer = ConduitDataContainer.parse(level.registryAccess(), lazyNodeNBT.getCompound(typeIndex)); - } - - node = new ConduitGraphObject(worldPosition, dataContainer); - ConduitGraphUtility.integrate(conduit, node, List.of()); - bundle.setNodeFor(conduit, node); - lazyNodes.put(conduit, node); - } else if (node != null) { - bundle.setNodeFor(conduit, node); - } - } - - // endregion - - /** - * @param direction the Direction to connect to - * @param conduit the conduit to be connected - * @param node the other node to check if those can connect - * @param forceMerge if disabledstate should be ignored - * @return true if a connection happens - */ - private boolean connectTo(Direction direction, Holder> conduit, ConduitNode node, boolean forceMerge) { - if (!doTypesMatch(conduit)) { - return false; - } - - if (!conduit.value().canConnectTo(bundle.getNodeFor(conduit), node)) { - return false; - } - - if (forceMerge || bundle.getConnectionState(direction, conduit) != StaticConnectionStates.DISABLED) { - connect(direction, conduit); - return true; - } - - return false; - } - - private boolean doTypesMatch(Holder> conduitToMatch) { - for (Holder> conduit : bundle.getConduits()) { - if (conduit.value().canConnectTo(conduitToMatch)) { - return true; - } - } - - return false; - } - - private void connect(Direction direction, Holder> conduit) { - bundle.connectTo(level, worldPosition, direction, conduit, false); - updateClient(); - } - - private void connectEnd(Direction direction, Holder> conduit) { - bundle.connectTo(level, worldPosition, direction, conduit, true); - updateClient(); - } - - private void disconnect(Direction direction, Holder> conduit) { - if (bundle.disconnectFrom(direction, conduit)) { - updateClient(); - } - } - - public MenuProvider menuProvider(Direction direction, Holder> conduit) { - return new ConduitMenuProvider(direction, conduit); - } - - private class ConduitMenuProvider implements MenuProvider { - - private final Direction direction; - private final Holder> conduit; - - private ConduitMenuProvider(Direction direction, Holder> conduit) { - this.direction = direction; - this.conduit = conduit; - } - - @Override - public Component getDisplayName() { - return getBlockState().getBlock().getName(); - } - - @Nullable - @Override - public AbstractContainerMenu createMenu(int pContainerId, Inventory pInventory, Player pPlayer) { - return new ConduitMenu(ConduitBundleBlockEntity.this, pInventory, pContainerId, direction, conduit); - } - } - - public static ICapabilityProvider createConduitCap( - BlockCapability cap) { - return (be, context) -> { - for (Holder> conduit : be.bundle.getConduits()) { - var proxiedCap = getProxiedCapability(cap, be, conduit, context); - if (proxiedCap != null) { - return proxiedCap; - } - } - - return null; - }; - } - - @Nullable - private static TCap getProxiedCapability(BlockCapability capability, - ConduitBundleBlockEntity blockEntity, Holder> conduit, @Nullable TContext context) { - - if (blockEntity.level == null) { - return null; - } - - ConduitGraphObject node = blockEntity.bundle.getNodeFor(conduit); - return conduit.value().proxyCapability(capability, node, blockEntity.level, blockEntity.getBlockPos(), context); - } - - public IItemHandler getConduitItemHandler() { - return conduitItemHandler; - } - - private class ConduitItemHandler implements IItemHandlerModifiable, INBTSerializable { - - @Override - public int getSlots() { - return 3 * ConduitBundle.MAX_CONDUITS * 6; - } - - @Override - public ItemStack getStackInSlot(int slot) { - if (slot >= getSlots()) { - return ItemStack.EMPTY; - } - - SlotData data = SlotData.of(slot); - if (data.conduitIndex() >= bundle.getConduits().size()) { - return ItemStack.EMPTY; - } - - ConnectionState connectionState = bundle.getConnectionState(data.direction(), data.conduitIndex()); - if (!(connectionState instanceof DynamicConnectionState dynamicConnectionState)) { - return ItemStack.EMPTY; - } - - Holder> conduit = bundle.getConduits().get(data.conduitIndex()); - ConduitMenuData conduitData = conduit.value().getMenuData(); - if ((data.slotType() == SlotType.FILTER_EXTRACT && conduitData.hasFilterExtract()) - || (data.slotType() == SlotType.FILTER_INSERT && conduitData.hasFilterInsert()) - || (data.slotType() == SlotType.UPGRADE_EXTRACT && conduitData.hasUpgrade())) { - return dynamicConnectionState.getItem(data.slotType()); - } - - return ItemStack.EMPTY; - } - - @Override - public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { - // see ItemStackHandler - if (stack.isEmpty()) { - return ItemStack.EMPTY; - } - - if (!isItemValid(slot, stack)) { - return stack; - } - - ItemStack existing = getStackInSlot(slot); - - int limit = Math.min(getSlotLimit(slot), stack.getMaxStackSize()); - - if (!existing.isEmpty()) { - if (!ItemStack.isSameItemSameComponents(stack, existing)) { - return stack; - } - - limit -= existing.getCount(); - } - - if (limit <= 0) { - return stack; - } - - boolean reachedLimit = stack.getCount() > limit; - - if (!simulate) { - if (existing.isEmpty()) { - setStackInSlot(slot, reachedLimit ? stack.copyWithCount(limit) : stack); - } else { - existing.grow(reachedLimit ? limit : stack.getCount()); - } - } - return reachedLimit ? stack.copyWithCount(stack.getCount() - limit) : ItemStack.EMPTY; - } - - @Override - public ItemStack extractItem(int slot, int amount, boolean simulate) { - if (amount == 0) { - return ItemStack.EMPTY; - } - - ItemStack existing = getStackInSlot(slot); - - if (existing.isEmpty()) { - return ItemStack.EMPTY; - } - - int toExtract = Math.min(amount, existing.getMaxStackSize()); - - if (existing.getCount() <= toExtract) { - if (!simulate) { - setStackInSlot(slot, ItemStack.EMPTY); - return existing; - } else { - return existing.copy(); - } - } else { - if (!simulate) { - setStackInSlot(slot, existing.copyWithCount(existing.getCount() - toExtract)); - } - return existing.copyWithCount(toExtract); - } - } - - @Override - public int getSlotLimit(int slot) { - return 1; - } - - @Override - public boolean isItemValid(int slot, @NotNull ItemStack stack) { - if (slot >= getSlots()) { - return false; - } - - SlotData slotData = SlotData.of(slot); - if (slotData.conduitIndex() >= bundle.getConduits().size()) { - return false; - } - - Holder> conduit = bundle.getConduits().get(slotData.conduitIndex()); - - switch (slotData.slotType()) { - case FILTER_EXTRACT: - case FILTER_INSERT: - ResourceFilter resourceFilter = stack.getCapability(EIOCapabilities.Filter.ITEM); - if (resourceFilter == null) { - return false; - } - - return conduit.value().canApplyFilter(slotData.slotType(), resourceFilter); - case UPGRADE_EXTRACT: - ConduitUpgrade conduitUpgrade = stack.getCapability(ConduitCapabilities.CONDUIT_UPGRADE); - if (conduitUpgrade == null) { - return false; - } - - return conduit.value().canApplyUpgrade(slotData.slotType(), conduitUpgrade); - default: - return false; - } - } - - @Override - public void setStackInSlot(int slot, @NotNull ItemStack stack) { - if (slot >= getSlots()) { - return; - } - - SlotData data = SlotData.of(slot); - if (data.conduitIndex() >= bundle.getConduits().size()) { - return; - } - - Holder> conduit = bundle.getConduits().get(data.conduitIndex()); - ConduitMenuData menuData = conduit.value().getMenuData(); - - if ((data.slotType() == SlotType.FILTER_EXTRACT && menuData.hasFilterExtract()) - || (data.slotType() == SlotType.FILTER_INSERT && menuData.hasFilterInsert()) - || (data.slotType() == SlotType.UPGRADE_EXTRACT && menuData.hasUpgrade())) { - bundle.setConnectionItem(data.direction(), data.conduitIndex(), data.slotType(), stack); - if (bundle.getConnectionState(data.direction(), - conduit) instanceof DynamicConnectionState dynamicConnectionState) { - ConduitGraphObject node = bundle.getNodeForTypeExact(conduit); - if (node != null) { - node.pushState(data.direction(), dynamicConnectionState); - } - } - } - } - - @Override - public CompoundTag serializeNBT(HolderLookup.Provider lookupProvider) { - CompoundTag tag = new CompoundTag(); - ListTag list = new ListTag(); - for (int i = 0; i < getSlots(); i++) { - ItemStack stack = getStackInSlot(i); - list.add(i, stack.saveOptional(lookupProvider)); - } - tag.put(CONDUIT_INV_KEY, list); - return tag; - } - - @Override - public void deserializeNBT(HolderLookup.Provider lookupProvider, CompoundTag nbt) { - ListTag list = nbt.getList(CONDUIT_INV_KEY, Tag.TAG_COMPOUND); - for (int i = 0; i < list.size(); i++) { - setStackInSlot(i, ItemStack.parseOptional(lookupProvider, list.getCompound(i))); - } - } - } - - public enum UpdateState { - NONE, NEXT_NEXT, NEXT, INITIALIZED; - - public boolean isInitialized() { - return this == INITIALIZED; - } - - public UpdateState next() { - return switch (this) { - case NONE, INITIALIZED -> NONE; - case NEXT_NEXT -> NEXT; - case NEXT -> INITIALIZED; - }; - } - - public UpdateState activate() { - return NEXT_NEXT; - } - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java new file mode 100644 index 0000000000..27527e50ef --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlock.java @@ -0,0 +1,635 @@ +package com.enderio.conduits.common.conduit.bundle; + +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.ConduitCapabilities; +import com.enderio.conduits.api.ConduitRedstoneSignalAware; +import com.enderio.conduits.api.bundle.AddConduitResult; +import com.enderio.conduits.api.connection.ConnectionStatus; +import com.enderio.conduits.client.model.conduit.facades.FacadeHelper; +import com.enderio.conduits.client.particle.ConduitBreakParticle; +import com.enderio.conduits.common.conduit.ConduitBlockItem; +import com.enderio.conduits.common.conduit.menu.ConduitMenu; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitConnectionConfig; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; +import com.enderio.conduits.common.init.ConduitBlockEntities; +import com.enderio.conduits.common.init.ConduitComponents; +import com.enderio.conduits.common.init.ConduitTypes; +import com.enderio.conduits.common.redstone.RedstoneInsertFilter; +import java.util.Optional; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.ItemInteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.level.material.PushReaction; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.neoforged.fml.loading.FMLLoader; +import org.jetbrains.annotations.Nullable; + +public class ConduitBundleBlock extends Block implements EntityBlock { + + private static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; + + public ConduitBundleBlock(Properties properties) { + super(properties); + registerDefaultState(getStateDefinition().any().setValue(WATERLOGGED, false)); + } + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return ConduitBlockEntities.CONDUIT.create(blockPos, blockState); + } + + @Override + public @Nullable BlockEntityTicker getTicker(Level level, BlockState state, + BlockEntityType blockEntityType) { + // return createTickerHelper(blockEntityType, typeSupplier.get(), + // EIOBlockEntity::tick); + return (level1, pos, state1, blockEntity) -> { + if (blockEntity instanceof ConduitBundleBlockEntity conduitBundleBlockEntity) { + if (level.isClientSide) { + conduitBundleBlockEntity.clientTick(); + } else { + conduitBundleBlockEntity.serverTick(); + } + conduitBundleBlockEntity.endTick(); + } + }; + } + + // region Block Shape + + @Override + protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + return getBundleShape(level, pos, true); + } + + @Override + protected VoxelShape getInteractionShape(BlockState state, BlockGetter level, BlockPos pos) { + return super.getInteractionShape(state, level, pos); + } + + @Override + protected VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, + CollisionContext context) { + return getBundleShape(level, pos, false); + } + + private VoxelShape getBundleShape(BlockGetter level, BlockPos pos, boolean canHideFacade) { + if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduit) { + if (conduit.hasFacade() && (!canHideFacade || FacadeHelper.areFacadesVisible())) { + return Shapes.block(); + } + + // Ensure if a bundle is bugged with 0 conduits that it can be broken. + if (!conduit.getConduits().isEmpty()) { + return conduit.getShape().getTotalShape(); + } + } + + // If there's no block entity, no shape - this will stop a bounding box flash + // when the bundle is first placed + return Shapes.empty(); + } + + // endregion + + // TODO: Review, I'm sure this could be neater + @Override + public ItemStack getCloneItemStack(BlockState state, HitResult target, LevelReader level, BlockPos pos, + Player player) { + if (level instanceof Level realLevel + && state.getOptionalValue(BlockStateProperties.WATERLOGGED).orElse(false)) { + var hitResult = Item.getPlayerPOVHitResult(realLevel, player, ClipContext.Fluid.NONE); + if (hitResult.getType() == HitResult.Type.MISS) { + return Items.AIR.getDefaultInstance(); + } + + if (hitResult.getBlockPos().equals(pos)) { + target = hitResult; + } else { + return level.getBlockState(hitResult.getBlockPos()) + .getCloneItemStack(hitResult, level, hitResult.getBlockPos(), player); + } + } + + if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity blockEntity) { + if (blockEntity.hasFacade() && FacadeHelper.areFacadesVisible()) { + return blockEntity.getFacadeBlock().asItem().getDefaultInstance(); + } + + Holder> conduit = blockEntity.getShape().getConduit(pos, target); + if (conduit == null) { + if (blockEntity.getConduits().isEmpty()) { + return ItemStack.EMPTY; + } + + conduit = blockEntity.getConduits().getFirst(); + } + + return ConduitBlockItem.getStackFor(conduit, 1); + } + + return super.getCloneItemStack(state, target, level, pos, player); + } + + @Override + public @Nullable PushReaction getPistonPushReaction(BlockState state) { + return PushReaction.BLOCK; + } + + @Override + protected boolean canBeReplaced(BlockState state, BlockPlaceContext useContext) { + return false; + } + + @Override + protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, + BlockPos neighborPos, boolean movedByPiston) { + + if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduit) { + conduit.updateNeighborRedstone(); + conduit.updateConnections(level, pos, neighborPos, true); + + // Invalidate caps in case of redstone update or something else. + level.invalidateCapabilities(pos); + } + + super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston); + } + + // region Water-logging + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return defaultBlockState().setValue(WATERLOGGED, + context.getLevel().getFluidState(context.getClickedPos()).getType() == Fluids.WATER); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(WATERLOGGED); + } + + @Override + public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, + BlockPos currentPos, BlockPos neighborPos) { + if (state.getValue(WATERLOGGED)) { + level.scheduleTick(currentPos, Fluids.WATER, Fluids.WATER.getTickDelay(level)); + } + + if (level.getBlockEntity(currentPos) instanceof ConduitBundleBlockEntity conduit) { + conduit.updateShape(); + } + + return super.updateShape(state, direction, neighborState, level, currentPos, neighborPos); + } + + @Override + public FluidState getFluidState(BlockState state) { + return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state); + } + + // endregion + + // region Place & Destroy Logic + + @Override + public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, + ItemStack stack) { + // TODO: Maybe we need to support non-player placement? + if (!(placer instanceof Player player)) { + return; + } + + if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduitBundle) { + Holder> conduit = stack.get(ConduitComponents.CONDUIT); + if (conduit != null) { + HitResult hit = player.pick(player.blockInteractionRange() + 5, 1, false); + Direction primaryConnectionSide = null; + if (hit instanceof BlockHitResult blockHitResult) { + primaryConnectionSide = blockHitResult.getDirection().getOpposite(); + } + + conduitBundle.addConduit(conduit, primaryConnectionSide, player); + } else { + // We might be placed using a facade item. If we are, apply the facade to the + var facadeProvider = stack.getCapability(ConduitCapabilities.CONDUIT_FACADE_PROVIDER); + if (facadeProvider != null && facadeProvider.isValid()) { + conduitBundle.setFacadeProvider(stack); + } + } + } + } + + @Override + public boolean onDestroyedByPlayer(BlockState state, Level level, BlockPos pos, Player player, boolean willHarvest, + FluidState fluid) { + // TODO: Destroying the last conduit in the block has a laggy disconnect for the + // neighbours... + + HitResult hit = player.pick(player.blockInteractionRange() + 5, 1, false); + + if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduitBundle) { + if (conduitBundle.hasFacade() && FacadeHelper.areFacadesVisible()) { + if (!level.isClientSide()) { + if (!player.getAbilities().instabuild) { + conduitBundle.dropFacadeItem(); + } + } + + int lightLevelBefore = level.getLightEmission(pos); + + conduitBundle.clearFacade(); + + // Handle light update + if (lightLevelBefore != level.getLightEmission(pos)) { + level.getLightEngine().checkBlock(pos); + } + + if (conduitBundle.isEmpty()) { + return super.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluid); + } else { + SoundType soundtype = state.getSoundType(level, pos, player); + level.playSound(player, pos, soundtype.getBreakSound(), SoundSource.BLOCKS, + (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F); + + level.gameEvent(GameEvent.BLOCK_DESTROY, pos, GameEvent.Context.of(player, state)); + return false; + } + } else { + // TODO: accessibility feature flag + Holder> conduit = null; +// if (true) { +// // If the player is holding a conduit and this flag is enabled, they purposely want to break the held conduit. +// conduit = ConduitA11yManager.getHeldConduit(); +// +// // If we don't have the held conduit, exit now. +// if (conduit != null && !conduitBundle.hasConduitStrict(conduit)) { +// level.playSound(player, pos, SoundEvents.GENERIC_SMALL_FALL, SoundSource.BLOCKS, 1F, 1F); +// return false; +// } +// +// // TODO: If we adopt the strategy of only showing a bigger box when we're holding a conduit, we need to +// // fire a packet to the server because we can't read whether the player is using the accessibility option on the server. +// +// // TODO: It could also be possible to leave this in? Idk if this would accidentally fire if the client state is up to date... +// } + + if (conduit == null) { + conduit = conduitBundle.getShape().getConduit(((BlockHitResult) hit).getBlockPos(), hit); + } + + if (conduit == null) { + if (!conduitBundle.getConduits().isEmpty()) { + level.playSound(player, pos, SoundEvents.GENERIC_SMALL_FALL, SoundSource.BLOCKS, 1F, 1F); + return false; + } + + return super.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluid); + } + + if (level.isClientSide) { + ConduitBreakParticle.addDestroyEffects(pos, state, conduit.value()); + } + + conduitBundle.removeConduit(conduit, player); + + if (conduitBundle.isEmpty()) { + return super.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluid); + } else { + SoundType soundtype = state.getSoundType(level, pos, player); + level.playSound(player, pos, soundtype.getBreakSound(), SoundSource.BLOCKS, + (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F); + + level.gameEvent(GameEvent.BLOCK_DESTROY, pos, GameEvent.Context.of(player, state)); + return false; + } + } + + } + + return super.onDestroyedByPlayer(state, level, pos, player, willHarvest, fluid); + } + + // endregion + + // region Open Menu + + @Override + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, + BlockHitResult hitResult) { + + if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduitBundle) { + // TODO: The connection shouldn't include the plate.. if we hit the plate open + // the first conduit? + var conduitConnection = conduitBundle.getShape().getConnectionFromHit(pos, hitResult); + + if (conduitConnection != null) { + if (conduitBundle.canOpenScreen(conduitConnection.getFirst(), conduitConnection.getSecond())) { + if (player instanceof ServerPlayer serverPlayer) { + ConduitMenu.openConduitMenu(serverPlayer, conduitBundle, conduitConnection.getFirst(), + conduitConnection.getSecond()); + } + + return InteractionResult.sidedSuccess(level.isClientSide()); + } + } + } + + return super.useWithoutItem(state, level, pos, player, hitResult); + } + + // endregion + + // region Item Interactions + + @Override + protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, + Player player, InteractionHand hand, BlockHitResult hitResult) { + + if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduitBundle) { + var result = addConduit(stack, level, pos, player, conduitBundle); + if (result != ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION) { + return result; + } + + // TODO: Yeta wrench handling + + result = addFacade(stack, level, pos, player, conduitBundle); + if (result != ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION) { + return result; + } + } + + return super.useItemOn(stack, state, level, pos, player, hand, hitResult); + } + + private ItemInteractionResult addConduit(ItemStack stack, Level level, BlockPos pos, Player player, + ConduitBundleBlockEntity conduitBundle) { + // Get the conduit from the item + Holder> conduit = stack.get(ConduitComponents.CONDUIT); + if (conduit == null) { + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + if (!conduitBundle.canAddConduit(conduit)) { + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + // Attempt to add to the bundle + HitResult hit = player.pick(player.blockInteractionRange() + 5, 1, false); + Direction primaryConnectionSide = null; + if (hit instanceof BlockHitResult blockHitResult) { + primaryConnectionSide = blockHitResult.getDirection().getOpposite(); + } + + AddConduitResult addResult = conduitBundle.addConduit(conduit, primaryConnectionSide, player); + + if (addResult instanceof AddConduitResult.Upgrade(Holder> replacedConduit)) { + if (!player.getAbilities().instabuild) { + stack.shrink(1); + player.getInventory().placeItemBackInInventory(ConduitBlockItem.getStackFor(replacedConduit, 1)); + } + } else if (addResult instanceof AddConduitResult.Insert) { + if (!player.getAbilities().instabuild) { + stack.shrink(1); + } + } else { + if (!FMLLoader.isProduction()) { + throw new IllegalStateException( + "ConduitBundleAccessor#canAddConduit returned true, but addConduit returned BLOCKED"); + } + + return ItemInteractionResult.FAIL; + } + + BlockState blockState = level.getBlockState(pos); + SoundType soundtype = blockState.getSoundType(level, pos, player); + level.playSound(player, pos, soundtype.getPlaceSound(), SoundSource.BLOCKS, + (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F); + level.gameEvent(GameEvent.BLOCK_PLACE, pos, GameEvent.Context.of(player, blockState)); + + return ItemInteractionResult.sidedSuccess(level.isClientSide()); + } + + private ItemInteractionResult addFacade(ItemStack stack, Level level, BlockPos pos, Player player, + ConduitBundleBlockEntity conduitBundle) { + var facadeProvider = stack.getCapability(ConduitCapabilities.CONDUIT_FACADE_PROVIDER); + if (facadeProvider == null || !facadeProvider.isValid()) { + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + if (conduitBundle.hasFacade()) { + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + int lightLevelBefore = level.getLightEmission(pos); + + conduitBundle.setFacadeProvider(stack); + if (!player.getAbilities().instabuild) { + stack.shrink(1); + } + + // Handle light change + if (lightLevelBefore != level.getLightEmission(pos)) { + level.getLightEngine().checkBlock(pos); + } + + // Block place effects + BlockState blockState = level.getBlockState(pos); + SoundType soundtype = blockState.getSoundType(level, pos, player); + level.playSound(player, pos, soundtype.getPlaceSound(), SoundSource.BLOCKS, + (soundtype.getVolume() + 1.0F) / 2.0F, soundtype.getPitch() * 0.8F); + level.gameEvent(GameEvent.BLOCK_PLACE, pos, GameEvent.Context.of(player, blockState)); + + return ItemInteractionResult.sidedSuccess(level.isClientSide()); + } + + // endregion + + // region Hardcoded Redstone Logic + + @Override + public boolean canConnectRedstone(BlockState state, BlockGetter level, BlockPos pos, + @Nullable Direction direction) { + if (direction == null) { + return false; + } + + if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduitBundle) { + // Get the redstone conduit + var redstoneConduit = conduitBundle.getConduitByType(ConduitTypes.REDSTONE.get()); + if (redstoneConduit == null) { + return false; + } + + var status = conduitBundle.getConnectionStatus(direction, redstoneConduit); + if (status != ConnectionStatus.CONNECTED_BLOCK) { + return false; + } + + var config = conduitBundle.getConnectionConfig(direction, redstoneConduit, + RedstoneConduitConnectionConfig.TYPE); + return config.canSend(ConduitRedstoneSignalAware.NONE); + } + + return false; + } + + @Override + protected int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { + return getNetworkSignal(level, pos, direction, false); + } + + @Override + protected int getDirectSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { + return getNetworkSignal(level, pos, direction, true); + } + + private int getNetworkSignal(BlockGetter level, BlockPos pos, Direction direction, boolean isDirectSignal) { + if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduitBundle) { + // No client redstone, network is server-only. + if (conduitBundle.getLevel().isClientSide()) { + return 0; + } + + // Get the redstone conduit + var redstoneConduit = conduitBundle.getConduitByType(ConduitTypes.REDSTONE.get()); + if (redstoneConduit == null) { + return 0; + } + + var status = conduitBundle.getConnectionStatus(direction.getOpposite(), redstoneConduit); + if (status != ConnectionStatus.CONNECTED_BLOCK) { + return 0; + } + + var config = conduitBundle.getConnectionConfig(direction.getOpposite(), redstoneConduit, + RedstoneConduitConnectionConfig.TYPE); + if (!config.canSend(ConduitRedstoneSignalAware.NONE)) { + return 0; + } + + if (isDirectSignal && !config.isStrongOutputSignal()) { + return 0; + } + + var node = conduitBundle.getConduitNode(redstoneConduit); + var network = node.getNetwork(); + if (network == null) { + return 0; + } + + var context = network.getContext(RedstoneConduitNetworkContext.TYPE); + if (context == null) { + return 0; + } + + if (node.getInsertFilter(direction) instanceof RedstoneInsertFilter redstoneInsertFilter) { + return redstoneInsertFilter.getOutputSignal(context, config.sendColor()); + } + + return context.getSignal(config.sendColor()); + } + + return 0; + } + + // endregion + + // region Facade Behaviours + + private Optional getFacadeBlock(BlockGetter level, BlockPos pos) { + if (level.getBlockEntity(pos) instanceof ConduitBundleBlockEntity conduitBundle) { + if (conduitBundle.hasFacade()) { + return Optional.of(conduitBundle.getFacadeBlock()); + } + } + + return Optional.empty(); + } + + @Override + public BlockState getAppearance(BlockState state, BlockAndTintGetter level, BlockPos pos, Direction side, + @Nullable BlockState queryState, @Nullable BlockPos queryPos) { + + Optional facade = getFacadeBlock(level, pos); + if (facade.isPresent()) { + return facade.get().defaultBlockState(); + } + + return super.getAppearance(state, level, pos, side, queryState, queryPos); + } + + @Override + public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) { + Optional facade = getFacadeBlock(level, pos); + if (facade.isPresent()) { + return facade.get().getLightEmission(facade.get().defaultBlockState(), level, pos); + } + + return super.getLightEmission(state, level, pos); + } + + @Override + public float getFriction(BlockState state, LevelReader level, BlockPos pos, @Nullable Entity entity) { + Optional facade = getFacadeBlock(level, pos); + if (facade.isPresent()) { + return facade.get().getFriction(facade.get().defaultBlockState(), level, pos, entity); + } + + return super.getFriction(state, level, pos, entity); + } + + @Override + public SoundType getSoundType(BlockState state, LevelReader level, BlockPos pos, @Nullable Entity entity) { + Optional facade = getFacadeBlock(level, pos); + if (facade.isPresent()) { + return facade.get().getSoundType(facade.get().defaultBlockState(), level, pos, entity); + } + + return super.getSoundType(state, level, pos, entity); + } + + @Override + public boolean supportsExternalFaceHiding(BlockState state) { + return true; + } + + // endregion +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java new file mode 100644 index 0000000000..bdcf262252 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleBlockEntity.java @@ -0,0 +1,1655 @@ +package com.enderio.conduits.common.conduit.bundle; + +import com.enderio.base.api.UseOnly; +import com.enderio.base.common.blockentity.Wrenchable; +import com.enderio.conduits.ConduitNBTKeys; +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.ConduitCapabilities; +import com.enderio.conduits.api.ConduitType; +import com.enderio.conduits.api.bundle.AddConduitResult; +import com.enderio.conduits.api.bundle.ConduitBundleAccessor; +import com.enderio.conduits.api.bundle.ConduitInventory; +import com.enderio.conduits.api.bundle.SlotType; +import com.enderio.conduits.api.connection.ConnectionStatus; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.facade.FacadeType; +import com.enderio.conduits.api.network.node.NodeData; +import com.enderio.conduits.client.model.conduit.bundle.ConduitBundleRenderState; +import com.enderio.conduits.common.conduit.ConduitBlockItem; +import com.enderio.conduits.common.conduit.ConduitSavedData; +import com.enderio.conduits.common.conduit.ConduitSorter; +import com.enderio.conduits.common.conduit.graph.ConduitConnectionHost; +import com.enderio.conduits.common.conduit.graph.ConduitDataContainer; +import com.enderio.conduits.common.conduit.graph.ConduitGraphContext; +import com.enderio.conduits.common.conduit.graph.ConduitGraphObject; +import com.enderio.conduits.common.conduit.graph.ConduitGraphUtility; +import com.enderio.conduits.common.conduit.legacy.ConnectionState; +import com.enderio.conduits.common.conduit.legacy.DynamicConnectionState; +import com.enderio.conduits.common.conduit.legacy.StaticConnectionStates; +import com.enderio.conduits.common.conduit.menu.ConduitMenu; +import com.enderio.conduits.common.init.ConduitBlockEntities; +import com.enderio.conduits.common.init.ConduitTypes; +import com.enderio.core.common.blockentity.EnderBlockEntity; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.gigaherz.graph3.Graph; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import me.liliandev.ensure.ensures.EnsureSide; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.Clearable; +import net.minecraft.world.Container; +import net.minecraft.world.ItemInteractionResult; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.fml.LogicalSide; +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.neoforge.capabilities.BlockCapability; +import net.neoforged.neoforge.capabilities.ICapabilityProvider; +import net.neoforged.neoforge.client.model.data.ModelData; +import org.jetbrains.annotations.Nullable; + +public final class ConduitBundleBlockEntity extends EnderBlockEntity + implements ConduitBundleAccessor, Clearable, Wrenchable, ConduitMenu.ConnectionAccessor { + + public static final int MAX_CONDUITS = 9; + + @UseOnly(LogicalSide.CLIENT) + public static final Map FACADES = new HashMap<>(); + + private ItemStack facadeProvider = ItemStack.EMPTY; + + private List>> conduits = new ArrayList<>(); + + private Map>, ConnectionContainer> conduitConnections = new HashMap<>(); + + private final ConduitBundleInventory inventory; + + // Map of all conduit nodes for this bundle. + private final Map>, ConduitGraphObject> conduitNodes = new HashMap<>(); + + // Data recovery mechanism + private final Map>, ConduitGraphObject> lazyNodes = new HashMap<>(); + private ListTag lazyNodeNBT = null; + private Map>, NodeData> lazyNodeData = null; + + // Client-side extra render data + @UseOnly(LogicalSide.CLIENT) + private final Map>, CompoundTag> clientConduitExtraWorldData = new HashMap<>(); + + private final ConduitShape shape = new ConduitShape(); + + private boolean hasDirtyNodes = false; + + // Deferred connection check + private UpdateState checkConnection = UpdateState.NONE; + + // NBT Keys + private static final String FACADE_PROVIDER_KEY = "FacadeProvider"; + private static final String CONDUITS_KEY = "Conduits"; + private static final String CONNECTIONS_KEY = "Connections"; + private static final String CONDUIT_INV_KEY = "ConduitInv"; + private static final String NODE_DATA_KEY = "NodeData"; + + private static final String CONDUIT_CLIENT_WORLD_DATA_KEY = "ConduitWorldData"; + + public ConduitBundleBlockEntity(BlockPos worldPosition, BlockState blockState) { + super(ConduitBlockEntities.CONDUIT.get(), worldPosition, blockState); + + inventory = new ConduitBundleInventory(this) { + @Override + protected void onChanged() { + setChanged(); + } + }; + } + + @Override + public void clearContent() { + // Remove all conduits and facades, this is normally called by /set + for (var conduit : getConduits()) { + removeConduit(conduit, null); + } + + clearFacade(); + } + + @Override + public void serverTick() { + super.serverTick(); + + if (level != null) { + checkConnection = checkConnection.next(); + if (checkConnection.isInitialized()) { + updateConnections(level, getBlockPos(), null, false); + } + + if (hasDirtyNodes) { + // This is for sending updates to clients when the nodes are dirty + // as such we only fire a block update + // TODO: We're also saving here, but maybe we shouldn't bother? + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_ALL); + setChanged(); + hasDirtyNodes = false; + } + } + } + + @Override + public void onLoad() { + super.onLoad(); + + updateShape(); + updateNeighborRedstone(); + + if (level instanceof ServerLevel serverLevel) { + // Fire on-created events + for (var conduit : conduits) { + conduit.value().onCreated(conduitNodes.get(conduit), level, getBlockPos(), null); + } + + // Attempt to make connections for recovered nodes. + for (var entry : lazyNodes.entrySet()) { + Holder> conduit = entry.getKey(); + ConduitGraphObject node = entry.getValue(); + + Graph graph = Objects.requireNonNull(node.getGraph()); + + for (Direction dir : Direction.values()) { + tryConnectTo(dir, conduit, false); + } + + ConduitSavedData.addPotentialGraph(conduit, graph, serverLevel); + } + } + + // Update lighting engine now that the bundle is loaded + if (level != null && hasFacade()) { + level.getLightEngine().checkBlock(getBlockPos()); + } + } + + /** + * Fire all relevant updates when the conduits or connections change. + */ + private void bundleChanged() { + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_ALL); + level.updateNeighborsAt(getBlockPos(), getBlockState().getBlock()); + level.invalidateCapabilities(getBlockPos()); + setChanged(); + updateShape(); + + if (level.isClientSide()) { + updateModel(); + } + } + + // region Shape and Model + + public ConduitShape getShape() { + return shape; + } + + public void updateShape() { + shape.updateConduit(this); + } + + @EnsureSide(EnsureSide.Side.CLIENT) + public void updateModel() { + requestModelDataUpdate(); + if (level != null) { + level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), Block.UPDATE_ALL); + } + + if (hasFacade()) { + FACADES.put(worldPosition, getFacadeBlock().defaultBlockState()); + } else { + FACADES.remove(worldPosition); + } + } + + @Override + public ModelData getModelData() { + return ModelData.builder().with(ConduitBundleRenderState.PROPERTY, ConduitBundleRenderState.of(this)).build(); + } + + // endregion + + // region Menu + + @Override + public boolean stillValid(Player player) { + return Container.stillValidBlockEntity(this, player); + } + + @Override + public List>> getAllOpenableConduits(Direction side) { + return conduits.stream().filter(c -> canOpenScreen(side, c)).toList(); + } + + public boolean canOpenScreen(Direction side, Holder> conduit) { + if (level == null) { + return false; + } + + if (!conduit.value().hasMenu()) { + return false; + } + + // If we've lost the conduit + if (!hasConduitStrict(conduit)) { + return false; + } + + // Cannot create a connection to a bundle + if (level.getBlockEntity(getBlockPos().relative(side)) instanceof ConduitBundleBlockEntity) { + return false; + } + + // TODO: This should be cached and updated whenever neighbors change... + return conduit.value().canForceConnectToBlock(level, getBlockPos(), side); + } + + @Override + public ItemInteractionResult onWrenched(UseOnContext context) { + if (level == null) { + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + // Get hit conduit + var side = context.getClickedFace(); + var conduit = shape.getConduit(context.getClickedPos(), context.getHitResult()); + if (conduit == null) { + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + var player = context.getPlayer(); + if (player != null && player.isSteppingCarefully()) { + removeConduit(conduit, player); + if (isEmpty()) { + level.setBlock(getBlockPos(), getBlockState().getFluidState().createLegacyBlock(), + level.isClientSide ? Block.UPDATE_ALL_IMMEDIATE : Block.UPDATE_ALL); + } + + return ItemInteractionResult.sidedSuccess(level.isClientSide()); + } + + // Get connection + var conduitConnection = shape.getConnectionFromHit(context.getClickedPos(), context.getHitResult()); + if (conduitConnection != null) { + + // Disable the connection + setConnectionStatus(conduitConnection.getFirst(), conduitConnection.getSecond(), ConnectionStatus.DISABLED); + onConnectionsUpdated(conduitConnection.getSecond()); + + // If we were connected to another bundle, we need to sever the graph + if (level.getBlockEntity(getBlockPos() + .relative(conduitConnection.getFirst())) instanceof ConduitBundleBlockEntity neighborBundle) { + neighborBundle.setConnectionStatus(conduitConnection.getFirst().getOpposite(), + conduitConnection.getSecond(), ConnectionStatus.DISABLED); + neighborBundle.onConnectionsUpdated(conduitConnection.getSecond()); + + if (level instanceof ServerLevel serverLevel) { + ConduitGraphObject thisNode = getConduitNode(conduitConnection.getSecond()); + ConduitGraphObject otherNode = neighborBundle.getConduitNode(conduitConnection.getSecond()); + + if (thisNode.getGraph() != null && otherNode.getGraph() != null) { + if (thisNode.getGraph() == otherNode.getGraph()) { + thisNode.getGraph().removeSingleEdge(thisNode, otherNode); + thisNode.getGraph().removeSingleEdge(otherNode, thisNode); + } + + ConduitSavedData.addPotentialGraph(conduitConnection.getSecond(), thisNode.getGraph(), + serverLevel); + ConduitSavedData.addPotentialGraph(conduitConnection.getSecond(), otherNode.getGraph(), + serverLevel); + + bundleChanged(); + } else { + // TODO: Warn, this is a bad place to be. + } + } + } + + return ItemInteractionResult.sidedSuccess(level.isClientSide()); + } + + // Attempt to make a new forced connection + var status = getConnectionStatus(side, conduit); + if (!status.isConnected()) { + tryConnectTo(side, conduit, true); + return ItemInteractionResult.sidedSuccess(level.isClientSide()); + } + + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + // endregion + + // region Redstone Cache + + private boolean hasRedstoneSignal; + + public void updateNeighborRedstone() { + if (level == null) { + hasRedstoneSignal = false; + } else { + hasRedstoneSignal = level.hasNeighborSignal(getBlockPos()); + } + } + + private boolean hasRedstoneSignal(@Nullable DyeColor signalColor) { + if (hasRedstoneSignal) { + return true; + } + + // If we have no signal color, do not attempt to query a redstone conduit + if (signalColor == null) { + return false; + } + + var redstoneConduit = getConduitByType(ConduitTypes.REDSTONE.get()); + if (redstoneConduit == null) { + return false; + } + + var node = getConduitNode(redstoneConduit); + var network = node.getNetwork(); + if (network == null) { + return false; + } + + var context = network.getContext(ConduitTypes.ContextTypes.REDSTONE.get()); + if (context == null) { + return false; + } + + return context.isActive(signalColor); + } + + // endregion + + // region Capability Proxies + + public static ICapabilityProvider createCapabilityProvider( + BlockCapability cap) { + return (be, context) -> { + for (Holder> conduit : be.getConduits()) { + var proxiedCap = getProxiedCapability(cap, be, conduit, context); + if (proxiedCap != null) { + return proxiedCap; + } + } + + return null; + }; + } + + @Nullable + private static TCap getProxiedCapability(BlockCapability capability, + ConduitBundleBlockEntity blockEntity, Holder> conduit, @Nullable TContext context) { + + if (blockEntity.level == null) { + return null; + } + + ConduitGraphObject node = blockEntity.conduitNodes.get(conduit); + if (node.getNetwork() == null) { + return null; + } + + return conduit.value() + .proxyCapability(blockEntity.level, ConduitSavedData::isRedstoneActive, node, capability, context); + } + + // endregion + + // region Conduits + + public List>> getConduits() { + return Collections.unmodifiableList(conduits); + } + + @Override + public boolean hasConduitByType(Holder> conduit) { + return conduits.stream().anyMatch(c -> c.value().canConnectToConduit(conduit)); + } + + @Override + public boolean hasConduitByType(ConduitType conduitType) { + return conduits.stream().anyMatch(c -> c.value().type() == conduitType); + } + + @Override + public boolean hasConduitStrict(Holder> conduit) { + return conduits.contains(conduit); + } + + @Nullable + public Holder> getConduitByType(ConduitType conduitType) { + return conduits.stream().filter(c -> c.value().type() == conduitType).findFirst().orElse(null); + } + + @Override + public boolean isEmpty() { + return conduits.isEmpty() && !hasFacade(); + } + + @Override + public boolean isFull() { + return conduits.size() == MAX_CONDUITS; + } + + /** + * Finds a conduit which is replaceable by the given conduit. + * @param possibleReplacement the conduit that may replace another. + * @return the conduit that can be replaced, or empty if none can be replaced. + */ + private Optional>> findReplacementCandidate(Holder> possibleReplacement) { + return conduits.stream() + .filter(existingConduit -> existingConduit.value().canBeReplacedBy(possibleReplacement)) + .findFirst(); + } + + /** + * @param conduit the conduit to check for. + * @return whether the provided conduit is compatible with the other conduits in the bundle. + */ + private boolean isConduitCompatibleWithExisting(Holder> conduit) { + return conduits.stream().allMatch(existingConduit -> existingConduit.value().canBeInSameBundle(conduit)); + } + + @Override + public boolean canAddConduit(Holder> conduit) { + if (level == null) { + return false; + } + + if (isFull()) { + return false; + } + + if (hasConduitStrict(conduit)) { + return false; + } + + if (findReplacementCandidate(conduit).isPresent()) { + return true; + } + + // If there are no replacement opportunities, we cannot have a conduit of this + // type. + if (hasConduitByType(conduit)) { + return false; + } + + return isConduitCompatibleWithExisting(conduit); + } + + @Override + public AddConduitResult addConduit(Holder> conduit, @Nullable Direction primaryConnectionSide, + @Nullable Player player) { + if (level == null) { + return new AddConduitResult.Blocked(); + } + + if (isFull()) { + return new AddConduitResult.Blocked(); + } + + if (hasConduitStrict(conduit)) { + return new AddConduitResult.Blocked(); + } + + // Attempt to upgrade an existing conduit. + AddConduitResult result; + var replacementCandidate = findReplacementCandidate(conduit); + if (replacementCandidate.isPresent()) { + int replacementIndex = conduits.indexOf(replacementCandidate.get()); + conduits.set(replacementIndex, conduit); + + // Add connections entry + var oldConnectionContainer = conduitConnections.remove(replacementCandidate.get()); + conduitConnections.put(conduit, oldConnectionContainer.copyFor(conduit)); + + if (!level.isClientSide()) { + ConduitGraphObject oldNode = conduitNodes.remove(replacementCandidate.get()); + + ConduitGraphObject newNode; + if (oldNode != null) { + // Copy data into the node + newNode = new ConduitGraphObject(getBlockPos(), oldNode.getNodeData()); + conduit.value().onRemoved(oldNode, level, getBlockPos()); + oldNode.getGraph().remove(oldNode); + } else { + newNode = new ConduitGraphObject(getBlockPos()); + } + + setNode(conduit, newNode); + conduit.value().onCreated(newNode, level, getBlockPos(), player); + } + + result = new AddConduitResult.Upgrade(replacementCandidate.get()); + } else { + // If there are no replacement opportunities, we cannot have a conduit of this + // type. + if (hasConduitByType(conduit)) { + return new AddConduitResult.Blocked(); + } + + // Ensure there are no incompatible conduits. + if (!isConduitCompatibleWithExisting(conduit)) { + return new AddConduitResult.Blocked(); + } + + // Ensure the conduits list is sorted correctly. + int id = ConduitSorter.getSortIndex(conduit); + var addBefore = conduits.stream().filter(c -> ConduitSorter.getSortIndex(c) > id).findFirst(); + if (addBefore.isPresent()) { + conduits.add(conduits.indexOf(addBefore.get()), conduit); + } else { + conduits.add(conduit); + } + + // Add connections entry + conduitConnections.put(conduit, new ConnectionContainer(conduit)); + + if (!level.isClientSide()) { + // Create the new node + ConduitGraphObject node = new ConduitGraphObject(getBlockPos()); + + // Add the node + setNode(conduit, node); + + // NeoForge contains a patch that calls onLoad after the conduit has been placed + // if it's the first one, so onCreated would be called twice. it's easier to + // detect here + if (conduits.size() != 1) { + conduit.value().onCreated(node, level, getBlockPos(), player); + } + } + + result = new AddConduitResult.Insert(); + } + + if (!level.isClientSide()) { + // Attach the new node to its own graph + var node = getConduitNode(conduit); + ConduitGraphUtility.integrate(conduit, node, List.of()); + + // Attach the bundle + node.attach(new ConnectionHost(this, conduit)); + } + + // Now attempt to make connections, starting from the "primary" side (clicked or + // facing direction) + if (primaryConnectionSide != null) { + tryConnectTo(primaryConnectionSide, conduit, false); + } + + for (Direction side : Direction.values()) { + if (side != primaryConnectionSide) { + tryConnectTo(side, conduit, false); + } + } + + if (level instanceof ServerLevel serverLevel) { + ConduitSavedData.addPotentialGraph(conduit, Objects.requireNonNull(getConduitNode(conduit).getGraph()), + serverLevel); + } + + if (result instanceof AddConduitResult.Upgrade(Holder> replacedConduit) + && !replacedConduit.value().canConnectToConduit(conduit)) { + removeNeighborConnections(replacedConduit); + } + + bundleChanged(); + return result; + } + + @Override + public void removeConduit(Holder> conduit, @Nullable Player player) { + if (level == null) { + return; + } + + if (!hasConduitStrict(conduit)) { + if (!FMLLoader.isProduction()) { + throw new IllegalArgumentException( + "Conduit: " + conduit.getRegisteredName() + " is not present in conduit bundle " + + Arrays.toString(conduits.stream().map(Holder::getRegisteredName).toArray())); + } + + return; + } + + // Drop the conduit and it's inventory items. + if (!level.isClientSide()) { + if (player != null && !player.getAbilities().instabuild) { + dropItem(ConduitBlockItem.getStackFor(conduit, 1)); + for (Direction side : Direction.values()) { + dropConnectionItems(side, conduit); + } + } + } + + // Node remove event + if (!level.isClientSide()) { + var node = getConduitNode(conduit); + if (node != null) { + conduit.value().onRemoved(node, level, getBlockPos()); + node.detach(); + + // Remove from the graph. + if (node.getGraph() != null) { + node.getGraph().remove(node); + } + } + } + + // Remove from the inventory's storage. + inventory.removeConduit(conduit); + + // Remove from the bundle + conduits.remove(conduit); + conduitConnections.remove(conduit); + conduitNodes.remove(conduit); + + // Remove neighbour connections + removeNeighborConnections(conduit); + + // Fire redstone updates, if applicable. + if (conduit.value().type() == ConduitTypes.REDSTONE.get()) { + for (Direction side : Direction.values()) { + redstoneConduitChanged(side); + } + } + + bundleChanged(); + } + + /** + * Removes connections to neigbouring bundles to the given conduit. + * @param conduit The conduit in this conduit that should be disconnected from other conduits. + */ + public void removeNeighborConnections(Holder> conduit) { + for (Direction dir : Direction.values()) { + removeNeighborConnection(dir, conduit); + } + } + + private void removeNeighborConnection(Direction side, Holder> conduit) { + if (level == null) { + return; + } + + if (!(level.getBlockEntity(getBlockPos().relative(side)) instanceof ConduitBundleBlockEntity neighborBundle)) { + return; + } + + neighborBundle.disconnect(side.getOpposite(), conduit); + + if (level instanceof ServerLevel serverLevel) { + if (neighborBundle.hasConduitByType(conduit)) { + Optional.of(neighborBundle.getConduitNode(conduit)) + .map(ConduitGraphObject::getGraph) + .filter(Objects::nonNull) + .ifPresent(graph -> ConduitSavedData.addPotentialGraph(conduit, graph, serverLevel)); + } + } + } + + private void dropItem(ItemStack stack) { + if (level != null) { + level.addFreshEntity(new ItemEntity(level, getBlockPos().getX(), getBlockPos().getY(), getBlockPos().getZ(), + stack.copy())); + } + } + + @Override + public ConduitInventory getInventory(Holder> conduit) { + if (!hasConduitStrict(conduit)) { + throw new IllegalStateException("Conduit not found in bundle."); + } + + return inventory.getInventoryFor(conduit); + } + + @EnsureSide(EnsureSide.Side.SERVER) + public ConduitGraphObject getConduitNode(Holder> conduit) { + if (!hasConduitByType(conduit)) { + throw new IllegalStateException("Conduit not found in bundle."); + } + + return conduitNodes.get(conduit); + } + + @Override + @Nullable + public CompoundTag getConduitExtraWorldData(Holder> conduit) { + if (level != null && !level.isClientSide()) { + return conduit.value().getExtraWorldData(this, getConduitNode(conduit)); + } + + return clientConduitExtraWorldData.get(conduit); + } + + // Synced by the GUI, only available on the server BE. + @EnsureSide(EnsureSide.Side.SERVER) + @Override + @Nullable + public CompoundTag getConduitExtraGuiData(Direction side, Holder> conduit) { + return conduit.value().getExtraGuiData(this, getConduitNode(conduit), side); + } + + @EnsureSide(EnsureSide.Side.SERVER) + private void setNode(Holder> conduit, ConduitGraphObject loadedNode) { + conduitNodes.put(conduit, loadedNode); + + // Attach to the node to provide connection data and inventory. + loadedNode.attach(new ConnectionHost(this, conduit)); + } + + // endregion + + // region Connections + + @Override + public List>> getConnectedConduits(Direction side) { + return conduitConnections.entrySet() + .stream() + .filter(e -> e.getValue().getStatus(side).isConnected()) + .map(Map.Entry::getKey) + .sorted(Comparator.comparingInt(ConduitSorter::getSortIndex)) + .toList(); + } + + @Override + public ConnectionStatus getConnectionStatus(Direction side, Holder> conduit) { + return conduitConnections.computeIfAbsent(conduit, ConnectionContainer::new).getStatus(side); + } + + @Override + public ConnectionConfig getConnectionConfig(Direction side, Holder> conduit) { + return conduitConnections.get(conduit).getConfig(side); + } + + @Override + public T getConnectionConfig(Direction side, Holder> conduit, + ConnectionConfigType type) { + var config = conduitConnections.get(conduit).getConfig(side); + if (config.type() != type) { + throw new IllegalStateException("Connection config type mismatch."); + } + + // noinspection unchecked + return (T) config; + } + + @Override + public void setConnectionConfig(Direction side, Holder> conduit, ConnectionConfig config) { + if (config.type() != conduit.value().connectionConfigType()) { + throw new IllegalArgumentException("Connection config is not the right type for this conduit."); + } + + conduitConnections.get(conduit).setConfig(side, config); + if (config.isConnected()) { + setConnectionStatus(side, conduit, ConnectionStatus.CONNECTED_BLOCK); + } else { + setConnectionStatus(side, conduit, ConnectionStatus.DISABLED); + } + + bundleChanged(); + } + + public void setConnectionStatus(Direction side, Holder> conduit, ConnectionStatus status) { + if (!hasConduitStrict(conduit)) { + throw new IllegalArgumentException("Conduit is not present in this bundle."); + } + + conduitConnections.get(conduit).setStatus(side, status); + bundleChanged(); + } + + @Override + public boolean isEndpoint(Direction side) { + return conduitConnections.values().stream().anyMatch(c -> c.hasEndpoint(side)); + } + + // TODO: This needs a better name or to handle blocks as well as conduits before + // it can be exposed via the interface. + public boolean canConnectTo(Direction side, Holder> conduit, ConduitGraphObject otherNode, + boolean isForcedConnection) { + if (level == null) { + return false; + } + + if (!doTypesMatch(conduit)) { + return false; + } + + if (conduit.value().hasServerConnectionChecks()) { + if (level.isClientSide()) { + // If this has server-side logic, don't continue locally. + return false; + } + + // Gated behind hasServerConnectionChecks to ensure conduit devs do not forget + // to override both. + if (!conduit.value().canConnectConduits(conduitNodes.get(conduit), otherNode)) { + return false; + } + } + + return isForcedConnection || conduitConnections.get(conduit).getStatus(side) != ConnectionStatus.DISABLED; + } + + private boolean doTypesMatch(Holder> conduitToMatch) { + for (Holder> conduit : conduits) { + if (conduit.value().canConnectToConduit(conduitToMatch)) { + return true; + } + } + + return false; + } + + public boolean tryConnectTo(Direction side, Holder> conduit, boolean isForcedConnection) { + if (level == null) { + return false; + } + + if (!hasConduitStrict(conduit)) { + throw new IllegalArgumentException("Conduit is not present in this bundle."); + } + + // Do not attempt to connect if we're not forcing a disabled connection + ConnectionStatus currentStatus = conduitConnections.get(conduit).getStatus(side); + if ((!isForcedConnection && currentStatus == ConnectionStatus.DISABLED)) { + return false; + } + + if (level.getBlockEntity( + getBlockPos().relative(side)) instanceof ConduitBundleBlockEntity neighbourConduitBundle) { + var node = conduitNodes.get(conduit); + + // Connect to another bundle which has a compatible conduit. + if (neighbourConduitBundle.canConnectTo(side.getOpposite(), conduit, node, isForcedConnection)) { + // Make connections to both sides + connectConduit(side, conduit); + neighbourConduitBundle.connectConduit(side.getOpposite(), conduit); + + // Fire node connection events + if (!level.isClientSide()) { + var neighbourNode = neighbourConduitBundle.getConduitNode(conduit); + conduit.value().onConnectTo(node, neighbourNode); + conduit.value().onConnectTo(neighbourNode, node); + + // Connect the graphs together + ConduitGraphUtility.connect(conduit, node, neighbourNode); + } + return true; + } + + disconnect(side, conduit); + return false; + } else if (conduit.value().canConnectToBlock(level, getBlockPos(), side) + || (isForcedConnection && conduit.value().canForceConnectToBlock(level, getBlockPos(), side))) { + connectBlock(side, conduit); + return true; + } + + return false; + } + + public void onConnectionsUpdated(Holder> conduit) { + if (level != null && !level.isClientSide) { + var node = getConduitNode(conduit); + + Set connectedSides = Arrays.stream(Direction.values()) + .filter(direction -> getConnectionStatus(direction, conduit).isConnected()) + .collect(Collectors.toSet()); + + conduit.value().onConnectionsUpdated(node, level, getBlockPos(), connectedSides); + } + } + + private void connectConduit(Direction side, Holder> conduit) { + conduitConnections.computeIfAbsent(conduit, ConnectionContainer::new) + .setStatus(side, ConnectionStatus.CONNECTED_CONDUIT); + onConnectionsUpdated(conduit); + + bundleChanged(); + } + + private void connectBlock(Direction side, Holder> conduit) { + conduitConnections.computeIfAbsent(conduit, ConnectionContainer::new) + .setStatus(side, ConnectionStatus.CONNECTED_BLOCK); + onConnectionsUpdated(conduit); + + bundleChanged(); + } + + // TODO: poorly named, we're disconnecting from another conduit on the given + // side. + private void disconnect(Direction side, Holder> conduit) { + boolean hasChanged = false; + for (var c : conduits) { + if (c.value().canConnectToConduit(conduit)) { + conduitConnections.computeIfAbsent(c, ConnectionContainer::new) + .setStatus(side, ConnectionStatus.DISCONNECTED); + onConnectionsUpdated(c); + hasChanged = true; + } + } + + if (hasChanged) { + bundleChanged(); + } + } + + private void dropConnectionItems(Direction side, Holder> conduit) { + for (SlotType slotType : SlotType.values()) { + ItemStack stack = inventory.getStackInSlot(conduit, side, slotType); + if (!stack.isEmpty()) { + dropItem(stack); + inventory.setStackInSlot(conduit, side, slotType, ItemStack.EMPTY); + } + } + } + + // TODO: I've not properly reviewed this method. + public void updateConnections(Level level, BlockPos pos, @Nullable BlockPos fromPos, boolean shouldActivate) { + if (fromPos != null && level.getBlockEntity(fromPos) instanceof ConduitBundleBlockEntity) { + return; + } + + for (Direction side : Direction.values()) { + for (var conduit : conduits) { + if (shouldActivate && conduit.value().hasConnectionDelay()) { + checkConnection = checkConnection.activate(); + continue; + } + + var currentStatus = getConnectionStatus(side, conduit); + + if (currentStatus == ConnectionStatus.DISCONNECTED) { + tryConnectTo(side, conduit, false); + } else if (currentStatus == ConnectionStatus.CONNECTED_BLOCK) { + if (!conduit.value().canForceConnectToBlock(level, getBlockPos(), side)) { + disconnect(side, conduit); + onConnectionsUpdated(conduit); + } + } + } + } + } + + // endregion + + // region Facades + + @Override + public boolean hasFacade() { + return !facadeProvider.isEmpty() + && facadeProvider.getCapability(ConduitCapabilities.CONDUIT_FACADE_PROVIDER) != null; + } + + @Override + public Block getFacadeBlock() { + if (facadeProvider.isEmpty()) { + throw new IllegalStateException("This bundle has no facade provider."); + } + + var provider = facadeProvider.getCapability(ConduitCapabilities.CONDUIT_FACADE_PROVIDER); + if (provider == null) { + // TODO: How to handle this error gracefully? + // For now default to a bedrock facade. + return Blocks.BEDROCK; + } + + return provider.block(); + } + + @Override + public FacadeType getFacadeType() { + if (facadeProvider.isEmpty()) { + throw new IllegalStateException("This bundle has no facade provider."); + } + + var provider = facadeProvider.getCapability(ConduitCapabilities.CONDUIT_FACADE_PROVIDER); + if (provider == null) { + return FacadeType.BASIC; + } + + return provider.type(); + } + + @Override + public ItemStack getFacadeProvider() { + return facadeProvider; + } + + @Override + public void setFacadeProvider(ItemStack facadeProvider) { + this.facadeProvider = facadeProvider.copyWithCount(1); + bundleChanged(); + } + + @Override + public void clearFacade() { + this.facadeProvider = ItemStack.EMPTY; + bundleChanged(); + } + + public void dropFacadeItem() { + dropItem(facadeProvider); + } + + // endregion + + // region Network Sync + + @Override + public Packet getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } + + @Override + public CompoundTag getUpdateTag(HolderLookup.Provider registries) { + CompoundTag updateTag = super.getUpdateTag(registries); + + // Send conduit sync data + ListTag nodeDataList = new ListTag(); + + for (var conduit : conduits) { + var node = getConduitNode(conduit); + var clientDataTag = conduit.value().getExtraWorldData(this, node); + if (clientDataTag != null && !clientDataTag.isEmpty()) { + CompoundTag tag = new CompoundTag(); + tag.put("Conduit", + Conduit.CODEC.encodeStart(registries.createSerializationContext(NbtOps.INSTANCE), conduit) + .getOrThrow()); + tag.put("Data", clientDataTag); + nodeDataList.add(tag); + } + } + + updateTag.put(CONDUIT_CLIENT_WORLD_DATA_KEY, nodeDataList); + return updateTag; + } + + @Override + public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket pkt, + HolderLookup.Provider lookupProvider) { + super.onDataPacket(net, pkt, lookupProvider); + + updateShape(); + updateModel(); + } + + @Override + public void handleUpdateTag(CompoundTag syncData, HolderLookup.Provider lookupProvider) { + super.handleUpdateTag(syncData, lookupProvider); + + updateShape(); + updateModel(); + } + + // endregion + + // region Serialization + + @Override + public void setLevel(Level level) { + super.setLevel(level); + + if (!level.isClientSide()) { + loadFromSavedData(); + } + } + + @EnsureSide(EnsureSide.Side.SERVER) + private void loadFromSavedData() { + if (!(level instanceof ServerLevel serverLevel)) { + return; + } + + ConduitSavedData savedData = ConduitSavedData.get(serverLevel); + for (int i = 0; i < conduits.size(); i++) { + Holder> type = conduits.get(i); + loadConduitFromSavedData(savedData, type, i); + } + + lazyNodeData = null; + lazyNodeNBT = null; + } + + @EnsureSide(EnsureSide.Side.SERVER) + private void loadConduitFromSavedData(ConduitSavedData savedData, Holder> conduit, int typeIndex) { + if (level == null || !(level instanceof ServerLevel serverLevel)) { + return; + } + + ConduitGraphObject node = savedData.takeUnloadedNodeIdentifier(conduit, this.worldPosition); + if (node == null && conduitNodes.get(conduit) == null) { + // Attempt to recover node data + NodeData nodeData = null; + if (lazyNodeData != null && lazyNodeData.containsKey(conduit)) { + nodeData = lazyNodeData.remove(conduit); + } + + if (nodeData == null) { + // Attempt to load legacy recovery data. + ConduitDataContainer dataContainer = null; + if (lazyNodeNBT != null && typeIndex < lazyNodeNBT.size()) { + dataContainer = ConduitDataContainer.parse(level.registryAccess(), + lazyNodeNBT.getCompound(typeIndex)); + } + + if (dataContainer != null) { + node = new ConduitGraphObject(getBlockPos(), dataContainer); + } else { + node = new ConduitGraphObject(getBlockPos()); + } + } else { + node = new ConduitGraphObject(getBlockPos(), nodeData); + } + + ConduitGraphUtility.integrate(conduit, node, List.of()); + setNode(conduit, node); + lazyNodes.put(conduit, node); + } else if (node != null) { + setNode(conduit, node); + } + } + + @Override + public void onChunkUnloaded() { + super.onChunkUnloaded(); + + if (level == null) { + return; + } + + if (level instanceof ServerLevel serverLevel) { + var savedData = ConduitSavedData.get(serverLevel); + + for (var conduit : conduits) { + var node = conduitNodes.get(conduit); + conduit.value().onRemoved(node, level, getBlockPos()); + node.detach(); + savedData.putUnloadedNodeIdentifier(conduit, this.worldPosition, node); + } + } else if (level.isClientSide()) { + FACADES.remove(worldPosition); + } + } + + @Override + public void setRemoved() { + super.setRemoved(); + + if (level != null && level.isClientSide()) { + FACADES.remove(worldPosition); + } + } + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + tag.put(CONDUIT_INV_KEY, inventory.serializeNBT(registries)); + + var serializationContext = registries.createSerializationContext(NbtOps.INSTANCE); + + // NEW: Save node data in case of need for recovery + ListTag nodeData = new ListTag(); + for (Holder> conduit : conduits) { + var data = conduitNodes.get(conduit).getNodeData(); + + if (data != null) { + CompoundTag nodeTag = new CompoundTag(); + nodeTag.put("Conduit", Conduit.CODEC.encodeStart(serializationContext, conduit).getOrThrow()); + nodeTag.put("Data", NodeData.GENERIC_CODEC.encodeStart(serializationContext, data).getOrThrow()); + nodeData.add(nodeTag); + } + } + tag.put(NODE_DATA_KEY, nodeData); + } + + @Override + protected void saveAdditionalSynced(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditionalSynced(tag, registries); + + if (!conduits.isEmpty()) { + ListTag conduitList = new ListTag(); + for (var conduit : conduits) { + conduitList + .add(Conduit.CODEC.encodeStart(registries.createSerializationContext(NbtOps.INSTANCE), conduit) + .getOrThrow()); + } + tag.put(CONDUITS_KEY, conduitList); + + // Save connections + ListTag conduitConnectionsList = new ListTag(); + for (var conduit : conduits) { + ListTag connectionsList = new ListTag(); + for (Direction side : Direction.values()) { + CompoundTag connectionTag = new CompoundTag(); + connectionTag.putString("Side", side.getSerializedName()); + connectionTag.putString("Status", getConnectionStatus(side, conduit).getSerializedName()); + + // Raw access to ensure we save the true data. + var config = conduitConnections.get(conduit).configs.get(side); + if (config != null && !config.equals(config.type().getDefault())) { + connectionTag.put("Config", + ConnectionConfig.GENERIC_CODEC + .encodeStart(registries.createSerializationContext(NbtOps.INSTANCE), config) + .getOrThrow()); + } + + connectionsList.add(connectionTag); + } + + conduitConnectionsList.add(connectionsList); + } + tag.put(CONNECTIONS_KEY, conduitConnectionsList); + } + + if (!facadeProvider.isEmpty()) { + tag.put(FACADE_PROVIDER_KEY, facadeProvider.save(registries)); + } + } + + @Override + protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + + if (tag.contains(ConduitNBTKeys.CONDUIT_BUNDLE)) { + // Convert the legacy bundle to the new format + var bundle = LegacyConduitBundle.parse(registries, tag.getCompound(ConduitNBTKeys.CONDUIT_BUNDLE)); + loadFromLegacyBundle(bundle); + } else { + // New save format + conduits.clear(); + if (tag.contains(CONDUITS_KEY, Tag.TAG_LIST)) { + // Get untyped list tag. + ListTag conduitList = (ListTag) tag.get(CONDUITS_KEY); + for (var conduitTag : conduitList) { + conduits.add(Conduit.CODEC.parse(registries.createSerializationContext(NbtOps.INSTANCE), conduitTag) + .getOrThrow()); + } + } + + // Load connections + conduitConnections.clear(); + if (tag.contains(CONNECTIONS_KEY)) { + ListTag conduitConnectionsList = tag.getList(CONNECTIONS_KEY, Tag.TAG_LIST); + + for (int i = 0; i < conduitConnectionsList.size(); i++) { + ListTag connectionsList = conduitConnectionsList.getList(i); + Holder> conduit = conduits.get(i); + + ConnectionContainer connections = new ConnectionContainer(conduit); + for (int j = 0; j < connectionsList.size(); j++) { + CompoundTag connectionTag = connectionsList.getCompound(j); + Direction side = Direction.byName(connectionTag.getString("Side")); + ConnectionStatus status = ConnectionStatus.byName(connectionTag.getString("Status")); + + if (side != null && status != null) { + connections.setStatus(side, status); + + if (connectionTag.contains("Config")) { + ConnectionConfig config = ConnectionConfig.GENERIC_CODEC + .parse(registries.createSerializationContext(NbtOps.INSTANCE), + connectionTag.get("Config")) + .getOrThrow(); + connections.setConfig(side, config); + } + } + } + + conduitConnections.put(conduit, connections); + } + } + + if (tag.contains(FACADE_PROVIDER_KEY)) { + facadeProvider = ItemStack.parseOptional(registries, tag.getCompound(FACADE_PROVIDER_KEY)); + } + } + + // Load inventory + if (tag.contains(CONDUIT_INV_KEY)) { + inventory.deserializeNBT(registries, tag.getCompound(CONDUIT_INV_KEY)); + } + + // Load node data used for recovery + if (tag.contains(ConduitNBTKeys.CONDUIT_EXTRA_DATA)) { + lazyNodeNBT = tag.getList(ConduitNBTKeys.CONDUIT_EXTRA_DATA, Tag.TAG_COMPOUND); + } else if (tag.contains(NODE_DATA_KEY)) { + var list = tag.getList(NODE_DATA_KEY, Tag.TAG_COMPOUND); + lazyNodeData = new HashMap<>(); + + var serializationContext = registries.createSerializationContext(NbtOps.INSTANCE); + + for (int i = 0; i < list.size(); i++) { + var nodeTag = list.getCompound(i); + var conduitParseResult = Conduit.CODEC.parse(serializationContext, nodeTag.get("Conduit")); + + if (conduitParseResult.isError()) { + continue; + } + + var dataParseResult = NodeData.GENERIC_CODEC.parse(serializationContext, nodeTag.get("Data")); + if (dataParseResult.isError()) { + continue; + } + + lazyNodeData.put(conduitParseResult.getOrThrow(), dataParseResult.getOrThrow()); + } + } + + // Load synced node data + if (tag.contains(CONDUIT_CLIENT_WORLD_DATA_KEY)) { + clientConduitExtraWorldData.clear(); + + ListTag nodeDataList = tag.getList(CONDUIT_CLIENT_WORLD_DATA_KEY, Tag.TAG_COMPOUND); + var serializationContext = registries.createSerializationContext(NbtOps.INSTANCE); + for (int i = 0; i < nodeDataList.size(); i++) { + CompoundTag nodeTag = nodeDataList.getCompound(i); + var conduit = Conduit.CODEC.parse(serializationContext, nodeTag.get("Conduit")).getOrThrow(); + clientConduitExtraWorldData.put(conduit, nodeTag.getCompound("Data")); + } + } + } + + // endregion + + // Special casing for redstone conduits. + private void redstoneConduitChanged(Direction side) { + if (level != null) { + BlockPos neighborPos = getBlockPos().relative(side); + if (!level.getBlockState(neighborPos).is(getBlockState().getBlock())) { + level.updateNeighborsAt(getBlockPos().relative(side), getBlockState().getBlock()); + } + } + } + + private class ConnectionContainer { + private final Holder> conduit; + private final Map statuses = new EnumMap<>(Direction.class); + private final Map configs = new EnumMap<>(Direction.class); + + public ConnectionContainer(Holder> conduit) { + this.conduit = conduit; + + var defaultConfig = conduit.value().connectionConfigType().getDefault(); + for (Direction dir : Direction.values()) { + statuses.put(dir, ConnectionStatus.DISCONNECTED); + configs.put(dir, defaultConfig); + } + } + + public ConnectionContainer copyFor(Holder> conduit) { + var copy = new ConnectionContainer(conduit); + copy.statuses.putAll(statuses); + + // Only copy connection config if compatible. + if (this.conduit.value().connectionConfigType() == conduit.value().connectionConfigType()) { + copy.configs.putAll(configs); + } + return copy; + } + + public ConnectionStatus getStatus(Direction side) { + return statuses.getOrDefault(side, ConnectionStatus.DISCONNECTED); + } + + public void setStatus(Direction side, ConnectionStatus status) { + statuses.put(side, status); + + if (status == ConnectionStatus.CONNECTED_BLOCK) { + if (configs.containsKey(side)) { + var config = configs.get(side); + if (!config.isConnected()) { + configs.put(side, config.reconnected()); + } + } + } + + if (conduit.value().type() == ConduitTypes.REDSTONE.get()) { + redstoneConduitChanged(side); + } + } + + public ConnectionConfig getConfig(Direction side) { + var defaultConfig = conduit.value().connectionConfigType().getDefault(); + var config = configs.getOrDefault(side, defaultConfig); + + // Ensure the connection type is correct. + // If it isn't, revert to the default. + if (config.type() != conduit.value().connectionConfigType()) { + config = defaultConfig; + configs.put(side, config); + bundleChanged(); + } + + // We keep the old state in case the wrench is used, but UI will need to show + // empty arrows. + if (statuses.get(side) != ConnectionStatus.CONNECTED_BLOCK && config.isConnected()) { + return config.disconnected(); + } + + return config; + } + + public void setConfig(Direction side, ConnectionConfig config) { + configs.put(side, config); + + if (conduit.value().type() == ConduitTypes.REDSTONE.get()) { + redstoneConduitChanged(side); + } + } + + public boolean hasEndpoint(Direction side) { + return getStatus(side) == ConnectionStatus.CONNECTED_BLOCK; + } + } + + private record ConnectionHost(ConduitBundleBlockEntity conduitBundle, Holder> conduit) + implements ConduitConnectionHost { + + @Override + public BlockPos pos() { + return conduitBundle.getBlockPos(); + } + + @Override + public boolean isConnectedTo(Direction side) { + return conduitBundle.getConnectionStatus(side, conduit) == ConnectionStatus.CONNECTED_BLOCK; + } + + @Override + public ConnectionConfig getConnectionConfig(Direction side) { + return conduitBundle.getConnectionConfig(side, conduit); + } + + @Override + public void setConnectionConfig(Direction side, ConnectionConfig connectionConfig) { + conduitBundle.setConnectionConfig(side, conduit, connectionConfig); + } + + @Override + public ConduitInventory inventory() { + return conduitBundle.getInventory(conduit); + } + + @Override + public void onNodeDirty() { + conduitBundle.hasDirtyNodes = true; + } + + @Override + public boolean isLoaded() { + return conduitBundle.level != null && conduitBundle.level.isLoaded(pos()) + && conduitBundle.level.shouldTickBlocksAt(pos()); + } + + @Override + public boolean hasRedstoneSignal(@Nullable DyeColor signalColor) { + return conduitBundle.hasRedstoneSignal(signalColor); + } + } + + public enum UpdateState { + NONE, NEXT_NEXT, NEXT, INITIALIZED; + + public boolean isInitialized() { + return this == INITIALIZED; + } + + public UpdateState next() { + return switch (this) { + case NONE, INITIALIZED -> NONE; + case NEXT_NEXT -> NEXT; + case NEXT -> INITIALIZED; + }; + } + + public UpdateState activate() { + return NEXT_NEXT; + } + } + + // region Legacy Bundle Conversion + + // TODO: Ender IO 8 - Remove. + + // Matches the same data format as the original conduit bundle. + // Enables us to convert between the new and old formats easily. + @SuppressWarnings("removal") + private record LegacyConduitBundle(BlockPos pos, List>> conduits, + Map connections, ItemStack facadeItem, + Map>, ConduitGraphObject> conduitNodes) { + + public static Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + BlockPos.CODEC.fieldOf("pos").forGetter(i -> i.pos), + Conduit.CODEC.listOf().fieldOf("conduits").forGetter(i -> i.conduits), + Codec.unboundedMap(Direction.CODEC, ConduitConnection.CODEC) + .fieldOf("connections") + .forGetter(i -> i.connections), + ItemStack.OPTIONAL_CODEC.optionalFieldOf("facade", ItemStack.EMPTY).forGetter(i -> i.facadeItem), + Codec.unboundedMap(Conduit.CODEC, ConduitGraphObject.CODEC) + .fieldOf("nodes") + .forGetter(i -> i.conduitNodes)) + .apply(instance, LegacyConduitBundle::new)); + + public static LegacyConduitBundle parse(HolderLookup.Provider lookupProvider, Tag tag) { + return CODEC.decode(lookupProvider.createSerializationContext(NbtOps.INSTANCE), tag) + .getOrThrow() + .getFirst(); + } + + public static final class ConduitConnection { + + public static Codec CODEC = ConnectionState.CODEC.listOf(0, MAX_CONDUITS) + .xmap(ConduitConnection::new, i -> Arrays.stream(i.connectionStates).toList()); + + private final ConnectionState[] connectionStates = Util.make(() -> { + var states = new ConnectionState[MAX_CONDUITS]; + Arrays.fill(states, StaticConnectionStates.DISCONNECTED); + return states; + }); + + private ConduitConnection(List connectionStates) { + if (connectionStates.size() > MAX_CONDUITS) { + throw new IllegalArgumentException( + "Cannot store more than " + MAX_CONDUITS + " conduit types per bundle."); + } + + for (var i = 0; i < connectionStates.size(); i++) { + this.connectionStates[i] = connectionStates.get(i); + } + } + + public ConnectionState getConnectionState(int index) { + return connectionStates[index]; + } + } + } + + @SuppressWarnings("removal") + private void loadFromLegacyBundle(LegacyConduitBundle bundle) { + // Copy the conduit list + conduits = new ArrayList<>(); + conduits.addAll(bundle.conduits); + + // Copy facade provider + facadeProvider = bundle.facadeItem.copy(); + + // Copy legacy connections into the new bundle + conduitConnections = new HashMap<>(); + for (var conduit : conduits) { + int conduitIndex = conduits.indexOf(conduit); + var connections = conduitConnections.computeIfAbsent(conduit, ConnectionContainer::new); + + for (Direction side : Direction.values()) { + var legacySide = bundle.connections.get(side); + + var state = legacySide.getConnectionState(conduitIndex); + + if (state == StaticConnectionStates.CONNECTED || state == StaticConnectionStates.CONNECTED_ACTIVE) { + connections.setStatus(side, ConnectionStatus.CONNECTED_CONDUIT); + } else if (state == StaticConnectionStates.DISCONNECTED) { + connections.setStatus(side, ConnectionStatus.DISCONNECTED); + } else if (state == StaticConnectionStates.DISABLED) { + connections.setStatus(side, ConnectionStatus.DISABLED); + } else if (state instanceof DynamicConnectionState dynamicState) { + connections.setStatus(side, ConnectionStatus.CONNECTED_BLOCK); + + connections.setConfig(side, + conduit.value() + .convertConnection(dynamicState.isInsert(), dynamicState.isExtract(), + dynamicState.insertChannel(), dynamicState.extractChannel(), + dynamicState.control(), dynamicState.redstoneChannel())); + + inventory.setStackInSlot(conduit, side, SlotType.FILTER_INSERT, dynamicState.filterInsert()); + inventory.setStackInSlot(conduit, side, SlotType.FILTER_EXTRACT, dynamicState.filterExtract()); + } + } + } + } + + // endregion +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleInventory.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleInventory.java new file mode 100644 index 0000000000..03feba42ac --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitBundleInventory.java @@ -0,0 +1,204 @@ +package com.enderio.conduits.common.conduit.bundle; + +import com.enderio.base.api.filter.ResourceFilter; +import com.enderio.base.common.init.EIOCapabilities; +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.bundle.ConduitBundleReader; +import com.enderio.conduits.api.bundle.ConduitInventory; +import com.enderio.conduits.api.bundle.SlotType; +import com.enderio.conduits.common.conduit.SlotData; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.common.util.INBTSerializable; +import net.neoforged.neoforge.items.IItemHandlerModifiable; + +public class ConduitBundleInventory implements IItemHandlerModifiable, INBTSerializable { + + // TODO: Currently conduit inventories are fairly strict - we might be able to + // improve this in the future? + + public static int MAX_SLOTS_PER_CONDUIT = 3; + public static int MAX_CONNECTIONS = Direction.values().length; // 6 + + private static final String CONDUIT_INV_KEY = "ConduitInv"; + + private final ConduitBundleReader conduitBundle; + + private Map>, Map>> conduitSlots = new HashMap<>(); + + public ConduitBundleInventory(ConduitBundleReader conduitBundle) { + this.conduitBundle = conduitBundle; + } + + protected void onChanged() { + } + + public ConduitInventory getInventoryFor(Holder> conduit) { + return new InventoryReference(this, conduit); + } + + public void removeConduit(Holder> conduit) { + conduitSlots.remove(conduit); + } + + @Override + public int getSlots() { + return MAX_SLOTS_PER_CONDUIT * ConduitBundleBlockEntity.MAX_CONDUITS * MAX_CONNECTIONS; + } + + @Override + public ItemStack getStackInSlot(int slot) { + if (slot < 0 || slot > getSlots()) { + throw new IndexOutOfBoundsException("Slot out of bounds"); + } + + SlotData slotData = SlotData.of(slot); + if (slotData.conduitIndex() >= conduitBundle.getConduits().size()) { + return ItemStack.EMPTY; + } + + return getStackInSlot(conduitBundle.getConduits().get(slotData.conduitIndex()), slotData.direction(), + slotData.slotType()); + } + + public ItemStack getStackInSlot(Holder> conduit, Direction side, SlotType slotType) { + return getStackInSlot(conduit, side, slotType.ordinal()); + } + + public ItemStack getStackInSlot(Holder> conduit, Direction side, int slot) { + if (!conduitBundle.hasConduitStrict(conduit)) { + throw new IllegalArgumentException("Conduit not found in bundle"); + } + + if (slot < 0 || slot >= MAX_SLOTS_PER_CONDUIT) { + throw new IndexOutOfBoundsException("Slot out of bounds"); + } + + var conduitSides = conduitSlots.computeIfAbsent(conduit, ignored -> new EnumMap<>(Direction.class)); + var slots = conduitSides.computeIfAbsent(side, + ignored -> NonNullList.withSize(MAX_SLOTS_PER_CONDUIT, ItemStack.EMPTY)); + + return slots.get(slot); + } + + public void setStackInSlot(Holder> conduit, Direction side, int slot, ItemStack stack) { + if (!conduitBundle.hasConduitStrict(conduit)) { + throw new IllegalArgumentException("Conduit not found in bundle"); + } + + if (slot < 0 || slot >= MAX_SLOTS_PER_CONDUIT) { + throw new IndexOutOfBoundsException("Slot out of bounds"); + } + + var conduitSides = conduitSlots.computeIfAbsent(conduit, ignored -> new EnumMap<>(Direction.class)); + var slots = conduitSides.computeIfAbsent(side, + ignored -> NonNullList.withSize(MAX_SLOTS_PER_CONDUIT, ItemStack.EMPTY)); + slots.set(slot, stack); + onChanged(); + } + + public void setStackInSlot(Holder> conduit, Direction side, SlotType slotType, ItemStack stack) { + setStackInSlot(conduit, side, slotType.ordinal(), stack); + } + + @Override + public void setStackInSlot(int slot, ItemStack stack) { + if (slot < 0 || slot > getSlots()) { + throw new IndexOutOfBoundsException("Slot out of bounds"); + } + + SlotData slotData = SlotData.of(slot); + if (slotData.conduitIndex() >= conduitBundle.getConduits().size()) { + return; + } + + setStackInSlot(conduitBundle.getConduits().get(slotData.conduitIndex()), slotData.direction(), + slotData.slotType(), stack); + } + + @Override + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { + return null; + } + + @Override + public ItemStack extractItem(int slot, int amount, boolean simulate) { + return null; + } + + @Override + public int getSlotLimit(int slot) { + return 1; + } + + @Override + public boolean isItemValid(int slot, ItemStack stack) { + if (slot >= getSlots()) { + return false; + } + + SlotData slotData = SlotData.of(slot); + if (slotData.conduitIndex() >= conduitBundle.getConduits().size()) { + return false; + } + + Holder> conduit = conduitBundle.getConduits().get(slotData.conduitIndex()); + + switch (slotData.slotType()) { + case FILTER_EXTRACT: + case FILTER_INSERT: + ResourceFilter resourceFilter = stack.getCapability(EIOCapabilities.Filter.ITEM); + if (resourceFilter == null) { + return false; + } + + return conduit.value().canApplyFilter(slotData.slotType(), resourceFilter); + case UPGRADE_EXTRACT: + // Upgrades have been removed + default: + return false; + } + } + + @Override + public CompoundTag serializeNBT(HolderLookup.Provider registries) { + CompoundTag tag = new CompoundTag(); + ListTag list = new ListTag(); + for (int i = 0; i < getSlots(); i++) { + ItemStack stack = getStackInSlot(i); + list.add(i, stack.saveOptional(registries)); + } + tag.put(CONDUIT_INV_KEY, list); + return tag; + } + + @Override + public void deserializeNBT(HolderLookup.Provider registries, CompoundTag tag) { + ListTag list = tag.getList(CONDUIT_INV_KEY, Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); i++) { + setStackInSlot(i, ItemStack.parseOptional(registries, list.getCompound(i))); + } + } + + private record InventoryReference(ConduitBundleInventory inventory, Holder> conduit) + implements ConduitInventory { + @Override + public ItemStack getStackInSlot(Direction side, SlotType slotType) { + return inventory.getStackInSlot(conduit, side, slotType); + } + + @Override + public void setStackInSlot(Direction side, SlotType slotType, ItemStack stack) { + inventory.setStackInSlot(conduit, side, slotType, stack); + } + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitShape.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitShape.java new file mode 100644 index 0000000000..7e1d633d1d --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/ConduitShape.java @@ -0,0 +1,236 @@ +package com.enderio.conduits.common.conduit.bundle; + +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.bundle.ConduitBundleReader; +import com.enderio.conduits.api.connection.ConnectionStatus; +import com.enderio.conduits.common.Area; +import com.enderio.conduits.common.conduit.OffsetHelper; +import com.mojang.datafixers.util.Pair; +import java.util.*; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.Vec3i; +import net.minecraft.util.Mth; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.BooleanOp; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.Nullable; + +public class ConduitShape { + + private final Map>>, VoxelShape> conduitConnections = new HashMap<>(); + private final Map>, VoxelShape> conduitShapes = new HashMap<>(); + + private final Map>, List> individualShapes = new HashMap<>(); + + private static final VoxelShape CONNECTOR = Block.box(2.5f, 2.5, 15f, 13.5f, 13.5f, 16f); + public static final VoxelShape CONNECTION = Block.box(6.5f, 6.5f, 9.5, 9.5f, 9.5f, 16); + private static final VoxelShape CORE = Block.box(6.5f, 6.5f, 6.5f, 9.5f, 9.5f, 9.5f); + private VoxelShape totalShape = CORE; + + public ConduitShape() { + + } + + public void updateConduit(ConduitBundleReader bundle) { + this.conduitShapes.clear(); + this.conduitConnections.clear(); + this.individualShapes.clear(); + for (Holder> conduit : bundle.getConduits()) { + updateShapeForConduit(bundle, conduit); + } + updateTotalShape(); + } + + // TODO: Looks weird when the connecting boxes arrive, but this at least now + // matches 1.12 behaviour. + public VoxelShape getShapeFromHit(BlockPos pos, HitResult result) { + var aimedConduit = getConduit(pos, result); + + if (aimedConduit == null || !individualShapes.containsKey(aimedConduit)) { + return Shapes.empty(); + } + + Vec3 vec3 = result.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ()); + + for (var shape : individualShapes.get(aimedConduit)) { + Optional point = shape.closestPointTo(vec3); + if (point.isEmpty()) { + continue; + } + + if (point.get().closerThan(vec3, Mth.EPSILON)) { // can't be 0 due to double + return shape; + } + } + + return Shapes.empty(); + } + + @Nullable + public Holder> getConduit(BlockPos pos, HitResult result) { + return getLookUpValue(conduitShapes, pos, result); + } + + @Nullable + public Pair>> getConnectionFromHit(BlockPos pos, HitResult hit) { + return getLookUpValue(conduitConnections, pos, hit); + } + + @Nullable + private T getLookUpValue(Map shapes, BlockPos pos, HitResult result) { + Vec3 vec3 = result.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ()); + for (Map.Entry entry : shapes.entrySet()) { + Optional point = entry.getValue().closestPointTo(vec3); + if (point.isEmpty()) { + continue; + } + + if (point.get().closerThan(vec3, Mth.EPSILON)) { // can't be 0 due to double + return entry.getKey(); + } + } + + return null; + } + + private void updateTotalShape() { + this.totalShape = Shapes.empty(); + this.conduitShapes.values() + .forEach(s -> this.totalShape = Shapes.joinUnoptimized(this.totalShape, s, BooleanOp.OR)); + totalShape.optimize(); + } + + public VoxelShape getTotalShape() { + return this.totalShape; + } + + private void updateShapeForConduit(ConduitBundleReader conduitBundle, Holder> conduit) { + List individualShapeList = individualShapes.computeIfAbsent(conduit, ignored -> new ArrayList<>()); + + VoxelShape conduitShape = Shapes.empty(); + Direction.Axis axis = OffsetHelper.findMainAxis(conduitBundle); + Map>, List> offsets = new HashMap<>(); + for (Direction direction : Direction.values()) { + VoxelShape conduitConnectionShape = Shapes.empty(); + + // TODO: Lift the connector plate out of updateShapeForConduit? + if (conduitBundle.getConnectionStatus(direction, conduit) == ConnectionStatus.CONNECTED_BLOCK) { + VoxelShape connectorShape = rotateVoxelShape(CONNECTOR, direction); + conduitShape = Shapes.joinUnoptimized(conduitShape, connectorShape, BooleanOp.OR); + conduitConnectionShape = Shapes.joinUnoptimized(connectorShape, connectorShape, BooleanOp.OR); + + individualShapeList.add(connectorShape); + } + + var connectedTypes = conduitBundle.getConnectedConduits(direction); + if (connectedTypes.contains(conduit)) { + Vec3i offset = OffsetHelper.translationFor(direction.getAxis(), + OffsetHelper.offsetConduit(connectedTypes.indexOf(conduit), connectedTypes.size())); + offsets.computeIfAbsent(conduit, ignored -> new ArrayList<>()).add(offset); + VoxelShape connectionShape = rotateVoxelShape(CONNECTION, direction).move(offset.getX() * 3f / 16f, + offset.getY() * 3f / 16f, offset.getZ() * 3f / 16f); + conduitShape = Shapes.joinUnoptimized(conduitShape, connectionShape, BooleanOp.OR); + + conduitConnectionShape = Shapes.joinUnoptimized(conduitConnectionShape, connectionShape, BooleanOp.OR); + + individualShapeList.add(connectionShape); + } + + conduitConnections.put(new Pair<>(direction, conduit), conduitConnectionShape.optimize()); + } + + var allConduits = conduitBundle.getConduits(); + @Nullable + Area box = null; + @Nullable + Holder> notRendered = null; + int i = allConduits.indexOf(conduit); + if (i == -1) { + conduitShapes.put(conduit, Shapes.block()); + return; + } + + @Nullable + List offsetsForConduit = offsets.get(conduit); + if (offsetsForConduit != null) { + // all are pointing to the same xyz reference meaning that we can draw the core + if (offsetsForConduit.stream().distinct().count() != 1) { + box = new Area(offsetsForConduit.toArray(new Vec3i[0])); + } + } else { + notRendered = conduit; + } + + VoxelShape coreShape = Shapes.empty(); + + if (offsetsForConduit != null && (box == null || !box.contains(offsetsForConduit.get(0)))) { + coreShape = Shapes.joinUnoptimized( + coreShape, CORE.move(offsetsForConduit.get(0).getX() * 3f / 16f, + offsetsForConduit.get(0).getY() * 3f / 16f, offsetsForConduit.get(0).getZ() * 3f / 16f), + BooleanOp.OR); + } + + if (box != null) { + if (notRendered != null) { + Vec3i offset = OffsetHelper.translationFor(axis, OffsetHelper.offsetConduit(i, allConduits.size())); + if (!box.contains(offset)) { + coreShape = Shapes.joinUnoptimized(coreShape, + CORE.move(offset.getX() * 3f / 16f, offset.getY() * 3f / 16f, offset.getZ() * 3f / 16f), + BooleanOp.OR); + } + } + + coreShape = Shapes.joinUnoptimized(coreShape, CORE.move(box.getMin().getX() * 3f / 16f, + box.getMin().getY() * 3f / 16f, box.getMin().getZ() * 3f / 16f), BooleanOp.OR); + } else { + if (notRendered != null) { + Vec3i offset = OffsetHelper.translationFor(axis, OffsetHelper.offsetConduit(i, allConduits.size())); + coreShape = Shapes.joinUnoptimized(coreShape, + CORE.move(offset.getX() * 3f / 16f, offset.getY() * 3f / 16f, offset.getZ() * 3f / 16f), + BooleanOp.OR); + } + } + + conduitShape = Shapes.joinUnoptimized(conduitShape, coreShape, BooleanOp.OR); + + conduitShapes.put(conduit, conduitShape.optimize()); + individualShapeList.add(coreShape.optimize()); + } + + /** + * Rotates a VoxelShape around the center to the specified Direction, Origin is SOUTH + * + * @param toRotate + * @param direction + * @return the rotated VoxelShape + */ + public static VoxelShape rotateVoxelShape(VoxelShape toRotate, Direction direction) { + VoxelShape[] buffer = new VoxelShape[] { toRotate, Shapes.empty() }; + if (direction.get2DDataValue() == -1) { + if (direction == Direction.DOWN) { + buffer[0].forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> buffer[1] = Shapes.or(buffer[1], + Shapes.box(minX, 1 - maxZ, minY, maxX, 1 - minZ, maxY))); + } else { + buffer[0].forAllBoxes((minX, minY, minZ, maxX, maxY, + maxZ) -> buffer[1] = Shapes.or(buffer[1], Shapes.box(minX, minZ, minY, maxX, maxZ, maxY))); + } + + return buffer[1]; + } + + for (int i = 0; i < (direction.get2DDataValue()) % 4; i++) { + buffer[0].forAllBoxes((minX, minY, minZ, maxX, maxY, + maxZ) -> buffer[1] = Shapes.or(buffer[1], Shapes.box(1 - maxZ, minY, minX, 1 - minZ, maxY, maxX))); + buffer[0] = buffer[1]; + buffer[1] = Shapes.empty(); + } + + return buffer[0]; + } + +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/package-info.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/package-info.java new file mode 100644 index 0000000000..a0fc67d334 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/bundle/package-info.java @@ -0,0 +1,4 @@ +@javax.annotation.ParametersAreNonnullByDefault +@net.minecraft.MethodsReturnNonnullByDefault + +package com.enderio.conduits.common.conduit.bundle; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/DynamicConnectionState.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/DynamicConnectionState.java deleted file mode 100644 index 5c40d8a323..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/DynamicConnectionState.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.enderio.conduits.common.conduit.connection; - -import com.enderio.base.api.UseOnly; -import com.enderio.base.api.network.MassiveStreamCodec; -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.SlotType; -import com.enderio.base.api.misc.RedstoneControl; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.world.item.DyeColor; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.neoforged.fml.LogicalSide; - -import java.util.HashMap; -import java.util.Map; - -public record DynamicConnectionState( - boolean isInsert, - DyeColor insertChannel, - boolean isExtract, - DyeColor extractChannel, - RedstoneControl control, - DyeColor redstoneChannel, - @UseOnly(LogicalSide.SERVER) ItemStack filterInsert, - @UseOnly(LogicalSide.SERVER) ItemStack filterExtract, - @UseOnly(LogicalSide.SERVER) ItemStack upgradeExtract -) implements ConnectionState { - - public static Codec CODEC = RecordCodecBuilder.create( - instance -> instance.group( - Codec.BOOL.fieldOf("is_insert").forGetter(DynamicConnectionState::isInsert), - DyeColor.CODEC.fieldOf("insert_channel").forGetter(DynamicConnectionState::insertChannel), - Codec.BOOL.fieldOf("is_extract").forGetter(DynamicConnectionState::isExtract), - DyeColor.CODEC.fieldOf("extract_channel").forGetter(DynamicConnectionState::extractChannel), - RedstoneControl.CODEC.fieldOf("redstone_control").forGetter(DynamicConnectionState::control), - DyeColor.CODEC.fieldOf("redstone_channel").forGetter(DynamicConnectionState::redstoneChannel), - ItemStack.OPTIONAL_CODEC.fieldOf("filter_insert").forGetter(DynamicConnectionState::filterInsert), - ItemStack.OPTIONAL_CODEC.fieldOf("filter_extract").forGetter(DynamicConnectionState::filterExtract), - ItemStack.OPTIONAL_CODEC.fieldOf("upgrade_extract").forGetter(DynamicConnectionState::upgradeExtract) - ).apply(instance, DynamicConnectionState::new) - ); - - public static StreamCodec STREAM_CODEC = MassiveStreamCodec.composite( - ByteBufCodecs.BOOL, - DynamicConnectionState::isInsert, - DyeColor.STREAM_CODEC, - DynamicConnectionState::insertChannel, - ByteBufCodecs.BOOL, - DynamicConnectionState::isExtract, - DyeColor.STREAM_CODEC, - DynamicConnectionState::extractChannel, - RedstoneControl.STREAM_CODEC, - DynamicConnectionState::control, - DyeColor.STREAM_CODEC, - DynamicConnectionState::redstoneChannel, - ItemStack.OPTIONAL_STREAM_CODEC, - DynamicConnectionState::filterInsert, - ItemStack.OPTIONAL_STREAM_CODEC, - DynamicConnectionState::filterExtract, - ItemStack.OPTIONAL_STREAM_CODEC, - DynamicConnectionState::upgradeExtract, - DynamicConnectionState::new - ); - - public static DynamicConnectionState defaultConnection(Level level, BlockPos pos, Direction direction, Holder> type) { - Conduit.ConduitConnectionData defaultConnection = type.value().getDefaultConnection(level, pos, direction); - return new DynamicConnectionState(defaultConnection.isInsert(), DyeColor.GREEN, defaultConnection.isExtract(), DyeColor.GREEN, defaultConnection.control(), DyeColor.RED, ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY); - } - - @Override - public boolean isConnection() { - return true; - } - - public ItemStack getItem(SlotType slotType) { - if (slotType == SlotType.FILTER_EXTRACT) { - return filterExtract; - } - - if (slotType == SlotType.FILTER_INSERT) { - return filterInsert; - } - - return upgradeExtract; - } - - public DynamicConnectionState withItem(SlotType type, ItemStack stack) { - Map items = new HashMap<>(); - for (SlotType type1: SlotType.values()) { - items.put(type1, type1 == type ? stack: getItem(type1)); - } - return new DynamicConnectionState(isInsert, insertChannel, isExtract, extractChannel, control, redstoneChannel, items.get(SlotType.FILTER_INSERT), items.get(SlotType.FILTER_EXTRACT), items.get(SlotType.UPGRADE_EXTRACT)); - } - public DynamicConnectionState withEnabled(boolean forExtract, boolean value) { - return new DynamicConnectionState(!forExtract ? value : isInsert, insertChannel, forExtract ? value : isExtract, extractChannel, control, redstoneChannel, filterInsert, filterExtract, upgradeExtract); - } - - public DynamicConnectionState withColor(boolean forExtract, DyeColor value) { - return new DynamicConnectionState(isInsert, !forExtract ? value : insertChannel, isExtract, forExtract ? value : extractChannel, control, redstoneChannel, filterInsert, filterExtract, upgradeExtract); - } - public DynamicConnectionState withRedstoneMode(RedstoneControl value) { - return new DynamicConnectionState(isInsert, insertChannel, isExtract, extractChannel, value, redstoneChannel, filterInsert, filterExtract, upgradeExtract); - } - public DynamicConnectionState withRedstoneChannel(DyeColor value) { - return new DynamicConnectionState(isInsert, insertChannel, isExtract, extractChannel, control, value, filterInsert, filterExtract, upgradeExtract); - } - - public boolean isEmpty() { - return !isInsert && !isExtract; - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/package-info.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/package-info.java deleted file mode 100644 index b8891fb1ae..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@javax.annotation.ParametersAreNonnullByDefault -@net.minecraft.MethodsReturnNonnullByDefault - -package com.enderio.conduits.common.conduit.connection; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/facades/ConduitFacadeItem.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/facades/ConduitFacadeItem.java index d1df9b1cc1..bdf49fe801 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/facades/ConduitFacadeItem.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/facades/ConduitFacadeItem.java @@ -2,16 +2,68 @@ import com.enderio.base.common.lang.EIOLang; import com.enderio.conduits.api.ConduitCapabilities; +import com.enderio.conduits.common.init.ConduitBlocks; import com.enderio.conduits.common.init.ConduitLang; import java.util.List; +import java.util.Map; +import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; -public class ConduitFacadeItem extends Item { +public class ConduitFacadeItem extends BlockItem { public ConduitFacadeItem(Properties properties) { - super(properties); + super(ConduitBlocks.CONDUIT.get(), properties); + } + + @Override + public InteractionResult place(BlockPlaceContext context) { + Level level = context.getLevel(); + @Nullable + Player player = context.getPlayer(); + BlockPos blockpos = context.getClickedPos(); + + // Allow placing from the edge of an adjacent block + BlockState blockState = level.getBlockState(blockpos); + if (!blockState.canBeReplaced()) { + // noinspection DataFlowIssue + return blockState + .useItemOn(context.getItemInHand(), level, player, context.getHand(), + context.getHitResult().withPosition(blockpos)) + .result(); + } + + return super.place(context); + } + + @Override + protected boolean canPlace(BlockPlaceContext context, BlockState state) { + // Must have a valid facade + var facade = context.getItemInHand().getCapability(ConduitCapabilities.CONDUIT_FACADE_PROVIDER); + if (facade == null || !facade.isValid()) { + return false; + } + + return super.canPlace(context, state); + } + + @Override + public void registerBlocks(Map blockToItemMap, Item item) { + // Do not register + } + + @Override + public String getDescriptionId() { + return this.getOrCreateDescriptionId(); } @Override diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java new file mode 100644 index 0000000000..1bae7b0ca1 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitConnectionHost.java @@ -0,0 +1,31 @@ +package com.enderio.conduits.common.conduit.graph; + +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.bundle.ConduitInventory; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.world.item.DyeColor; +import org.jetbrains.annotations.Nullable; + +// TODO: Did the interface for now but honestly maybe this should just be an object because its not API public. +public interface ConduitConnectionHost { + Holder> conduit(); + + BlockPos pos(); + + boolean isConnectedTo(Direction side); + + ConnectionConfig getConnectionConfig(Direction side); + + void setConnectionConfig(Direction side, ConnectionConfig connectionConfig); + + ConduitInventory inventory(); + + void onNodeDirty(); + + boolean isLoaded(); + + boolean hasRedstoneSignal(@Nullable DyeColor signalColor); +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitDataContainer.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitDataContainer.java similarity index 61% rename from enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitDataContainer.java rename to enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitDataContainer.java index cd06064620..5adcab09e9 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitDataContainer.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitDataContainer.java @@ -1,9 +1,11 @@ -package com.enderio.conduits.common.conduit; +package com.enderio.conduits.common.conduit.graph; -import com.enderio.conduits.api.ConduitDataAccessor; -import com.enderio.conduits.api.ConduitDataType; -import com.enderio.conduits.api.ConduitData; +import com.enderio.conduits.api.network.node.legacy.ConduitData; +import com.enderio.conduits.api.network.node.legacy.ConduitDataAccessor; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; import com.mojang.serialization.Codec; +import java.util.Objects; +import java.util.Optional; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; @@ -13,19 +15,18 @@ import net.minecraft.util.ExtraCodecs; import org.jetbrains.annotations.Nullable; -import java.util.Objects; -import java.util.Optional; - /** * A safe way to store conduit data. */ +@Deprecated(forRemoval = true, since = "7.2") public class ConduitDataContainer implements ConduitDataAccessor { public static Codec CODEC = ExtraCodecs.optionalEmptyMap(ConduitData.CODEC) - .xmap(ConduitDataContainer::new, i -> Optional.ofNullable(i.data)); + .xmap(ConduitDataContainer::new, i -> Optional.ofNullable(i.data)); - public static StreamCodec STREAM_CODEC = ByteBufCodecs.optional(ConduitData.STREAM_CODEC) - .map(ConduitDataContainer::new, i -> Optional.ofNullable(i.data)); + public static StreamCodec STREAM_CODEC = ByteBufCodecs + .optional(ConduitData.STREAM_CODEC) + .map(ConduitDataContainer::new, i -> Optional.ofNullable(i.data)); @Nullable private ConduitData data; @@ -42,15 +43,24 @@ private ConduitDataContainer(Optional> data) { this.data = data.orElse(null); } + public boolean hasData() { + return data != null; + } + public boolean hasData(ConduitDataType type) { return data != null && data.type() == type; } + @Nullable + public ConduitData getData() { + return data; + } + @Nullable @SuppressWarnings("unchecked") public > T getData(ConduitDataType type) { if (data != null && type == data.type()) { - return (T)data; + return (T) data; } return null; @@ -59,37 +69,11 @@ public > T getData(ConduitDataType type) { @SuppressWarnings("unchecked") public > T getOrCreateData(ConduitDataType type) { if (data != null && type == data.type()) { - return (T)data; + return (T) data; } data = type.factory().get(); - return (T)data; - } - - public void handleClientChanges(ConduitDataContainer clientDataContainer) { - // Ensure the type we contain matches the client. - // If it does not, assume the client is out of date and ignore it. - if (data != null && !clientDataContainer.hasData(data.type())) { - return; - } - - if (data != null) { - data = applyClientChanges(data.type(), clientDataContainer); - } else { - data = applyClientChanges(clientDataContainer.data.type(), clientDataContainer); - } - } - - @SuppressWarnings("unchecked") - private > T applyClientChanges(ConduitDataType type, ConduitDataContainer clientDataContainer) { - T myData = getOrCreateData(type); - T clientData = clientDataContainer.getData(type); - - if (clientData == null) { - return myData; - } - - return myData.withClientChanges(clientData); + return (T) data; } public Tag save(HolderLookup.Provider lookupProvider) { diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitGraphContext.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java similarity index 74% rename from enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitGraphContext.java rename to enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java index 141a5d3c22..9763451485 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/ConduitGraphContext.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphContext.java @@ -1,11 +1,12 @@ -package com.enderio.conduits.common.conduit; +package com.enderio.conduits.common.conduit.graph; -import com.enderio.conduits.api.EnderIOConduitsRegistries; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitNetworkContext; -import com.enderio.conduits.api.ConduitNetworkContextAccessor; -import com.enderio.conduits.api.ConduitNetworkContextType; +import com.enderio.conduits.api.EnderIOConduitsRegistries; +import com.enderio.conduits.api.network.ConduitNetworkContext; +import com.enderio.conduits.api.network.ConduitNetworkContextAccessor; +import com.enderio.conduits.api.network.ConduitNetworkContextType; import dev.gigaherz.graph3.Mergeable; +import java.util.Objects; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; @@ -14,8 +15,6 @@ import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.Nullable; -import java.util.Objects; - public class ConduitGraphContext implements Mergeable, ConduitNetworkContextAccessor { @Nullable @@ -37,7 +36,7 @@ public boolean hasContext(ConduitNetworkContextType type) { @Override public > @Nullable T getContext(ConduitNetworkContextType type) { if (context != null && context.type() == type) { - return (T)context; + return (T) context; } return null; @@ -47,11 +46,11 @@ public boolean hasContext(ConduitNetworkContextType type) { @Override public > T getOrCreateContext(ConduitNetworkContextType type) { if (context != null && context.type() == type) { - return (T)context; + return (T) context; } context = type.factory().get(); - return (T)context; + return (T) context; } // region Mergable Implementation @@ -80,9 +79,9 @@ public ConduitGraphContext copy() { // endregion - private > Z castContext(){ - //noinspection unchecked - return (Z)context; + private > Z castContext() { + // noinspection unchecked + return (Z) context; } @Nullable @@ -95,7 +94,9 @@ public CompoundTag save(HolderLookup.Provider lookupProvider) { return null; } - var contextTypeKey = Objects.requireNonNull(EnderIOConduitsRegistries.CONDUIT_NETWORK_CONTEXT_TYPE.getKey(context.type()), "Context type is not registered!"); + var contextTypeKey = Objects.requireNonNull( + EnderIOConduitsRegistries.CONDUIT_NETWORK_CONTEXT_TYPE.getKey(context.type()), + "Context type is not registered!"); var tag = new CompoundTag(); tag.putString("Type", contextTypeKey.toString()); @@ -105,8 +106,11 @@ public CompoundTag save(HolderLookup.Provider lookupProvider) { // Gross. @SuppressWarnings("unchecked") - private > Tag encodeContext(HolderLookup.Provider lookupProvider, ConduitNetworkContextType type) { - return type.codec().encodeStart(lookupProvider.createSerializationContext(NbtOps.INSTANCE), (T)context).getOrThrow(); + private > Tag encodeContext(HolderLookup.Provider lookupProvider, + ConduitNetworkContextType type) { + return type.codec() + .encodeStart(lookupProvider.createSerializationContext(NbtOps.INSTANCE), (T) context) + .getOrThrow(); } public static > ConduitGraphContext of(T context) { @@ -117,22 +121,26 @@ public static ConduitGraphContext createNetworkContext() { return new ConduitGraphContext(); } - public static ConduitGraphContext loadNetworkContext(Holder> conduit, HolderLookup.Provider lookupProvider, CompoundTag contextTag) { + public static ConduitGraphContext loadNetworkContext(Holder> conduit, + HolderLookup.Provider lookupProvider, CompoundTag contextTag) { ConduitNetworkContext context = loadNetworkContext(conduit.value(), lookupProvider, contextTag); return new ConduitGraphContext(context); } @Nullable - private static ConduitNetworkContext loadNetworkContext(Conduit conduit, HolderLookup.Provider lookupProvider, CompoundTag contextTag) { + private static ConduitNetworkContext loadNetworkContext(Conduit conduit, + HolderLookup.Provider lookupProvider, CompoundTag contextTag) { ResourceLocation serializerKey = ResourceLocation.parse(contextTag.getString("Type")); - ConduitNetworkContextType contextType = Objects.requireNonNull(EnderIOConduitsRegistries.CONDUIT_NETWORK_CONTEXT_TYPE.get(serializerKey), - "Unable to find conduit network context type with key " + serializerKey); + ConduitNetworkContextType contextType = Objects.requireNonNull( + EnderIOConduitsRegistries.CONDUIT_NETWORK_CONTEXT_TYPE.get(serializerKey), + "Unable to find conduit network context type with key " + serializerKey); if (contextType.codec() == null) { return null; } - // TODO: We're using getOrThrow a lot for conduits. Should definitely make more robust/flexible. + // TODO: We're using getOrThrow a lot for conduits. Should definitely make more + // robust/flexible. CompoundTag data = contextTag.getCompound("Data"); return contextType.codec().parse(lookupProvider.createSerializationContext(NbtOps.INSTANCE), data).getOrThrow(); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java new file mode 100644 index 0000000000..27d2cf4ef9 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphObject.java @@ -0,0 +1,285 @@ +package com.enderio.conduits.common.conduit.graph; + +import com.enderio.base.api.filter.ResourceFilter; +import com.enderio.base.common.init.EIOCapabilities; +import com.enderio.conduits.api.bundle.SlotType; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; +import com.enderio.conduits.api.network.node.NodeData; +import com.enderio.conduits.api.network.node.NodeDataType; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dev.gigaherz.graph3.Graph; +import dev.gigaherz.graph3.GraphObject; +import java.util.Optional; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; +import net.minecraft.world.item.DyeColor; +import org.jetbrains.annotations.Nullable; + +public class ConduitGraphObject implements GraphObject, ConduitNode { + + private static final Codec LEGACY_CODEC = RecordCodecBuilder.create(instance -> instance + .group(BlockPos.CODEC.fieldOf("pos").forGetter(ConduitGraphObject::getPos), + ConduitDataContainer.CODEC.fieldOf("data").forGetter(i -> i.legacyDataContainer)) + .apply(instance, ConduitGraphObject::new)); + + private static final Codec NEW_CODEC = RecordCodecBuilder + .create(instance -> instance + .group(BlockPos.CODEC.fieldOf("pos").forGetter(ConduitGraphObject::getPos), + NodeData.GENERIC_CODEC.optionalFieldOf("node_data") + .forGetter(i -> Optional.ofNullable(i.nodeData))) + .apply(instance, ConduitGraphObject::new)); + + public static final Codec CODEC = Codec.withAlternative(NEW_CODEC, LEGACY_CODEC); + + private BlockPos pos; + + @Nullable + private Graph graph = null; + + @Nullable + private WrappedConduitNetwork wrappedGraph = null; + + @Nullable + private ConduitDataContainer legacyDataContainer = null; + + @Nullable + private NodeData nodeData; + + // TODO: Instead of a special construct, we could just pass the type and bundle + // in? + @Nullable + private ConduitConnectionHost connectionHost; + + public ConduitGraphObject(BlockPos pos) { + this.pos = pos; + this.nodeData = null; + } + + public ConduitGraphObject(BlockPos pos, ConduitDataContainer conduitDataContainer) { + this.pos = pos; + + // Convert the old data + this.legacyDataContainer = conduitDataContainer; + var oldData = legacyDataContainer.getData(); + if (oldData != null) { + this.nodeData = oldData.toNodeData(); + } else { + this.nodeData = null; + } + } + + public ConduitGraphObject(BlockPos pos, @Nullable NodeData nodeData) { + this.pos = pos; + this.nodeData = nodeData; + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + private ConduitGraphObject(BlockPos pos, Optional nodeData) { + this.pos = pos; + this.nodeData = nodeData.orElse(null); + } + + @Nullable + @Override + public Graph getGraph() { + return graph; + } + + @Override + public void setGraph(@Nullable Graph graph) { + this.graph = graph; + this.wrappedGraph = graph == null ? null : new WrappedConduitNetwork(graph); + upgradeLegacyData(); + } + + @Nullable + @Override + public ConduitNetwork getNetwork() { + return wrappedGraph; + } + + public void attach(ConduitConnectionHost connectionHost) { + if (!connectionHost.pos().equals(pos)) { + throw new IllegalArgumentException("Connection host and node position do not match!"); + } + + this.connectionHost = connectionHost; + upgradeLegacyData(); + } + + public void detach() { + this.connectionHost = null; + } + + public BlockPos getPos() { + return pos; + } + + // TODO: Remove in EnderIO 8. + // Convert old conduit data to the new formats. + private void upgradeLegacyData() { + if (graph == null || connectionHost == null) { + return; + } + + if (legacyDataContainer == null || !legacyDataContainer.hasData()) { + return; + } + + // Upgrade with old data + // noinspection deprecation + connectionHost.conduit().value().copyLegacyData(this, legacyDataContainer); + legacyDataContainer = null; + } + + // region Node Data + + @Override + public boolean hasNodeData(NodeDataType type) { + return nodeData != null && nodeData.type() == type; + } + + @Override + public @Nullable NodeData getNodeData() { + return nodeData; + } + + @Override + public @Nullable T getNodeData(NodeDataType type) { + if (nodeData != null && type == nodeData.type()) { + // noinspection unchecked + return (T) nodeData; + } + + return null; + } + + @Override + public T getOrCreateNodeData(NodeDataType type) { + if (nodeData != null && type == nodeData.type()) { + // noinspection unchecked + return (T) nodeData; + } + + nodeData = type.factory().get(); + // noinspection unchecked + return (T) nodeData; + } + + @Override + public void setNodeData(@Nullable T data) { + nodeData = data; + } + + // endregion + + // region Connection Config + + @Override + public boolean isConnectedTo(Direction side) { + if (connectionHost == null) { + throw new IllegalStateException("No connection host!"); + } + + return connectionHost.isConnectedTo(side); + } + + @Override + public ConnectionConfig getConnectionConfig(Direction side) { + if (connectionHost == null) { + throw new IllegalStateException("No connection host!"); + } + + return connectionHost.getConnectionConfig(side); + } + + @Override + public T getConnectionConfig(Direction side, ConnectionConfigType type) { + var config = getConnectionConfig(side); + + if (config.type() != type) { + throw new IllegalStateException( + "Connection config type mismatch! Conversion failed somewhere in the bundle."); + } + + // noinspection unchecked + return (T) config; + } + + @Override + public void setConnectionConfig(Direction side, ConnectionConfig config) { + if (connectionHost == null) { + throw new IllegalStateException("No connection host!"); + } + + if (config.type() != connectionHost.getConnectionConfig(side).type()) { + throw new IllegalArgumentException("Connection config type mismatch!"); + } + + connectionHost.setConnectionConfig(side, config); + } + + // endregion + + @Override + public @Nullable ResourceFilter getExtractFilter(Direction direction) { + if (connectionHost == null) { + throw new IllegalStateException("No connection host!"); + } + + return connectionHost.inventory() + .getStackInSlot(direction, SlotType.FILTER_EXTRACT) + .getCapability(EIOCapabilities.Filter.ITEM); + } + + @Override + public @Nullable ResourceFilter getInsertFilter(Direction direction) { + if (connectionHost == null) { + throw new IllegalStateException("No connection host!"); + } + + return connectionHost.inventory() + .getStackInSlot(direction, SlotType.FILTER_INSERT) + .getCapability(EIOCapabilities.Filter.ITEM); + } + + @Override + public boolean isLoaded() { + if (connectionHost == null) { + return false; + } + + return connectionHost.isLoaded(); + } + + @Override + public boolean hasRedstoneSignal(@Nullable DyeColor signalColor) { + if (!isLoaded()) { + throw new IllegalStateException("Unable to query redstone signals when the node is not loaded."); + } + + return connectionHost.hasRedstoneSignal(signalColor); + } + + @Override + public void markDirty() { + if (connectionHost != null) { + connectionHost.onNodeDirty(); + } + } + + public Tag save(HolderLookup.Provider lookupProvider) { + return CODEC.encodeStart(lookupProvider.createSerializationContext(NbtOps.INSTANCE), this).getOrThrow(); + } + + public static ConduitGraphObject parse(HolderLookup.Provider lookupProvider, Tag tag) { + return CODEC.decode(lookupProvider.createSerializationContext(NbtOps.INSTANCE), tag).getOrThrow().getFirst(); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java new file mode 100644 index 0000000000..345ec31b1c --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/ConduitGraphUtility.java @@ -0,0 +1,30 @@ +package com.enderio.conduits.common.conduit.graph; + +import com.enderio.conduits.api.Conduit; +import dev.gigaherz.graph3.Graph; +import dev.gigaherz.graph3.GraphObject; +import java.util.List; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; + +public class ConduitGraphUtility { + + public static void integrate(Holder> conduit, GraphObject graphObject, + List> neighbours) { + Graph.integrate(graphObject, neighbours, Graph::new, g -> ConduitGraphContext.createNetworkContext()); + } + + public static void integrateWithLoad(Holder> conduit, GraphObject graphObject, + List> neighbours, HolderLookup.Provider lookupProvider, + CompoundTag contextTag) { + Graph.integrate(graphObject, neighbours, Graph::new, + g -> ConduitGraphContext.loadNetworkContext(conduit, lookupProvider, contextTag)); + } + + public static void connect(Holder> conduit, GraphObject graphObject, + GraphObject neighbour) { + Graph.connect(graphObject, neighbour, Graph::new, g -> ConduitGraphContext.createNetworkContext()); + } + +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/WrappedConduitNetwork.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java similarity index 67% rename from enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/WrappedConduitNetwork.java rename to enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java index 9a2f98ef8a..40f677062b 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/WrappedConduitNetwork.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/WrappedConduitNetwork.java @@ -1,13 +1,12 @@ -package com.enderio.conduits.common.conduit; +package com.enderio.conduits.common.conduit.graph; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNetworkContext; -import com.enderio.conduits.api.ConduitNetworkContextType; -import com.enderio.conduits.api.ConduitNode; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.ConduitNetworkContext; +import com.enderio.conduits.api.network.ConduitNetworkContextType; +import com.enderio.conduits.api.network.node.ConduitNode; import dev.gigaherz.graph3.Graph; -import org.jetbrains.annotations.Nullable; - import java.util.Collection; +import org.jetbrains.annotations.Nullable; /** * Wrap the graph for public API consumption. @@ -16,10 +15,8 @@ public record WrappedConduitNetwork(Graph graph) implements @Override public Collection getNodes() { - //noinspection unchecked - return graph.getObjects().stream() - .map(object -> (ConduitNode) object) - .toList(); + // noinspection unchecked + return graph.getObjects().stream().map(object -> (ConduitNode) object).toList(); } @Override diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/block/package-info.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/package-info.java similarity index 64% rename from enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/block/package-info.java rename to enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/package-info.java index 392d8ccb0b..a19f14d171 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/block/package-info.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/graph/package-info.java @@ -1,4 +1,4 @@ @javax.annotation.ParametersAreNonnullByDefault @net.minecraft.MethodsReturnNonnullByDefault -package com.enderio.conduits.common.conduit.block; +package com.enderio.conduits.common.conduit.graph; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/ConnectionState.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java similarity index 81% rename from enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/ConnectionState.java rename to enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java index 63bdaa03de..5790d444ec 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/ConnectionState.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/ConnectionState.java @@ -1,4 +1,4 @@ -package com.enderio.conduits.common.conduit.connection; +package com.enderio.conduits.common.conduit.legacy; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; @@ -7,10 +7,10 @@ import net.minecraft.network.codec.StreamCodec; import org.apache.commons.lang3.NotImplementedException; +@Deprecated(forRemoval = true, since = "7.2") public sealed interface ConnectionState permits StaticConnectionStates, DynamicConnectionState { - Codec CODEC = - Codec.either(StaticConnectionStates.CODEC, DynamicConnectionState.CODEC) + Codec CODEC = Codec.either(StaticConnectionStates.CODEC, DynamicConnectionState.CODEC) .xmap(e -> e.left().isPresent() ? e.left().get() : e.right().get(), e -> { if (e instanceof StaticConnectionStates staticConnectionStates) { return Either.left(staticConnectionStates); @@ -21,8 +21,8 @@ public sealed interface ConnectionState permits StaticConnectionStates, DynamicC throw new NotImplementedException(); }); - StreamCodec STREAM_CODEC = - ByteBufCodecs.either(StaticConnectionStates.STREAM_CODEC, DynamicConnectionState.STREAM_CODEC) + StreamCodec STREAM_CODEC = ByteBufCodecs + .either(StaticConnectionStates.STREAM_CODEC, DynamicConnectionState.STREAM_CODEC) .map(e -> e.left().isPresent() ? e.left().get() : e.right().get(), e -> { if (e instanceof StaticConnectionStates staticConnectionStates) { return Either.left(staticConnectionStates); diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java new file mode 100644 index 0000000000..4cc147df9d --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/DynamicConnectionState.java @@ -0,0 +1,116 @@ +package com.enderio.conduits.common.conduit.legacy; + +import com.enderio.base.api.UseOnly; +import com.enderio.base.api.misc.RedstoneControl; +import com.enderio.base.api.network.MassiveStreamCodec; +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.bundle.SlotType; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.neoforged.fml.LogicalSide; + +@Deprecated(forRemoval = true, since = "7.2") +public record DynamicConnectionState(boolean isInsert, DyeColor insertChannel, boolean isExtract, + DyeColor extractChannel, RedstoneControl control, DyeColor redstoneChannel, + @UseOnly(LogicalSide.SERVER) ItemStack filterInsert, @UseOnly(LogicalSide.SERVER) ItemStack filterExtract, + @UseOnly(LogicalSide.SERVER) ItemStack upgradeExtract) implements ConnectionState { + + public static Codec CODEC = RecordCodecBuilder.create(instance -> instance + .group(Codec.BOOL.fieldOf("is_insert").forGetter(DynamicConnectionState::isInsert), + DyeColor.CODEC.fieldOf("insert_channel").forGetter(DynamicConnectionState::insertChannel), + Codec.BOOL.fieldOf("is_extract").forGetter(DynamicConnectionState::isExtract), + DyeColor.CODEC.fieldOf("extract_channel").forGetter(DynamicConnectionState::extractChannel), + RedstoneControl.CODEC.fieldOf("redstone_control").forGetter(DynamicConnectionState::control), + DyeColor.CODEC.fieldOf("redstone_channel").forGetter(DynamicConnectionState::redstoneChannel), + ItemStack.OPTIONAL_CODEC.fieldOf("filter_insert").forGetter(DynamicConnectionState::filterInsert), + ItemStack.OPTIONAL_CODEC.fieldOf("filter_extract").forGetter(DynamicConnectionState::filterExtract), + ItemStack.OPTIONAL_CODEC.fieldOf("upgrade_extract") + .forGetter(DynamicConnectionState::upgradeExtract)) + .apply(instance, DynamicConnectionState::new)); + + public static StreamCodec STREAM_CODEC = MassiveStreamCodec + .composite(ByteBufCodecs.BOOL, DynamicConnectionState::isInsert, DyeColor.STREAM_CODEC, + DynamicConnectionState::insertChannel, ByteBufCodecs.BOOL, DynamicConnectionState::isExtract, + DyeColor.STREAM_CODEC, DynamicConnectionState::extractChannel, RedstoneControl.STREAM_CODEC, + DynamicConnectionState::control, DyeColor.STREAM_CODEC, DynamicConnectionState::redstoneChannel, + ItemStack.OPTIONAL_STREAM_CODEC, DynamicConnectionState::filterInsert, + ItemStack.OPTIONAL_STREAM_CODEC, DynamicConnectionState::filterExtract, + ItemStack.OPTIONAL_STREAM_CODEC, DynamicConnectionState::upgradeExtract, + DynamicConnectionState::new); + + public static DynamicConnectionState defaultConnection(Level level, BlockPos pos, Direction direction, + Holder> type) { + // Conduit.ConduitConnectionData defaultConnection = + // type.value().getDefaultConnection(level, pos, direction); + // return new DynamicConnectionState(defaultConnection.isInsert(), + // DyeColor.GREEN, defaultConnection.isExtract(), DyeColor.GREEN, + // defaultConnection.control(), DyeColor.RED, ItemStack.EMPTY, ItemStack.EMPTY, + // ItemStack.EMPTY); + // STUB + return new DynamicConnectionState(false, DyeColor.GREEN, true, DyeColor.GREEN, RedstoneControl.NEVER_ACTIVE, + DyeColor.RED, ItemStack.EMPTY, ItemStack.EMPTY, ItemStack.EMPTY); + } + + @Override + public boolean isConnection() { + return true; + } + + public ItemStack getItem(SlotType slotType) { + if (slotType == SlotType.FILTER_EXTRACT) { + return filterExtract; + } + + if (slotType == SlotType.FILTER_INSERT) { + return filterInsert; + } + + return upgradeExtract; + } + + public DynamicConnectionState withItem(SlotType type, ItemStack stack) { + Map items = new HashMap<>(); + for (SlotType type1 : SlotType.values()) { + items.put(type1, type1 == type ? stack : getItem(type1)); + } + return new DynamicConnectionState(isInsert, insertChannel, isExtract, extractChannel, control, redstoneChannel, + items.get(SlotType.FILTER_INSERT), items.get(SlotType.FILTER_EXTRACT), + items.get(SlotType.UPGRADE_EXTRACT)); + } + + public DynamicConnectionState withEnabled(boolean forExtract, boolean value) { + return new DynamicConnectionState(!forExtract ? value : isInsert, insertChannel, forExtract ? value : isExtract, + extractChannel, control, redstoneChannel, filterInsert, filterExtract, upgradeExtract); + } + + public DynamicConnectionState withColor(boolean forExtract, DyeColor value) { + return new DynamicConnectionState(isInsert, !forExtract ? value : insertChannel, isExtract, + forExtract ? value : extractChannel, control, redstoneChannel, filterInsert, filterExtract, + upgradeExtract); + } + + public DynamicConnectionState withRedstoneMode(RedstoneControl value) { + return new DynamicConnectionState(isInsert, insertChannel, isExtract, extractChannel, value, redstoneChannel, + filterInsert, filterExtract, upgradeExtract); + } + + public DynamicConnectionState withRedstoneChannel(DyeColor value) { + return new DynamicConnectionState(isInsert, insertChannel, isExtract, extractChannel, control, value, + filterInsert, filterExtract, upgradeExtract); + } + + public boolean isEmpty() { + return !isInsert && !isExtract; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java new file mode 100644 index 0000000000..1bc2e94144 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyFluidConduitData.java @@ -0,0 +1,93 @@ +package com.enderio.conduits.common.conduit.legacy; + +import com.enderio.conduits.api.network.node.NodeData; +import com.enderio.conduits.api.network.node.legacy.ConduitData; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; +import com.enderio.conduits.common.init.ConduitTypes; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.Objects; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.Fluids; +import org.jetbrains.annotations.Nullable; + +@Deprecated(forRemoval = true, since = "7.2.0-alpha") +public class LegacyFluidConduitData implements ConduitData { + + public static MapCodec CODEC = RecordCodecBuilder + .mapCodec( + instance -> instance + .group(Codec.BOOL.fieldOf("should_reset").forGetter(i -> i.shouldReset), + BuiltInRegistries.FLUID.byNameCodec() + .optionalFieldOf("locked_fluid", Fluids.EMPTY) + .forGetter(i -> i.lockedFluid)) + .apply(instance, LegacyFluidConduitData::new)); + + public static StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, i -> i.shouldReset, ByteBufCodecs.registry(Registries.FLUID), i -> i.lockedFluid, + LegacyFluidConduitData::new); + + private Fluid lockedFluid = Fluids.EMPTY; + private boolean shouldReset = false; + + public LegacyFluidConduitData() { + } + + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + public LegacyFluidConduitData(boolean shouldReset, Fluid fluid) { + this.shouldReset = shouldReset; + this.lockedFluid = fluid; + } + + public Fluid lockedFluid() { + return lockedFluid; + } + + public void setLockedFluid(Fluid lockedFluid) { + this.lockedFluid = lockedFluid; + } + + public boolean shouldReset() { + return shouldReset; + } + + public void setShouldReset(boolean shouldReset) { + this.shouldReset = shouldReset; + } + + @Override + public LegacyFluidConduitData withClientChanges(LegacyFluidConduitData guiData) { + this.shouldReset = guiData.shouldReset; + + // TODO: Soon we will swap to records which will mean this will be a new + // instance. + // This API has been designed with this pending change in mind. + return this; + } + + @Override + public LegacyFluidConduitData deepCopy() { + return new LegacyFluidConduitData(shouldReset, lockedFluid); + } + + @Override + public ConduitDataType type() { + return ConduitTypes.Data.FLUID.get(); + } + + @Override + public @Nullable NodeData toNodeData() { + return null; + } + + @Override + public int hashCode() { + return Objects.hash(shouldReset, lockedFluid); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitData.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java similarity index 52% rename from enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitData.java rename to enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java index 16d3199127..5277cc33ae 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitData.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyItemConduitData.java @@ -1,53 +1,56 @@ -package com.enderio.conduits.common.conduit.type.item; +package com.enderio.conduits.common.conduit.legacy; -import com.enderio.conduits.api.ConduitDataType; -import com.enderio.conduits.api.ConduitData; +import com.enderio.conduits.api.network.node.NodeData; +import com.enderio.conduits.api.network.node.legacy.ConduitData; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; +import com.enderio.conduits.common.conduit.type.item.ItemConduitNodeData; import com.enderio.conduits.common.init.ConduitTypes; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import io.netty.buffer.ByteBuf; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; import net.minecraft.core.Direction; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -public class ItemConduitData implements ConduitData { +@Deprecated(forRemoval = true, since = "7.2.0-alpha") +public class LegacyItemConduitData implements ConduitData { - public static MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group( - Codec.unboundedMap(Direction.CODEC, ItemSidedData.CODEC) - .fieldOf("item_sided_data").forGetter(i -> i.itemSidedData) - ).apply(instance, ItemConduitData::new) - ); + public static MapCodec CODEC = RecordCodecBuilder + .mapCodec(instance -> instance.group(Codec.unboundedMap(Direction.CODEC, ItemSidedData.CODEC) + .fieldOf("item_sided_data") + .forGetter(i -> i.itemSidedData)).apply(instance, LegacyItemConduitData::new)); - public static StreamCodec STREAM_CODEC = - ByteBufCodecs.map(i -> (Map) new HashMap(i), - Direction.STREAM_CODEC, ItemSidedData.STREAM_CODEC) - .map(ItemConduitData::new, i -> i.itemSidedData).cast(); + public static StreamCodec STREAM_CODEC = ByteBufCodecs + .map(i -> (Map) new HashMap(i), Direction.STREAM_CODEC, + ItemSidedData.STREAM_CODEC) + .map(LegacyItemConduitData::new, i -> i.itemSidedData) + .cast(); public Map itemSidedData; - public ItemConduitData() { + public LegacyItemConduitData() { itemSidedData = new HashMap<>(Direction.values().length); } - public ItemConduitData(Map itemSidedData) { + public LegacyItemConduitData(Map itemSidedData) { this.itemSidedData = new HashMap<>(itemSidedData); } @Override - public ItemConduitData withClientChanges(ItemConduitData guiData) { + public LegacyItemConduitData withClientChanges(LegacyItemConduitData guiData) { for (Direction direction : Direction.values()) { compute(direction).applyGuiChanges(guiData.get(direction)); } - // TODO: Soon we will swap to records which will mean this will be a new instance. - // This API has been designed with this pending change in mind. + // TODO: Soon we will swap to records which will mean this will be a new + // instance. + // This API has been designed with this pending change in mind. return this; } @@ -65,12 +68,26 @@ public int hashCode() { } @Override - public ConduitDataType type() { + public ConduitDataType type() { return ConduitTypes.Data.ITEM.get(); } @Override - public ItemConduitData deepCopy() { + public @Nullable NodeData toNodeData() { + // Convert to the new node data format + Map roundRobinIndexes = new HashMap<>(); + for (var side : Direction.values()) { + var sidedData = itemSidedData.get(side); + if (sidedData != null && sidedData.isRoundRobin) { + roundRobinIndexes.put(side, sidedData.rotatingIndex); + } + } + + return new ItemConduitNodeData(roundRobinIndexes); + } + + @Override + public LegacyItemConduitData deepCopy() { var newSidedData = new HashMap(Direction.values().length); for (Direction direction : Direction.values()) { if (itemSidedData.containsKey(direction)) { @@ -78,29 +95,22 @@ public ItemConduitData deepCopy() { } } - return new ItemConduitData(newSidedData); + return new LegacyItemConduitData(newSidedData); } public static class ItemSidedData { - public static Codec CODEC = RecordCodecBuilder.create( - instance -> instance.group( - Codec.BOOL.fieldOf("is_round_robin").forGetter(i -> i.isRoundRobin), - Codec.INT.fieldOf("rotating_index").forGetter(i -> i.rotatingIndex), - Codec.BOOL.fieldOf("is_self_feed").forGetter(i -> i.isSelfFeed), - Codec.INT.fieldOf("priority").forGetter(i -> i.priority) - ).apply(instance, ItemSidedData::new) - ); - - public static StreamCodec STREAM_CODEC = StreamCodec.composite( - ByteBufCodecs.BOOL, - i -> i.isRoundRobin, - ByteBufCodecs.BOOL, - i -> i.isSelfFeed, - ByteBufCodecs.INT, - i -> i.priority, - ItemSidedData::new - ); + public static Codec CODEC = RecordCodecBuilder + .create(instance -> instance + .group(Codec.BOOL.fieldOf("is_round_robin").forGetter(i -> i.isRoundRobin), + Codec.INT.fieldOf("rotating_index").forGetter(i -> i.rotatingIndex), + Codec.BOOL.fieldOf("is_self_feed").forGetter(i -> i.isSelfFeed), + Codec.INT.fieldOf("priority").forGetter(i -> i.priority)) + .apply(instance, ItemSidedData::new)); + + public static StreamCodec STREAM_CODEC = StreamCodec.composite(ByteBufCodecs.BOOL, + i -> i.isRoundRobin, ByteBufCodecs.BOOL, i -> i.isSelfFeed, ByteBufCodecs.INT, i -> i.priority, + ItemSidedData::new); public static ItemSidedData EMPTY = new ItemSidedData(false, 0, false, 0); diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java new file mode 100644 index 0000000000..2aa9983bee --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/LegacyRedstoneConduitData.java @@ -0,0 +1,97 @@ +package com.enderio.conduits.common.conduit.legacy; + +import com.enderio.conduits.api.network.node.NodeData; +import com.enderio.conduits.api.network.node.legacy.ConduitData; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; +import com.enderio.conduits.common.init.ConduitTypes; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.DyeColor; +import org.jetbrains.annotations.Nullable; + +@Deprecated(forRemoval = true, since = "7.2") +public class LegacyRedstoneConduitData implements ConduitData { + + public static MapCodec CODEC = RecordCodecBuilder + .mapCodec( + instance -> instance + .group(Codec.BOOL.fieldOf("is_active").forGetter(i -> i.isActive), + Codec.unboundedMap(DyeColor.CODEC, Codec.INT) + .fieldOf("active_colors") + .forGetter(i -> i.activeColors)) + .apply(instance, LegacyRedstoneConduitData::new)); + + public static StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, r -> r.isActive, + ByteBufCodecs.map(HashMap::new, DyeColor.STREAM_CODEC, ByteBufCodecs.INT), r -> r.activeColors, + LegacyRedstoneConduitData::new); + + private boolean isActive = false; + private final EnumMap activeColors = new EnumMap<>(DyeColor.class); + + public LegacyRedstoneConduitData() { + } + + private LegacyRedstoneConduitData(boolean isActive, Map activeColors) { + this.isActive = isActive; + this.activeColors.putAll(activeColors); + } + + @Override + public ConduitDataType type() { + return ConduitTypes.Data.REDSTONE.get(); + } + + @Override + public @Nullable NodeData toNodeData() { + return null; + } + + public boolean isActive() { + return isActive; + } + + public boolean isActive(DyeColor color) { + return activeColors.containsKey(color); + } + + public int getSignal(DyeColor color) { + return activeColors.getOrDefault(color, 0); + } + + public Map getActiveColors() { + return activeColors; + } + + public void clearActive() { + activeColors.clear(); + isActive = false; + } + + public void setActiveColor(DyeColor color, int signal) { + if (activeColors.containsKey(color)) { + return; + } + + isActive = true; + activeColors.put(color, signal); + } + + @Override + public LegacyRedstoneConduitData deepCopy() { + return new LegacyRedstoneConduitData(isActive, new EnumMap<>(activeColors)); + } + + @Override + public int hashCode() { + return Objects.hash(isActive, activeColors); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/StaticConnectionStates.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java similarity index 80% rename from enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/StaticConnectionStates.java rename to enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java index 3927c158cd..efe8b09fff 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/connection/StaticConnectionStates.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/StaticConnectionStates.java @@ -1,14 +1,13 @@ -package com.enderio.conduits.common.conduit.connection; +package com.enderio.conduits.common.conduit.legacy; import com.mojang.serialization.Codec; import io.netty.buffer.ByteBuf; +import java.util.function.IntFunction; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.util.ByIdMap; import net.minecraft.util.StringRepresentable; -import java.util.function.IntFunction; - /** * This class is for all static ConnectionStates without being connected to a block. For this use {@link DynamicConnectionState}
* It's intended usage is to remove the full nullability things of ConnectionStates @@ -17,15 +16,17 @@ * {@linkplain StaticConnectionStates#DISCONNECTED DISCONNECTED} is for no Connection * {@linkplain StaticConnectionStates#DISABLED DISABLED} is for no connection and activly disabled, so they don't connect, when a conduit is placed next to it (this state is set using the wrench or when IO is both disabled the state returns to this) */ +@Deprecated(forRemoval = true, since = "7.2") public enum StaticConnectionStates implements ConnectionState, StringRepresentable { - CONNECTED(0, "connected"), - CONNECTED_ACTIVE(1, "connected_active"), - DISCONNECTED(2, "disconnected"), + CONNECTED(0, "connected"), CONNECTED_ACTIVE(1, "connected_active"), DISCONNECTED(2, "disconnected"), DISABLED(3, "disabled"); - public static final Codec CODEC = StringRepresentable.fromEnum(StaticConnectionStates::values); - public static final IntFunction BY_ID = ByIdMap.continuous(key -> key.id, values(), ByIdMap.OutOfBoundsStrategy.ZERO); - public static final StreamCodec STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, v -> v.id); + public static final Codec CODEC = StringRepresentable + .fromEnum(StaticConnectionStates::values); + public static final IntFunction BY_ID = ByIdMap.continuous(key -> key.id, values(), + ByIdMap.OutOfBoundsStrategy.ZERO); + public static final StreamCodec STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, + v -> v.id); private final int id; private final String name; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/package-info.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/package-info.java new file mode 100644 index 0000000000..7e1665c1bf --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/legacy/package-info.java @@ -0,0 +1,4 @@ +@javax.annotation.ParametersAreNonnullByDefault +@net.minecraft.MethodsReturnNonnullByDefault + +package com.enderio.conduits.common.conduit.legacy; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java new file mode 100644 index 0000000000..d1f7c36b75 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/ConduitMenu.java @@ -0,0 +1,319 @@ +package com.enderio.conduits.common.conduit.menu; + +import com.enderio.base.api.UseOnly; +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlockEntity; +import com.enderio.conduits.common.init.ConduitMenus; +import com.enderio.conduits.common.network.S2CConduitExtraGuiDataPacket; +import com.enderio.conduits.common.network.S2CConduitListPacket; +import com.enderio.conduits.common.network.SetConduitConnectionConfigPacket; +import com.enderio.core.common.menu.BaseEnderMenu; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import me.liliandev.ensure.ensures.EnsureSide; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.ItemStack; +import net.neoforged.fml.LogicalSide; +import net.neoforged.neoforge.network.PacketDistributor; +import org.jetbrains.annotations.Nullable; + +// TODO: Make this not connect to the block entity at all, that way when the screen desyncs, the client world isn't desynced too. +// This means server menu should get/set connections direct from the BE but the client should have a standalone config store. +// Need to work out what this means for the client sync tag - it might need to be synced separately to the client from this GUI too. +// Possibly create an NBT sync slot and then use it for that? +public class ConduitMenu extends BaseEnderMenu { + + public static void openConduitMenu(ServerPlayer serverPlayer, ConduitBundleBlockEntity conduitBundle, + Direction side, Holder> conduit) { + serverPlayer.openMenu(new MenuProvider(conduitBundle, side, conduit), buf -> { + buf.writeBlockPos(conduitBundle.getBlockPos()); + buf.writeEnum(side); + Conduit.STREAM_CODEC.encode(buf, conduit); + ClientConnectionAccessor.writeStartingSyncData(conduitBundle, side, conduit, buf); + }); + } + + public static final int BUTTON_CHANGE_CONDUIT_START_ID = 0; + public static final int BUTTON_CHANGE_CONDUIT_ID_COUNT = ConduitBundleBlockEntity.MAX_CONDUITS; + + private final Direction side; + private final Holder> selectedConduit; + + private final ConnectionAccessor connectionAccessor; + + @UseOnly(LogicalSide.SERVER) + private ConnectionConfig remoteConnectionConfig; + + @UseOnly(LogicalSide.SERVER) + private CompoundTag remoteExtraGuiData; + + @UseOnly(LogicalSide.SERVER) + private int conduitListHashCode; + + private BlockPos pos; + + public ConduitMenu(int containerId, Inventory playerInventory, ConduitBundleBlockEntity conduitBundle, + Direction side, Holder> selectedConduit) { + super(ConduitMenus.CONDUIT_MENU.get(), containerId, playerInventory); + + this.pos = conduitBundle.getBlockPos(); + this.side = side; + this.selectedConduit = selectedConduit; + this.connectionAccessor = conduitBundle; + + // Set to sensible defaults to allow a sync after the menu opens + this.remoteConnectionConfig = selectedConduit.value().connectionConfigType().getDefault(); + + // TODO: Add conduit slots. + + addPlayerInventorySlots(23, 113); + } + + public ConduitMenu(int containerId, Inventory playerInventory, RegistryFriendlyByteBuf buf) { + super(ConduitMenus.CONDUIT_MENU.get(), containerId, playerInventory); + + pos = buf.readBlockPos(); + side = buf.readEnum(Direction.class); + selectedConduit = Conduit.STREAM_CODEC.decode(buf); + + // Use default config until synced by broadcastChanges. + var clientConnectionAccessor = new ClientConnectionAccessor(buf); + this.connectionAccessor = clientConnectionAccessor; + + // TODO: Add conduit slots. + + addPlayerInventorySlots(23, 113); + } + + public BlockPos getBlockPos() { + return pos; + } + + public Direction getSide() { + return side; + } + + public Holder> getSelectedConduit() { + return selectedConduit; + } + + public List>> getConnectedConduits() { + return connectionAccessor.getAllOpenableConduits(side); + } + + @EnsureSide(EnsureSide.Side.CLIENT) + public void setConnectedConduits(List>> connectedConduits) { + if (connectionAccessor instanceof ClientConnectionAccessor clientConnectionAccessor) { + clientConnectionAccessor.connectedConduits = connectedConduits; + } + } + + public ConnectionConfigType connectionConfigType() { + return selectedConduit.value().connectionConfigType(); + } + + public ConnectionConfig connectionConfig() { + return connectionAccessor.getConnectionConfig(side, selectedConduit); + } + + public T connectionConfig(ConnectionConfigType type) { + var config = connectionConfig(); + if (config.type() == type) { + // noinspection unchecked + return (T) config; + } + + throw new IllegalStateException("Connection config type mismatch"); + } + + public void setConnectionConfig(ConnectionConfig config) { + connectionAccessor.setConnectionConfig(side, selectedConduit, config); + } + + @Nullable + public CompoundTag extraGuiData() { + return connectionAccessor.getConduitExtraGuiData(side, selectedConduit); + } + + @EnsureSide(EnsureSide.Side.CLIENT) + public void setExtraGuiData(CompoundTag extraGuiData) { + if (connectionAccessor instanceof ClientConnectionAccessor clientConnectionAccessor) { + clientConnectionAccessor.extraGuiData = extraGuiData; + } + } + + @Override + public boolean stillValid(Player player) { + return connectionAccessor.stillValid(player) && connectionAccessor.canOpenScreen(side, selectedConduit); + } + + @Override + public boolean clickMenuButton(Player player, int id) { + // var bundle = getBlockEntity(); + // var currentConfig = connectionConfig(); + + if (player instanceof ServerPlayer serverPlayer) { + if (id >= BUTTON_CHANGE_CONDUIT_START_ID + && id <= BUTTON_CHANGE_CONDUIT_ID_COUNT + BUTTON_CHANGE_CONDUIT_ID_COUNT) { + // TODO: attempt to change to a different conduit on the same face. + // var conduitList = getBlockEntity().getConduits(); + + // TODO Find and switch to conduit and tell the client. + + int conduitIndex = id - BUTTON_CHANGE_CONDUIT_START_ID; + var connectedConduits = getConnectedConduits(); + if (conduitIndex < connectedConduits.size()) { + openConduitMenu(serverPlayer, (ConduitBundleBlockEntity) connectionAccessor, side, + connectedConduits.get(conduitIndex)); + } + } + } + + return super.clickMenuButton(player, id); + } + + // TODO + @Override + public ItemStack quickMoveStack(Player player, int i) { + return ItemStack.EMPTY; + } + + @Override + public void broadcastChanges() { + super.broadcastChanges(); + + if (getPlayerInventory().player instanceof ServerPlayer serverPlayer) { + if (!Objects.equals(connectionConfig(), remoteConnectionConfig)) { + PacketDistributor.sendToPlayer(serverPlayer, + new SetConduitConnectionConfigPacket(containerId, connectionConfig())); + this.remoteConnectionConfig = connectionConfig(); + } + + var extraGuiData = extraGuiData(); + if (!Objects.equals(extraGuiData, remoteExtraGuiData)) { + PacketDistributor.sendToPlayer(serverPlayer, + new S2CConduitExtraGuiDataPacket(containerId, extraGuiData)); + this.remoteExtraGuiData = extraGuiData; + } + + var conduitList = connectionAccessor.getAllOpenableConduits(side); + if (conduitListHashCode != conduitList.hashCode()) { + PacketDistributor.sendToPlayer(serverPlayer, new S2CConduitListPacket(containerId, conduitList)); + conduitListHashCode = conduitList.hashCode(); + } + } + } + + public interface ConnectionAccessor { + List>> getAllOpenableConduits(Direction side); + + ConnectionConfig getConnectionConfig(Direction side, Holder> conduit); + + void setConnectionConfig(Direction side, Holder> conduit, ConnectionConfig config); + + boolean canOpenScreen(Direction side, Holder> conduit); + + @Nullable + CompoundTag getConduitExtraGuiData(Direction side, Holder> conduit); + + boolean stillValid(Player player); + } + + private static class ClientConnectionAccessor implements ConnectionAccessor { + + private List>> connectedConduits = List.of(); + + private ConnectionConfig connectionConfig; + + @Nullable + private CompoundTag extraGuiData; + + public ClientConnectionAccessor(RegistryFriendlyByteBuf buf) { + this.connectedConduits = Conduit.STREAM_CODEC + .apply(ByteBufCodecs.list(ConduitBundleBlockEntity.MAX_CONDUITS)) + .decode(buf); + + this.connectionConfig = ConnectionConfig.STREAM_CODEC.decode(buf); + + extraGuiData = ByteBufCodecs.optional(ByteBufCodecs.COMPOUND_TAG) + .map(opt -> opt.orElse(null), Optional::ofNullable) + .decode(buf); + } + + private static void writeStartingSyncData(ConduitBundleBlockEntity conduitBundle, Direction side, + Holder> conduit, RegistryFriendlyByteBuf buf) { + Conduit.STREAM_CODEC.apply(ByteBufCodecs.list(ConduitBundleBlockEntity.MAX_CONDUITS)) + .encode(buf, conduitBundle.getAllOpenableConduits(side)); + + ConnectionConfig.STREAM_CODEC.encode(buf, conduitBundle.getConnectionConfig(side, conduit)); + + // noinspection DataFlowIssue + ByteBufCodecs.optional(ByteBufCodecs.COMPOUND_TAG) + .map(opt -> opt.orElse(null), Optional::ofNullable) + .encode(buf, conduitBundle.getConduitExtraGuiData(side, conduit)); + } + + @Override + public List>> getAllOpenableConduits(Direction side) { + return connectedConduits; + } + + @Override + public ConnectionConfig getConnectionConfig(Direction side, Holder> conduit) { + return connectionConfig; + } + + @Override + public void setConnectionConfig(Direction side, Holder> conduit, ConnectionConfig config) { + connectionConfig = config; + } + + @Override + public boolean canOpenScreen(Direction side, Holder> conduit) { + return true; + } + + @Override + public CompoundTag getConduitExtraGuiData(Direction side, Holder> conduit) { + return extraGuiData; + } + + @Override + public boolean stillValid(Player player) { + return true; + } + } + + private record MenuProvider(ConduitBundleBlockEntity conduitBundle, Direction side, Holder> conduit) + implements net.minecraft.world.MenuProvider { + + @Override + public Component getDisplayName() { + return conduit.value().description(); + } + + @Override + public AbstractContainerMenu createMenu(int containerId, Inventory inventory, Player player) { + return new ConduitMenu(containerId, inventory, conduitBundle, side, conduit); + } + + @Override + public boolean shouldTriggerClientSideContainerClosingOnOpen() { + // Prevents the mouse from jumping when changing between conduits. + return false; + } + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/package-info.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/package-info.java similarity index 65% rename from enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/package-info.java rename to enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/package-info.java index 05d1ff4ede..fc2c317dd0 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/client/gui/conduit/package-info.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/menu/package-info.java @@ -1,4 +1,4 @@ @javax.annotation.ParametersAreNonnullByDefault @net.minecraft.MethodsReturnNonnullByDefault -package com.enderio.conduits.client.gui.conduit; +package com.enderio.conduits.common.conduit.menu; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java index 4a607dfd91..fe79788bb1 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduit.java @@ -1,22 +1,25 @@ package com.enderio.conduits.common.conduit.type.energy; +import com.enderio.base.api.misc.RedstoneControl; +import com.enderio.conduits.api.ColoredRedstoneProvider; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitMenuData; -import com.enderio.conduits.api.ConduitNode; import com.enderio.conduits.api.ConduitType; -import com.enderio.base.api.misc.RedstoneControl; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.network.node.ConduitNode; import com.enderio.conduits.common.init.ConduitLang; import com.enderio.conduits.common.init.ConduitTypes; import com.enderio.core.common.util.TooltipUtil; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.function.Consumer; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Item; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; @@ -26,29 +29,22 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.function.Consumer; +// TODO: Redstone control isn't working properly - the cap needs to refuse input to the node if the connection being fed into is blocked by a redstone signal. + +public record EnergyConduit(ResourceLocation texture, Component description, int transferRatePerTick) + implements Conduit { -public record EnergyConduit( - ResourceLocation texture, - Component description, - int transferRatePerTick -) implements Conduit { - - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - builder -> builder - .group( - ResourceLocation.CODEC.fieldOf("texture").forGetter(Conduit::texture), - ComponentSerialization.CODEC.fieldOf("description").forGetter(Conduit::description), - Codec.INT.fieldOf("transfer_rate").forGetter(EnergyConduit::transferRatePerTick) - ).apply(builder, EnergyConduit::of) - ); + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(builder -> builder + .group(ResourceLocation.CODEC.fieldOf("texture").forGetter(Conduit::texture), + ComponentSerialization.CODEC.fieldOf("description").forGetter(Conduit::description), + Codec.INT.fieldOf("transfer_rate").forGetter(EnergyConduit::transferRatePerTick)) + .apply(builder, EnergyConduit::of)); public static EnergyConduit of(ResourceLocation texture, Component description, int transferRate) { return new EnergyConduit(texture, description, transferRate); } private static final EnergyConduitTicker TICKER = new EnergyConduitTicker(); - private static final ConduitMenuData MENU_DATA = new ConduitMenuData.Simple(false, false, false, false, false, true); // Not configurable - energy is instantaneous @Override @@ -67,17 +63,17 @@ public EnergyConduitTicker getTicker() { } @Override - public ConduitMenuData getMenuData() { - return MENU_DATA; + public boolean hasMenu() { + return true; } @Override - public boolean canBeInSameBundle(Holder> otherConduit) { + public boolean canBeInSameBundle(Holder> otherConduit) { return !(otherConduit.value() instanceof EnergyConduit); } @Override - public boolean canBeReplacedBy(Holder> otherConduit) { + public boolean canBeReplacedBy(Holder> otherConduit) { if (!(otherConduit.value() instanceof EnergyConduit otherEnergyConduit)) { return false; } @@ -86,19 +82,54 @@ public boolean canBeReplacedBy(Holder> otherConduit) { } @Override - public @Nullable TCap proxyCapability(BlockCapability capability, ConduitNode node, - Level level, BlockPos pos, @Nullable TContext context) { + public boolean canConnectToBlock(Level level, BlockPos conduitPos, Direction direction) { + IEnergyStorage capability = level.getCapability(Capabilities.EnergyStorage.BLOCK, + conduitPos.relative(direction), direction.getOpposite()); + return capability != null; + } + + @Override + public @Nullable TCap proxyCapability(Level level, ColoredRedstoneProvider coloredRedstoneProvider, + ConduitNode node, BlockCapability capability, @Nullable TContext context) { if (Capabilities.EnergyStorage.BLOCK == capability && (context == null || context instanceof Direction)) { + boolean isMutable = true; + if (context != null) { - var state = node.getIOState((Direction) context); - if (state.isPresent() && !state.get().isExtract()) { + Direction side = (Direction) context; + + // No connection, no cap. + if (!node.isConnectedTo(side)) { return null; } + + var config = node.getConnectionConfig(side, connectionConfigType()); + if (!config.isConnected() || !config.isReceive()) { + return null; + } + + if (config.receiveRedstoneControl() == RedstoneControl.NEVER_ACTIVE) { + isMutable = false; + } else if (config.receiveRedstoneControl() != RedstoneControl.ALWAYS_ACTIVE) { + boolean hasRedstone = coloredRedstoneProvider.isRedstoneActive(level, node.getPos(), + config.receiveRedstoneChannel()); + if (!hasRedstone) { + for (Direction direction : Direction.values()) { + if (level.getSignal(node.getPos().relative(direction), direction.getOpposite()) > 0) { + hasRedstone = true; + break; + } + } + } + + if (!hasRedstone) { + isMutable = false; + } + } } - //noinspection unchecked - return (TCap)new EnergyConduitStorage(transferRatePerTick(), node); + // noinspection unchecked + return (TCap) new EnergyConduitStorage(isMutable, transferRatePerTick(), node); } return null; @@ -110,23 +141,19 @@ public void onRemoved(ConduitNode node, Level level, BlockPos pos) { } @Override - public ConduitConnectionData getDefaultConnection(Level level, BlockPos pos, Direction direction) { - IEnergyStorage capability = level.getCapability(Capabilities.EnergyStorage.BLOCK, pos.relative(direction), direction.getOpposite()); - if (capability != null) { - if (!capability.canReceive() && !capability.canExtract()) { - // This ensures that if there's an energy capability that might be pushing but won't allow pulling is present, we can still interact - // For example Thermal's Dynamos report false until they have energy in them and flux networks always refuse. - return new ConduitConnectionData(false, true, RedstoneControl.ALWAYS_ACTIVE); - } - - return new ConduitConnectionData(capability.canReceive(), capability.canExtract(), RedstoneControl.ALWAYS_ACTIVE); - } + public ConnectionConfigType connectionConfigType() { + return ConduitTypes.ConnectionTypes.ENERGY.get(); + } - return Conduit.super.getDefaultConnection(level, pos, direction); + @Override + public EnergyConduitConnectionConfig convertConnection(boolean isInsert, boolean isExtract, DyeColor inputChannel, + DyeColor outputChannel, RedstoneControl redstoneControl, DyeColor redstoneChannel) { + return new EnergyConduitConnectionConfig(isInsert, isExtract, redstoneControl, redstoneChannel); } @Override - public void addToTooltip(Item.TooltipContext pContext, Consumer pTooltipAdder, TooltipFlag pTooltipFlag) { + public void addToTooltip(Item.TooltipContext pContext, Consumer pTooltipAdder, + TooltipFlag pTooltipFlag) { String transferLimitFormatted = String.format("%,d", transferRatePerTick()); pTooltipAdder.accept(TooltipUtil.styledWithArgs(ConduitLang.ENERGY_RATE_TOOLTIP, transferLimitFormatted)); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java new file mode 100644 index 0000000000..fc9fb29a27 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitConnectionConfig.java @@ -0,0 +1,111 @@ +package com.enderio.conduits.common.conduit.type.energy; + +import com.enderio.base.api.misc.RedstoneControl; +import com.enderio.conduits.api.ConduitRedstoneSignalAware; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.connection.config.IOConnectionConfig; +import com.enderio.conduits.api.connection.config.RedstoneSensitiveConnectionConfig; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.netty.buffer.ByteBuf; +import java.util.List; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.DyeColor; + +public record EnergyConduitConnectionConfig(boolean isSend, boolean isReceive, RedstoneControl receiveRedstoneControl, + DyeColor receiveRedstoneChannel) implements IOConnectionConfig, RedstoneSensitiveConnectionConfig { + + public static EnergyConduitConnectionConfig DEFAULT = new EnergyConduitConnectionConfig(true, true, + RedstoneControl.ALWAYS_ACTIVE, DyeColor.RED); + + public static MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance + .group(Codec.BOOL.fieldOf("is_send").forGetter(EnergyConduitConnectionConfig::isSend), + Codec.BOOL.fieldOf("is_receive").forGetter(EnergyConduitConnectionConfig::isReceive), + RedstoneControl.CODEC.fieldOf("receive_redstone_control") + .forGetter(EnergyConduitConnectionConfig::receiveRedstoneControl), + DyeColor.CODEC.fieldOf("receive_redstone_channel") + .forGetter(EnergyConduitConnectionConfig::receiveRedstoneChannel)) + .apply(instance, EnergyConduitConnectionConfig::new)); + + public static StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, EnergyConduitConnectionConfig::isSend, ByteBufCodecs.BOOL, + EnergyConduitConnectionConfig::isReceive, RedstoneControl.STREAM_CODEC, + EnergyConduitConnectionConfig::receiveRedstoneControl, DyeColor.STREAM_CODEC, + EnergyConduitConnectionConfig::receiveRedstoneChannel, EnergyConduitConnectionConfig::new); + + public static final ConnectionConfigType TYPE = new ConnectionConfigType<>(CODEC, + STREAM_CODEC.cast(), () -> DEFAULT); + + @Override + public ConnectionConfig reconnected() { + return new EnergyConduitConnectionConfig(DEFAULT.isSend, DEFAULT.isReceive, receiveRedstoneControl, + receiveRedstoneChannel); + } + + @Override + public ConnectionConfig disconnected() { + return new EnergyConduitConnectionConfig(false, false, receiveRedstoneControl, receiveRedstoneChannel); + } + + @Override + public DyeColor sendColor() { + return DyeColor.RED; + } + + @Override + public DyeColor receiveColor() { + return DyeColor.RED; + } + + @Override + public boolean canSend(ConduitRedstoneSignalAware signalAware) { + // TODO: sendRedstoneControl + return isSend(); + } + + @Override + public boolean canReceive(ConduitRedstoneSignalAware signalAware) { + if (!isReceive()) { + return false; + } + + if (receiveRedstoneControl.isRedstoneSensitive()) { + return receiveRedstoneControl.isActive(signalAware.hasRedstoneSignal(receiveRedstoneChannel)); + } else { + return true; + } + } + + @Override + public List getRedstoneSignalColors() { + if (receiveRedstoneControl.isRedstoneSensitive()) { + return List.of(receiveRedstoneChannel); + } + + return List.of(); + } + + public EnergyConduitConnectionConfig withIsSend(boolean isSend) { + return new EnergyConduitConnectionConfig(isSend, isReceive, receiveRedstoneControl, receiveRedstoneChannel); + } + + public EnergyConduitConnectionConfig withIsReceive(boolean isReceive) { + return new EnergyConduitConnectionConfig(isSend, isReceive, receiveRedstoneControl, receiveRedstoneChannel); + } + + public EnergyConduitConnectionConfig withReceiveRedstoneControl(RedstoneControl receiveRedstoneControl) { + return new EnergyConduitConnectionConfig(isSend, isReceive, receiveRedstoneControl, receiveRedstoneChannel); + } + + public EnergyConduitConnectionConfig withReceiveRedstoneChannel(DyeColor receiveRedstoneChannel) { + return new EnergyConduitConnectionConfig(isSend, isReceive, receiveRedstoneControl, receiveRedstoneChannel); + } + + @Override + public ConnectionConfigType type() { + return TYPE; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java index 7230c39e4a..6a12a047c6 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitNetworkContext.java @@ -1,19 +1,19 @@ package com.enderio.conduits.common.conduit.type.energy; -import com.enderio.conduits.api.ConduitNetworkContext; -import com.enderio.conduits.api.ConduitNetworkContextType; -import com.enderio.conduits.common.init.Conduits; +import com.enderio.conduits.api.network.ConduitNetworkContext; +import com.enderio.conduits.api.network.ConduitNetworkContextType; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; public class EnergyConduitNetworkContext implements ConduitNetworkContext { - public static final Codec CODEC = RecordCodecBuilder.create( - builder -> builder.group( - Codec.INT.fieldOf("energy_stored").forGetter(i -> i.energyStored), - Codec.INT.fieldOf("rotating_index").forGetter(i -> i.rotatingIndex) - ).apply(builder, EnergyConduitNetworkContext::new) - ); + public static final Codec CODEC = RecordCodecBuilder.create(builder -> builder + .group(Codec.INT.fieldOf("energy_stored").forGetter(i -> i.energyStored), + Codec.INT.fieldOf("rotating_index").forGetter(i -> i.rotatingIndex)) + .apply(builder, EnergyConduitNetworkContext::new)); + + public static ConduitNetworkContextType TYPE = new ConduitNetworkContextType<>(CODEC, + EnergyConduitNetworkContext::new); private int energyStored = 0; private int rotatingIndex = 0; @@ -61,6 +61,6 @@ public EnergyConduitNetworkContext copy() { @Override public ConduitNetworkContextType type() { - return Conduits.ContextSerializers.ENERGY.get(); + return TYPE; } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java index 2ff66c4c12..8a3ba638a3 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitStorage.java @@ -1,13 +1,9 @@ package com.enderio.conduits.common.conduit.type.energy; -import com.enderio.conduits.api.ConduitNode; -import com.enderio.conduits.common.init.Conduits; +import com.enderio.conduits.api.network.node.ConduitNode; import net.neoforged.neoforge.energy.IEnergyStorage; -public record EnergyConduitStorage( - int transferRate, - ConduitNode node -) implements IEnergyStorage { +public record EnergyConduitStorage(boolean isMutable, int transferRate, ConduitNode node) implements IEnergyStorage { private static final int ENERGY_BUFFER_SCALER = 4; @@ -17,7 +13,7 @@ public int receiveEnergy(int toReceive, boolean simulate) { return 0; } - EnergyConduitNetworkContext context = node.getParentGraph().getOrCreateContext(Conduits.ContextSerializers.ENERGY.get()); + var context = node.getNetwork().getOrCreateContext(EnergyConduitNetworkContext.TYPE); // Cap to transfer rate. toReceive = Math.min(transferRate(), toReceive); @@ -37,7 +33,7 @@ public int extractEnergy(int toExtract, boolean simulate) { @Override public int getEnergyStored() { - EnergyConduitNetworkContext context = node.getParentGraph().getContext(Conduits.ContextSerializers.ENERGY.get()); + var context = node.getNetwork().getContext(EnergyConduitNetworkContext.TYPE); if (context == null) { return 0; } @@ -48,8 +44,9 @@ public int getEnergyStored() { @Override public int getMaxEnergyStored() { // Capacity is transfer rate + nodeCount * transferRatePerTick / 2 (expanded). - // This ensures at least the transfer rate of the cable is available, but capacity doesn't grow outrageously. - int nodeCount = node.getParentGraph().getNodes().size(); + // This ensures at least the transfer rate of the cable is available, but + // capacity doesn't grow outrageously. + int nodeCount = node.getNetwork().getNodes().size(); // The maximum number of nodes before the network capacity is INT_MAX. int maxNodesBeforeLimit = Integer.MAX_VALUE / (transferRate() / ENERGY_BUFFER_SCALER) - ENERGY_BUFFER_SCALER; @@ -67,9 +64,10 @@ public boolean canExtract() { } // The block will not expose this capability unless it can be extracted from - // This means we don't have to worry about checking if we can extract at this point. + // This means we don't have to worry about checking if we can extract at this + // point. @Override public boolean canReceive() { - return node.getParentGraph() != null; + return node.getNetwork() != null && isMutable; } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java index 33dc7f72de..f91f92a0ed 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/energy/EnergyConduitTicker.java @@ -1,35 +1,35 @@ package com.enderio.conduits.common.conduit.type.energy; import com.enderio.conduits.api.ColoredRedstoneProvider; -import com.enderio.conduits.api.ConduitNetwork; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; import com.enderio.conduits.api.ticker.IOAwareConduitTicker; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity; -import com.enderio.conduits.common.init.Conduits; import java.util.ArrayList; import java.util.List; -import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.DyeColor; import net.minecraft.world.level.Level; import net.neoforged.neoforge.capabilities.Capabilities; import net.neoforged.neoforge.energy.IEnergyStorage; +import org.jetbrains.annotations.Nullable; -public class EnergyConduitTicker implements IOAwareConduitTicker { +public class EnergyConduitTicker + extends IOAwareConduitTicker { public EnergyConduitTicker() { } @Override - public void tickColoredGraph(ServerLevel level, EnergyConduit conduit, List inserts, - List extracts, DyeColor color, ConduitNetwork graph, + protected void tickColoredGraph(ServerLevel level, EnergyConduit conduit, List senders, + List receivers, DyeColor color, ConduitNetwork graph, ColoredRedstoneProvider coloredRedstoneProvider) { // Adjust for tick rate. Always flow up so we are at minimum meeting the // required rate. int transferRate = (int) Math.ceil(conduit.transferRatePerTick() * (20.0 / conduit.graphTickRate())); - EnergyConduitNetworkContext context = graph.getContext(Conduits.ContextSerializers.ENERGY.get()); + EnergyConduitNetworkContext context = graph.getContext(EnergyConduitNetworkContext.TYPE); if (context == null) { return; } @@ -39,9 +39,9 @@ public void tickColoredGraph(ServerLevel level, EnergyConduit conduit, List storagesForInsert = new ArrayList<>(); - for (var insert : inserts) { - IEnergyStorage capability = level.getCapability(Capabilities.EnergyStorage.BLOCK, insert.move(), - insert.direction().getOpposite()); + for (var sender : senders) { + IEnergyStorage capability = level.getCapability(Capabilities.EnergyStorage.BLOCK, sender.neighborPos(), + sender.neighborSide()); if (capability != null) { storagesForInsert.add(capability); } @@ -75,18 +75,39 @@ public void tickColoredGraph(ServerLevel level, EnergyConduit conduit, List extractList, List insertList) { - return insertList.isEmpty(); + protected boolean canReceive(ConduitNode node, EnergyConduitConnectionConfig config) { + // We don't require a receive component. + return false; } @Override - public boolean canConnectTo(Level level, BlockPos conduitPos, Direction direction) { - if (level.getBlockEntity(conduitPos.relative(direction)) instanceof ConduitBundleBlockEntity) { - return false; + protected boolean shouldSkipColor(List senders, List receivers) { + return senders.isEmpty(); + } + + @Override + protected @Nullable EnergyConduitTicker.Connection createConnection(Level level, ConduitNode node, Direction side) { + IEnergyStorage energyStorage = level.getCapability(Capabilities.EnergyStorage.BLOCK, + node.getPos().relative(side), side.getOpposite()); + if (energyStorage != null) { + return new Connection(node, side, node.getConnectionConfig(side, EnergyConduitConnectionConfig.TYPE), + energyStorage); } - IEnergyStorage capability = level.getCapability(Capabilities.EnergyStorage.BLOCK, - conduitPos.relative(direction), direction.getOpposite()); - return capability != null; + return null; + } + + protected static class Connection extends SimpleConnection { + private final IEnergyStorage energyStorage; + + public Connection(ConduitNode node, Direction side, EnergyConduitConnectionConfig config, + IEnergyStorage energyStorage) { + super(node, side, config); + this.energyStorage = energyStorage; + } + + public IEnergyStorage energyStorage() { + return energyStorage; + } } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java index b907b47cd4..9002bfbf53 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduit.java @@ -2,34 +2,42 @@ import com.enderio.base.api.filter.FluidStackFilter; import com.enderio.base.api.filter.ResourceFilter; +import com.enderio.base.api.misc.RedstoneControl; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitMenuData; -import com.enderio.conduits.api.ConduitNode; import com.enderio.conduits.api.ConduitType; -import com.enderio.conduits.api.SlotType; -import com.enderio.conduits.api.upgrade.ConduitUpgrade; -import com.enderio.conduits.common.components.ExtractionSpeedUpgrade; +import com.enderio.conduits.api.bundle.ConduitBundleReader; +import com.enderio.conduits.api.bundle.SlotType; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.network.node.ConduitNode; +import com.enderio.conduits.api.network.node.legacy.ConduitDataAccessor; import com.enderio.conduits.common.init.ConduitLang; import com.enderio.conduits.common.init.ConduitTypes; import com.enderio.core.common.util.TooltipUtil; -import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.Objects; import java.util.function.Consumer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Item; import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.material.Fluids; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; +import org.jetbrains.annotations.Nullable; public record FluidConduit(ResourceLocation texture, Component description, int transferRatePerTick, - boolean isMultiFluid) implements Conduit { - - private static final Logger LOGGER = LogUtils.getLogger(); + boolean isMultiFluid) implements Conduit { public static final MapCodec CODEC = RecordCodecBuilder .mapCodec( @@ -41,10 +49,6 @@ public record FluidConduit(ResourceLocation texture, Component description, int Codec.BOOL.fieldOf("is_multi_fluid").forGetter(FluidConduit::isMultiFluid)) .apply(builder, FluidConduit::new)); - public static final ConduitMenuData NORMAL_MENU_DATA = new ConduitMenuData.Simple(true, true, true, false, false, - true); - public static final ConduitMenuData ADVANCED_MENU_DATA = new ConduitMenuData.Simple(true, true, true, true, true, - true); private static final FluidConduitTicker TICKER = new FluidConduitTicker(); @Override @@ -58,17 +62,17 @@ public FluidConduitTicker getTicker() { } @Override - public ConduitMenuData getMenuData() { - return isMultiFluid ? ADVANCED_MENU_DATA : NORMAL_MENU_DATA; + public boolean hasMenu() { + return true; } @Override - public boolean canBeInSameBundle(Holder> otherConduit) { + public boolean canBeInSameBundle(Holder> otherConduit) { return !(otherConduit.value() instanceof FluidConduit); } @Override - public boolean canBeReplacedBy(Holder> otherConduit) { + public boolean canBeReplacedBy(Holder> otherConduit) { if (!(otherConduit.value() instanceof FluidConduit otherFluidConduit)) { return false; } @@ -77,33 +81,44 @@ public boolean canBeReplacedBy(Holder> otherConduit) { } @Override - public boolean canConnectTo(ConduitNode selfNode, ConduitNode otherNode) { - FluidConduitData selfData = selfNode.getOrCreateData(ConduitTypes.Data.FLUID.get()); - FluidConduitData otherData = otherNode.getOrCreateData(ConduitTypes.Data.FLUID.get()); - - return selfData.lockedFluid() == null || otherData.lockedFluid() == null - || selfData.lockedFluid() == otherData.lockedFluid(); + public boolean hasServerConnectionChecks() { + return !isMultiFluid(); } @Override - public void onConnectTo(ConduitNode selfNode, ConduitNode otherNode) { - FluidConduitData selfData = selfNode.getOrCreateData(ConduitTypes.Data.FLUID.get()); - FluidConduitData otherData = otherNode.getOrCreateData(ConduitTypes.Data.FLUID.get()); + public boolean canConnectConduits(ConduitNode selfNode, ConduitNode otherNode) { + if (isMultiFluid()) { + return true; + } - if (selfData.lockedFluid() != null) { - if (otherData.lockedFluid() != null && selfData.lockedFluid() != otherData.lockedFluid()) { - LOGGER.warn("incompatible fluid conduits merged"); - } + // Ensure the networks are not locked to different fluids before connecting. + var selfNetwork = selfNode.getNetwork(); + var otherNetwork = otherNode.getNetwork(); + + // If one network does not yet exist, then we're good to connect. + if (selfNetwork == null || otherNetwork == null) { + return true; + } + + var selfContext = selfNetwork.getContext(FluidConduitNetworkContext.TYPE); + var otherContext = otherNetwork.getContext(FluidConduitNetworkContext.TYPE); + + if (selfContext == null || otherContext == null) { + return true; + } - otherData.setLockedFluid(selfData.lockedFluid()); - } else if (otherData.lockedFluid() != null) { - selfData.setLockedFluid(otherData.lockedFluid()); + if (selfContext.lockedFluid().isSame(Fluids.EMPTY) || otherContext.lockedFluid().isSame(Fluids.EMPTY)) { + return true; } + + return selfContext.lockedFluid().isSame(otherContext.lockedFluid()); } @Override - public boolean canApplyUpgrade(SlotType slotType, ConduitUpgrade conduitUpgrade) { - return conduitUpgrade instanceof ExtractionSpeedUpgrade; + public boolean canConnectToBlock(Level level, BlockPos conduitPos, Direction direction) { + IFluidHandler capability = level.getCapability(Capabilities.FluidHandler.BLOCK, conduitPos.relative(direction), + direction.getOpposite()); + return capability != null; } @Override @@ -111,6 +126,61 @@ public boolean canApplyFilter(SlotType slotType, ResourceFilter resourceFilter) return resourceFilter instanceof FluidStackFilter; } + @Override + public ConnectionConfigType connectionConfigType() { + return FluidConduitConnectionConfig.TYPE; + } + + @Override + public FluidConduitConnectionConfig convertConnection(boolean isInsert, boolean isExtract, DyeColor inputChannel, + DyeColor outputChannel, RedstoneControl redstoneControl, DyeColor redstoneChannel) { + return new FluidConduitConnectionConfig(isInsert, inputChannel, isExtract, outputChannel, redstoneControl, + redstoneChannel); + } + + @Override + public void copyLegacyData(ConduitNode node, ConduitDataAccessor legacyDataAccessor) { + var legacyData = legacyDataAccessor.getData(ConduitTypes.Data.FLUID.get()); + if (legacyData == null) { + return; + } + + var context = Objects.requireNonNull(node.getNetwork()).getOrCreateContext(FluidConduitNetworkContext.TYPE); + + if (!context.lockedFluid().isSame(Fluids.EMPTY)) { + return; + } + + // Copy locked fluid from old data. + context.setLockedFluid(legacyData.lockedFluid()); + } + + @Override + public @Nullable CompoundTag getExtraGuiData(ConduitBundleReader conduitBundle, ConduitNode node, Direction side) { + return getExtraWorldData(conduitBundle, node); + } + + @Override + @Nullable + public CompoundTag getExtraWorldData(ConduitBundleReader conduitBundle, ConduitNode node) { + if (node.getNetwork() == null) { + return null; + } + + var context = node.getNetwork().getContext(FluidConduitNetworkContext.TYPE); + if (context == null) { + return null; + } + + if (context.lockedFluid().isSame(Fluids.EMPTY)) { + return null; + } + + var tag = new CompoundTag(); + tag.putString("LockedFluid", BuiltInRegistries.FLUID.getKey(context.lockedFluid()).toString()); + return tag; + } + @Override public void addToTooltip(Item.TooltipContext pContext, Consumer pTooltipAdder, TooltipFlag pTooltipFlag) { diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java new file mode 100644 index 0000000000..d0d3d719c4 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitConnectionConfig.java @@ -0,0 +1,130 @@ +package com.enderio.conduits.common.conduit.type.fluid; + +import com.enderio.base.api.misc.RedstoneControl; +import com.enderio.conduits.api.ConduitRedstoneSignalAware; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.connection.config.IOConnectionConfig; +import com.enderio.conduits.api.connection.config.RedstoneSensitiveConnectionConfig; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.netty.buffer.ByteBuf; +import java.util.List; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.DyeColor; + +public record FluidConduitConnectionConfig(boolean isSend, DyeColor sendColor, boolean isReceive, DyeColor receiveColor, + RedstoneControl receiveRedstoneControl, DyeColor receiveRedstoneChannel) + implements IOConnectionConfig, RedstoneSensitiveConnectionConfig { + + public static FluidConduitConnectionConfig DEFAULT = new FluidConduitConnectionConfig(false, DyeColor.GREEN, true, + DyeColor.GREEN, RedstoneControl.NEVER_ACTIVE, DyeColor.RED); + + public static MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance + .group(Codec.BOOL.fieldOf("is_send").forGetter(FluidConduitConnectionConfig::isSend), + DyeColor.CODEC.fieldOf("send_color").forGetter(FluidConduitConnectionConfig::sendColor), + Codec.BOOL.fieldOf("is_receive").forGetter(FluidConduitConnectionConfig::isReceive), + DyeColor.CODEC.fieldOf("receive_channel").forGetter(FluidConduitConnectionConfig::receiveColor), + RedstoneControl.CODEC.fieldOf("receive_redstone_control") + .forGetter(FluidConduitConnectionConfig::receiveRedstoneControl), + DyeColor.CODEC.fieldOf("receive_redstone_channel") + .forGetter(FluidConduitConnectionConfig::receiveRedstoneChannel)) + .apply(instance, FluidConduitConnectionConfig::new)); + + // @formatter:off + public static StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, + FluidConduitConnectionConfig::isSend, + DyeColor.STREAM_CODEC, + FluidConduitConnectionConfig::sendColor, + ByteBufCodecs.BOOL, + FluidConduitConnectionConfig::isReceive, + DyeColor.STREAM_CODEC, + FluidConduitConnectionConfig::receiveColor, + RedstoneControl.STREAM_CODEC, + FluidConduitConnectionConfig::receiveRedstoneControl, + DyeColor.STREAM_CODEC, + FluidConduitConnectionConfig::receiveRedstoneChannel, + FluidConduitConnectionConfig::new); + // @formatter:on + + public static ConnectionConfigType TYPE = new ConnectionConfigType<>(CODEC, + STREAM_CODEC.cast(), () -> DEFAULT); + + @Override + public ConnectionConfig reconnected() { + return new FluidConduitConnectionConfig(DEFAULT.isSend, sendColor, DEFAULT.isReceive, receiveColor, + receiveRedstoneControl, receiveRedstoneChannel); + } + + @Override + public ConnectionConfig disconnected() { + return new FluidConduitConnectionConfig(false, sendColor, false, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel); + } + + @Override + public boolean canSend(ConduitRedstoneSignalAware signalAware) { + // TODO: sendRedstoneControl + return isSend(); + } + + @Override + public boolean canReceive(ConduitRedstoneSignalAware signalAware) { + if (!isReceive()) { + return false; + } + + if (receiveRedstoneControl.isRedstoneSensitive()) { + return receiveRedstoneControl.isActive(signalAware.hasRedstoneSignal(receiveRedstoneChannel)); + } else { + return true; + } + } + + @Override + public List getRedstoneSignalColors() { + if (receiveRedstoneControl.isRedstoneSensitive()) { + return List.of(receiveRedstoneChannel); + } + + return List.of(); + } + + public FluidConduitConnectionConfig withIsSend(boolean isSend) { + return new FluidConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel); + } + + public FluidConduitConnectionConfig withSendColor(DyeColor sendColor) { + return new FluidConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel); + } + + public FluidConduitConnectionConfig withIsReceive(boolean isReceive) { + return new FluidConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel); + } + + public FluidConduitConnectionConfig withReceiveColor(DyeColor receiveColor) { + return new FluidConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel); + } + + public FluidConduitConnectionConfig withReceiveRedstoneControl(RedstoneControl receiveRedstoneControl) { + return new FluidConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel); + } + + public FluidConduitConnectionConfig withReceiveRedstoneChannel(DyeColor receiveRedstoneChannel) { + return new FluidConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel); + } + + @Override + public ConnectionConfigType type() { + return TYPE; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitData.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitData.java deleted file mode 100644 index 123937ea26..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitData.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.enderio.conduits.common.conduit.type.fluid; - -import com.enderio.conduits.api.ConduitData; -import com.enderio.conduits.api.ConduitDataType; -import com.enderio.conduits.common.init.ConduitTypes; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.Fluids; -import org.jetbrains.annotations.Nullable; - -import java.util.Objects; -import java.util.Optional; - -public class FluidConduitData implements ConduitData { - - public static MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group( - Codec.BOOL.fieldOf("should_reset").forGetter(i -> i.shouldReset), - BuiltInRegistries.FLUID.byNameCodec() - .optionalFieldOf("locked_fluid", Fluids.EMPTY) - .forGetter(i -> i.lockedFluid) - ).apply(instance, FluidConduitData::new) - ); - - public static StreamCodec STREAM_CODEC = StreamCodec.composite( - ByteBufCodecs.BOOL, - i -> i.shouldReset, - ByteBufCodecs.registry(Registries.FLUID), - i -> i.lockedFluid, - FluidConduitData::new - ); - - private Fluid lockedFluid = Fluids.EMPTY; - private boolean shouldReset = false; - - public FluidConduitData() { - } - - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - public FluidConduitData(boolean shouldReset, Fluid fluid) { - this.shouldReset = shouldReset; - this.lockedFluid = fluid; - } - - public Fluid lockedFluid() { - return lockedFluid; - } - - public void setLockedFluid(Fluid lockedFluid) { - this.lockedFluid = lockedFluid; - } - - public boolean shouldReset() { - return shouldReset; - } - - public void setShouldReset(boolean shouldReset) { - this.shouldReset = shouldReset; - } - - @Override - public FluidConduitData withClientChanges(FluidConduitData guiData) { - this.shouldReset = guiData.shouldReset; - - // TODO: Soon we will swap to records which will mean this will be a new instance. - // This API has been designed with this pending change in mind. - return this; - } - - @Override - public FluidConduitData deepCopy() { - return new FluidConduitData(shouldReset, lockedFluid); - } - - @Override - public ConduitDataType type() { - return ConduitTypes.Data.FLUID.get(); - } - - @Override - public int hashCode() { - return Objects.hash(shouldReset, lockedFluid); - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java new file mode 100644 index 0000000000..e151daedfb --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitNetworkContext.java @@ -0,0 +1,79 @@ +package com.enderio.conduits.common.conduit.type.fluid; + +import com.enderio.conduits.api.network.ConduitNetworkContext; +import com.enderio.conduits.api.network.ConduitNetworkContextType; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.Fluids; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// TODO: packet to clear the fluid lock. +public class FluidConduitNetworkContext implements ConduitNetworkContext { + + private static final Logger log = LoggerFactory.getLogger(FluidConduitNetworkContext.class); + public static Codec CODEC = RecordCodecBuilder.create(instance -> instance + .group(BuiltInRegistries.FLUID.byNameCodec() + .optionalFieldOf("locked_fluid", Fluids.EMPTY) + .forGetter(FluidConduitNetworkContext::lockedFluid)) + .apply(instance, FluidConduitNetworkContext::new)); + + public static ConduitNetworkContextType TYPE = new ConduitNetworkContextType<>(CODEC, + FluidConduitNetworkContext::new); + + private Fluid lockedFluid; + private Fluid lastLockedFluid = Fluids.EMPTY; + + public FluidConduitNetworkContext() { + this(Fluids.EMPTY); + } + + public FluidConduitNetworkContext(Fluid lockedFluid) { + this.lockedFluid = lockedFluid; + } + + public FluidConduitNetworkContext(Fluid lockedFluid, Fluid lastLockedFluid) { + this.lockedFluid = lockedFluid; + this.lastLockedFluid = lastLockedFluid; + } + + public Fluid lockedFluid() { + return lockedFluid; + } + + public Fluid lastLockedFluid() { + return lastLockedFluid; + } + + public void clearLastLockedFluid() { + this.lastLockedFluid = lockedFluid; + } + + public void setLockedFluid(Fluid lockedFluid) { + this.lastLockedFluid = this.lockedFluid; + this.lockedFluid = lockedFluid; + } + + @Override + public FluidConduitNetworkContext mergeWith(FluidConduitNetworkContext other) { + // Merge with the locked fluid, but set the last to empty so the ticker marks + // the nodes as dirty. + if (lockedFluid.equals(Fluids.EMPTY)) { + return new FluidConduitNetworkContext(other.lockedFluid, Fluids.EMPTY); + } + + return new FluidConduitNetworkContext(lockedFluid, Fluids.EMPTY); + } + + @Override + public FluidConduitNetworkContext copy() { + return new FluidConduitNetworkContext(lockedFluid); + } + + @Override + public ConduitNetworkContextType type() { + return TYPE; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java index 49c571e0b6..6770f56382 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/fluid/FluidConduitTicker.java @@ -2,60 +2,54 @@ import com.enderio.base.api.filter.FluidStackFilter; import com.enderio.conduits.api.ColoredRedstoneProvider; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNode; -import com.enderio.conduits.api.ticker.CapabilityAwareConduitTicker; -import com.enderio.conduits.common.components.ExtractionSpeedUpgrade; -import com.enderio.conduits.common.init.ConduitTypes; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; +import com.enderio.conduits.api.ticker.IOAwareConduitTicker; +import java.util.List; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.level.Level; import net.minecraft.world.level.material.FlowingFluid; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; -import net.neoforged.neoforge.capabilities.BlockCapability; import net.neoforged.neoforge.capabilities.Capabilities; import net.neoforged.neoforge.fluids.FluidStack; import net.neoforged.neoforge.fluids.FluidUtil; import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import org.jetbrains.annotations.Nullable; -import java.util.List; -import java.util.Optional; - -public class FluidConduitTicker extends CapabilityAwareConduitTicker { +public class FluidConduitTicker + extends IOAwareConduitTicker { - private int getScaledFluidRate(FluidConduit conduit, CapabilityConnection extractingConnection) { - // Adjust for tick rate. Always flow up so we are at minimum meeting the required rate. - int rate = (int)Math.ceil(conduit.transferRatePerTick() * (20.0 / conduit.graphTickRate())); - - // Apply speed upgrade - if (extractingConnection.upgrade() instanceof ExtractionSpeedUpgrade speedUpgrade) { - // TODO: Review scaling. - rate *= (int) Math.pow(2, speedUpgrade.tier()); - } - return rate; + private int getScaledFluidRate(FluidConduit conduit) { + // Adjust for tick rate. Always flow up so we are at minimum meeting the + // required rate. + return (int) Math.ceil(conduit.transferRatePerTick() * (20.0 / conduit.graphTickRate())); } - private int doFluidTransfer(FluidStack fluid, CapabilityConnection extract, List inserts) { - FluidStack extractedFluid = extract.capability().drain(fluid, IFluidHandler.FluidAction.SIMULATE); + private int doFluidTransfer(FluidStack fluid, Connection receiver, List senders) { + FluidStack extractedFluid = receiver.fluidHandler().drain(fluid, IFluidHandler.FluidAction.SIMULATE); if (extractedFluid.isEmpty()) { return fluid.getAmount(); } - if (extract.extractFilter() instanceof FluidStackFilter fluidStackFilter) { + if (receiver.extractFilter() instanceof FluidStackFilter fluidStackFilter) { if (!fluidStackFilter.test(extractedFluid)) { return fluid.getAmount(); } } - for (CapabilityConnection insert : inserts) { + for (Connection insert : senders) { if (insert.insertFilter() instanceof FluidStackFilter fluidStackFilter) { if (!fluidStackFilter.test(extractedFluid)) { continue; } } - FluidStack transferredFluid = FluidUtil.tryFluidTransfer(insert.capability(), extract.capability(), fluid, true); + FluidStack transferredFluid = FluidUtil.tryFluidTransfer(insert.fluidHandler(), receiver.fluidHandler(), + fluid, true); if (!transferredFluid.isEmpty()) { fluid.shrink(transferredFluid.getAmount()); @@ -70,48 +64,35 @@ private int doFluidTransfer(FluidStack fluid, CapabilityConnection extract, List } @Override - public void tickGraph( - ServerLevel level, - FluidConduit conduit, - List loadedNodes, - ConduitNetwork graph, - ColoredRedstoneProvider coloredRedstoneProvider) { - - boolean shouldReset = false; - for (var loadedNode : loadedNodes) { - FluidConduitData fluidExtendedData = loadedNode.getOrCreateData(ConduitTypes.Data.FLUID.get()); - if (fluidExtendedData.shouldReset()) { - shouldReset = true; - fluidExtendedData.setShouldReset(false); - } - } - - if (shouldReset) { - for (var loadedNode : loadedNodes) { - FluidConduitData fluidExtendedData = loadedNode.getOrCreateData(ConduitTypes.Data.FLUID.get()); - fluidExtendedData.setLockedFluid(Fluids.EMPTY); + public void tickGraph(ServerLevel level, FluidConduit conduit, ConduitNetwork graph, + ColoredRedstoneProvider coloredRedstoneProvider) { + super.tickGraph(level, conduit, graph, coloredRedstoneProvider); + + // Update if the network is now locked + var context = graph.getOrCreateContext(FluidConduitNetworkContext.TYPE); + if (!context.lockedFluid().equals(context.lastLockedFluid())) { + context.clearLastLockedFluid(); + for (var node : graph.getNodes()) { + if (node.isLoaded()) { + node.markDirty(); + } } } - super.tickGraph(level, conduit, loadedNodes, graph, coloredRedstoneProvider); } @Override - protected void tickCapabilityGraph( - ServerLevel level, - FluidConduit conduit, - List inserts, - List extracts, - ConduitNetwork graph, - ColoredRedstoneProvider coloredRedstoneProvider) { - - for (CapabilityConnection extract : extracts) { - IFluidHandler extractHandler = extract.capability(); - FluidConduitData fluidExtendedData = extract.node().getOrCreateData(ConduitTypes.Data.FLUID.get()); - - final int fluidRate = getScaledFluidRate(conduit, extract); - - if (!fluidExtendedData.lockedFluid().isSame(Fluids.EMPTY)) { - doFluidTransfer(new FluidStack(fluidExtendedData.lockedFluid(), fluidRate), extract, inserts); + protected void tickColoredGraph(ServerLevel level, FluidConduit conduit, List senders, + List receivers, DyeColor color, ConduitNetwork graph, + ColoredRedstoneProvider coloredRedstoneProvider) { + + final int fluidRate = getScaledFluidRate(conduit); + var context = graph.getOrCreateContext(FluidConduitNetworkContext.TYPE); + + for (Connection receiver : receivers) { + IFluidHandler extractHandler = receiver.fluidHandler(); + + if (!context.lockedFluid().isSame(Fluids.EMPTY)) { + doFluidTransfer(new FluidStack(context.lockedFluid(), fluidRate), receiver, senders); } else { int remaining = fluidRate; @@ -121,17 +102,15 @@ protected void tickCapabilityGraph( } Fluid fluid = extractHandler.getFluidInTank(i).getFluid(); - remaining = doFluidTransfer(new FluidStack(fluid, remaining), extract, inserts); + remaining = doFluidTransfer(new FluidStack(fluid, remaining), receiver, senders); if (!conduit.isMultiFluid() && remaining < fluidRate) { - for (ConduitNode node : graph.getNodes()) { - if (fluid instanceof FlowingFluid flowing) { - fluid = flowing.getSource(); - } - - node.getOrCreateData(ConduitTypes.Data.FLUID.get()).setLockedFluid(fluid); + if (fluid instanceof FlowingFluid flowing) { + fluid = flowing.getSource(); } + context.setLockedFluid(fluid); + break; } } @@ -140,7 +119,27 @@ protected void tickCapabilityGraph( } @Override - protected BlockCapability getCapability() { - return Capabilities.FluidHandler.BLOCK; + protected @Nullable FluidConduitTicker.Connection createConnection(Level level, ConduitNode node, Direction side) { + IFluidHandler fluidHandler = level.getCapability(Capabilities.FluidHandler.BLOCK, node.getPos().relative(side), + side.getOpposite()); + if (fluidHandler != null) { + return new Connection(node, side, node.getConnectionConfig(side, FluidConduitConnectionConfig.TYPE), + fluidHandler); + } + return null; + } + + protected static class Connection extends SimpleConnection { + private final IFluidHandler fluidHandler; + + public Connection(ConduitNode node, Direction side, FluidConduitConnectionConfig config, + IFluidHandler fluidHandler) { + super(node, side, config); + this.fluidHandler = fluidHandler; + } + + public IFluidHandler fluidHandler() { + return fluidHandler; + } } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java index 0c02099f0c..c8330da18e 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduit.java @@ -2,46 +2,46 @@ import com.enderio.base.api.filter.ItemStackFilter; import com.enderio.base.api.filter.ResourceFilter; +import com.enderio.base.api.misc.RedstoneControl; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitMenuData; import com.enderio.conduits.api.ConduitType; -import com.enderio.conduits.api.SlotType; -import com.enderio.conduits.api.upgrade.ConduitUpgrade; -import com.enderio.conduits.common.components.ExtractionSpeedUpgrade; +import com.enderio.conduits.api.bundle.ConduitBundleReader; +import com.enderio.conduits.api.bundle.SlotType; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.network.node.ConduitNode; +import com.enderio.conduits.api.network.node.legacy.ConduitDataAccessor; import com.enderio.conduits.common.init.ConduitLang; import com.enderio.conduits.common.init.ConduitTypes; import com.enderio.core.common.util.TooltipUtil; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.function.Consumer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Item; import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.items.IItemHandler; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.function.Consumer; +public record ItemConduit(ResourceLocation texture, Component description, int transferRatePerCycle, int graphTickRate) + implements Conduit { -public record ItemConduit( - ResourceLocation texture, - Component description, - int transferRatePerCycle, - int graphTickRate -) implements Conduit { - - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - builder -> builder - .group( - ResourceLocation.CODEC.fieldOf("texture").forGetter(ItemConduit::texture), - ComponentSerialization.CODEC.fieldOf("description").forGetter(ItemConduit::description), - // Using optionals in order to support the old conduit format. - Codec.INT.optionalFieldOf("transfer_rate", 4).forGetter(ItemConduit::transferRatePerCycle), - Codec.intRange(1, 20).optionalFieldOf("ticks_per_cycle", 20).forGetter(ItemConduit::graphTickRate) - ).apply(builder, ItemConduit::new) - ); - - private static final ConduitMenuData MENU_DATA = new ConduitMenuData.Simple(true, true, true, true, true, true); + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(builder -> builder + .group(ResourceLocation.CODEC.fieldOf("texture").forGetter(ItemConduit::texture), + ComponentSerialization.CODEC.fieldOf("description").forGetter(ItemConduit::description), + // Using optionals in order to support the old conduit format. + Codec.INT.optionalFieldOf("transfer_rate", 4).forGetter(ItemConduit::transferRatePerCycle), + Codec.intRange(1, 20).optionalFieldOf("ticks_per_cycle", 20).forGetter(ItemConduit::graphTickRate)) + .apply(builder, ItemConduit::new)); private static final ItemConduitTicker TICKER = new ItemConduitTicker(); @@ -56,13 +56,8 @@ public ItemConduitTicker getTicker() { } @Override - public ConduitMenuData getMenuData() { - return MENU_DATA; - } - - @Override - public boolean canApplyUpgrade(SlotType slotType, ConduitUpgrade conduitUpgrade) { - return conduitUpgrade instanceof ExtractionSpeedUpgrade; + public boolean hasMenu() { + return true; } @Override @@ -71,9 +66,12 @@ public boolean canApplyFilter(SlotType slotType, ResourceFilter resourceFilter) } @Override - public void addToTooltip(Item.TooltipContext pContext, Consumer pTooltipAdder, TooltipFlag pTooltipFlag) { - String calculatedTransferLimitFormatted = String.format("%,d", (int)Math.floor(transferRatePerCycle() * (20.0 / graphTickRate()))); - pTooltipAdder.accept(TooltipUtil.styledWithArgs(ConduitLang.ITEM_EFFECTIVE_RATE_TOOLTIP, calculatedTransferLimitFormatted)); + public void addToTooltip(Item.TooltipContext pContext, Consumer pTooltipAdder, + TooltipFlag pTooltipFlag) { + String calculatedTransferLimitFormatted = String.format("%,d", + (int) Math.floor(transferRatePerCycle() * (20.0 / graphTickRate()))); + pTooltipAdder.accept( + TooltipUtil.styledWithArgs(ConduitLang.ITEM_EFFECTIVE_RATE_TOOLTIP, calculatedTransferLimitFormatted)); if (pTooltipFlag.hasShiftDown()) { String transferLimitFormatted = String.format("%,d", transferRatePerCycle()); @@ -91,6 +89,65 @@ public boolean showDebugTooltip() { return true; } + @Override + public boolean canConnectToBlock(Level level, BlockPos conduitPos, Direction direction) { + IItemHandler capability = level.getCapability(Capabilities.ItemHandler.BLOCK, conduitPos.relative(direction), + direction.getOpposite()); + return capability != null; + } + + @Override + public ConnectionConfigType connectionConfigType() { + return ConduitTypes.ConnectionTypes.ITEM.get(); + } + + // TODO: Move conversions into the connection config type? + @Override + public ItemConduitConnectionConfig convertConnection(boolean isInsert, boolean isExtract, DyeColor inputChannel, + DyeColor outputChannel, RedstoneControl redstoneControl, DyeColor redstoneChannel) { + return new ItemConduitConnectionConfig(isInsert, inputChannel, isExtract, outputChannel, redstoneControl, + redstoneChannel, false, false, 0); + } + + @Override + public void copyLegacyData(ConduitNode node, ConduitDataAccessor legacyDataAccessor) { + var legacyData = legacyDataAccessor.getData(ConduitTypes.Data.ITEM.get()); + if (legacyData == null) { + return; + } + + // Copy connection config + for (Direction side : Direction.values()) { + if (node.isConnectedTo(side)) { + var oldSideConfig = legacyData.get(side); + var currentConfig = node.getConnectionConfig(side, ItemConduitConnectionConfig.TYPE); + + node.setConnectionConfig(side, + currentConfig.withIsRoundRobin(oldSideConfig.isRoundRobin) + .withIsSelfFeed(oldSideConfig.isSelfFeed) + .withPriority(oldSideConfig.priority)); + } + } + } + + @Override + @Nullable + public CompoundTag getExtraGuiData(ConduitBundleReader conduitBundle, ConduitNode node, Direction side) { + if (!node.isConnectedTo(side)) { + return null; + } + + var config = node.getConnectionConfig(side, connectionConfigType()); + if (!config.receiveRedstoneControl().isRedstoneSensitive()) { + return null; + } + + CompoundTag tag = new CompoundTag(); + tag.putBoolean("HasRedstoneSignal", node.hasRedstoneSignal(config.receiveRedstoneChannel())); + tag.putBoolean("HasRedstoneConduit", conduitBundle.hasConduitByType(ConduitTypes.REDSTONE.get())); + return tag; + } + @Override public int compareTo(@NotNull ItemConduit o) { double selfEffectiveSpeed = transferRatePerCycle() * (20.0 / graphTickRate()); diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java new file mode 100644 index 0000000000..0c9334edde --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitConnectionConfig.java @@ -0,0 +1,142 @@ +package com.enderio.conduits.common.conduit.type.item; + +import com.enderio.base.api.misc.RedstoneControl; +import com.enderio.base.api.network.MassiveStreamCodec; +import com.enderio.conduits.api.ConduitRedstoneSignalAware; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.connection.config.IOConnectionConfig; +import com.enderio.conduits.api.connection.config.RedstoneSensitiveConnectionConfig; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.netty.buffer.ByteBuf; +import java.util.List; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.DyeColor; + +public record ItemConduitConnectionConfig(boolean isSend, DyeColor sendColor, boolean isReceive, DyeColor receiveColor, + RedstoneControl receiveRedstoneControl, DyeColor receiveRedstoneChannel, boolean isRoundRobin, + boolean isSelfFeed, int priority) implements IOConnectionConfig, RedstoneSensitiveConnectionConfig { + + public static ItemConduitConnectionConfig DEFAULT = new ItemConduitConnectionConfig(false, DyeColor.GREEN, true, + DyeColor.GREEN, RedstoneControl.NEVER_ACTIVE, DyeColor.RED, false, false, 0); + + public static MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance + .group(Codec.BOOL.fieldOf("is_send").forGetter(ItemConduitConnectionConfig::isSend), + DyeColor.CODEC.fieldOf("send_color").forGetter(ItemConduitConnectionConfig::sendColor), + Codec.BOOL.fieldOf("is_receive").forGetter(ItemConduitConnectionConfig::isReceive), + DyeColor.CODEC.fieldOf("receive_color").forGetter(ItemConduitConnectionConfig::receiveColor), + RedstoneControl.CODEC.fieldOf("receive_redstone_control") + .forGetter(ItemConduitConnectionConfig::receiveRedstoneControl), + DyeColor.CODEC.fieldOf("receive_redstone_channel") + .forGetter(ItemConduitConnectionConfig::receiveRedstoneChannel), + Codec.BOOL.fieldOf("is_round_robin").forGetter(ItemConduitConnectionConfig::isRoundRobin), + Codec.BOOL.fieldOf("is_self_feed").forGetter(ItemConduitConnectionConfig::isSelfFeed), + Codec.INT.fieldOf("priority").forGetter(ItemConduitConnectionConfig::priority)) + .apply(instance, ItemConduitConnectionConfig::new)); + + public static StreamCodec STREAM_CODEC = MassiveStreamCodec.composite( + ByteBufCodecs.BOOL, ItemConduitConnectionConfig::isSend, DyeColor.STREAM_CODEC, + ItemConduitConnectionConfig::sendColor, ByteBufCodecs.BOOL, ItemConduitConnectionConfig::isReceive, + DyeColor.STREAM_CODEC, ItemConduitConnectionConfig::receiveColor, RedstoneControl.STREAM_CODEC, + ItemConduitConnectionConfig::receiveRedstoneControl, DyeColor.STREAM_CODEC, + ItemConduitConnectionConfig::receiveRedstoneChannel, ByteBufCodecs.BOOL, + ItemConduitConnectionConfig::isRoundRobin, ByteBufCodecs.BOOL, ItemConduitConnectionConfig::isSelfFeed, + ByteBufCodecs.INT, ItemConduitConnectionConfig::priority, ItemConduitConnectionConfig::new); + + public static ConnectionConfigType TYPE = new ConnectionConfigType<>(CODEC, + STREAM_CODEC.cast(), () -> DEFAULT); + + @Override + public ConnectionConfig reconnected() { + return new ItemConduitConnectionConfig(false, sendColor, true, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + @Override + public ConnectionConfig disconnected() { + return new ItemConduitConnectionConfig(false, sendColor, false, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + @Override + public boolean canSend(ConduitRedstoneSignalAware signalAware) { + // TODO: sendRedstoneControl + return isSend(); + } + + @Override + public boolean canReceive(ConduitRedstoneSignalAware signalAware) { + if (!isReceive()) { + return false; + } + + if (receiveRedstoneControl.isRedstoneSensitive()) { + return receiveRedstoneControl.isActive(signalAware.hasRedstoneSignal(receiveRedstoneChannel)); + } else { + return true; + } + } + + @Override + public List getRedstoneSignalColors() { + if (receiveRedstoneControl.isRedstoneSensitive()) { + return List.of(receiveRedstoneChannel); + } + + return List.of(); + } + + // Generate with methods for each field + public ItemConduitConnectionConfig withIsSend(boolean isSend) { + return new ItemConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + public ItemConduitConnectionConfig withSendColor(DyeColor sendColor) { + return new ItemConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + public ItemConduitConnectionConfig withIsReceive(boolean isReceive) { + return new ItemConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + public ItemConduitConnectionConfig withReceiveColor(DyeColor receiveColor) { + return new ItemConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + public ItemConduitConnectionConfig withReceiveRedstoneControl(RedstoneControl receiveRedstoneControl) { + return new ItemConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + public ItemConduitConnectionConfig withReceiveRedstoneChannel(DyeColor receiveRedstoneChannel) { + return new ItemConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + public ItemConduitConnectionConfig withIsRoundRobin(boolean isRoundRobin) { + return new ItemConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + public ItemConduitConnectionConfig withIsSelfFeed(boolean isSelfFeed) { + return new ItemConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + public ItemConduitConnectionConfig withPriority(int priority) { + return new ItemConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, receiveRedstoneControl, + receiveRedstoneChannel, isRoundRobin, isSelfFeed, priority); + } + + @Override + public ConnectionConfigType type() { + return TYPE; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitNodeData.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitNodeData.java new file mode 100644 index 0000000000..64c06aa1e0 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitNodeData.java @@ -0,0 +1,47 @@ +package com.enderio.conduits.common.conduit.type.item; + +import com.enderio.conduits.api.network.node.NodeData; +import com.enderio.conduits.api.network.node.NodeDataType; +import com.enderio.conduits.common.init.ConduitTypes; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.core.Direction; + +/** + * Stores round-robin indexes for this node. + */ +public final class ItemConduitNodeData implements NodeData { + + public static MapCodec CODEC = RecordCodecBuilder + .mapCodec(instance -> instance.group(Codec.unboundedMap(Direction.CODEC, Codec.INT) + .fieldOf("round_robin_indexes") + .forGetter(i -> i.roundRobinIndexes)).apply(instance, ItemConduitNodeData::new)); + + public static NodeDataType TYPE = new NodeDataType<>(ItemConduitNodeData.CODEC, + ItemConduitNodeData::new); + private final Map roundRobinIndexes; + + public ItemConduitNodeData() { + this(Map.of()); + } + + public ItemConduitNodeData(Map roundRobinIndexes) { + this.roundRobinIndexes = new HashMap<>(roundRobinIndexes); + } + + @Override + public NodeDataType type() { + return ConduitTypes.NodeData.ITEM.get(); + } + + public int getIndex(Direction side) { + return roundRobinIndexes.getOrDefault(side, 0); + } + + public void setIndex(Direction side, int index) { + roundRobinIndexes.put(side, index); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java index 774284e3e7..bfa255967c 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/item/ItemConduitTicker.java @@ -2,34 +2,37 @@ import com.enderio.base.api.filter.ItemStackFilter; import com.enderio.conduits.api.ColoredRedstoneProvider; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ticker.CapabilityAwareConduitTicker; -import com.enderio.conduits.common.components.ExtractionSpeedUpgrade; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; +import com.enderio.conduits.api.ticker.IOAwareConduitTicker; import com.enderio.conduits.common.init.ConduitTypes; +import java.util.Comparator; import java.util.List; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.capabilities.BlockCapability; +import net.minecraft.world.level.Level; import net.neoforged.neoforge.capabilities.Capabilities; import net.neoforged.neoforge.items.IItemHandler; import net.neoforged.neoforge.items.ItemHandlerHelper; +import org.jetbrains.annotations.Nullable; -public class ItemConduitTicker extends CapabilityAwareConduitTicker { +public class ItemConduitTicker + extends IOAwareConduitTicker { @Override - protected void tickCapabilityGraph(ServerLevel level, ItemConduit conduit, List inserts, - List extracts, ConduitNetwork graph, + protected void tickColoredGraph(ServerLevel level, ItemConduit conduit, List senders, + List receivers, DyeColor color, ConduitNetwork graph, ColoredRedstoneProvider coloredRedstoneProvider) { - toNextExtract: for (CapabilityConnection extract : extracts) { - IItemHandler extractHandler = extract.capability(); + toNextExtract: for (Connection extract : receivers) { + ItemConduitNodeData nodeData = extract.node().getOrCreateNodeData(ConduitTypes.NodeData.ITEM.get()); + + IItemHandler extractHandler = extract.itemHandler(); int extracted = 0; int speed = conduit.transferRatePerCycle(); - if (extract.upgrade() instanceof ExtractionSpeedUpgrade speedUpgrade) { - speed *= (int) Math.pow(2, speedUpgrade.tier()); - } nextItem: for (int i = 0; i < extractHandler.getSlots(); i++) { ItemStack extractedItem = extractHandler.extractItem(i, speed - extracted, true); @@ -43,24 +46,23 @@ protected void tickCapabilityGraph(ServerLevel level, ItemConduit conduit, List< } } - ItemConduitData.ItemSidedData sidedExtractData = extract.node() - .getOrCreateData(ConduitTypes.Data.ITEM.get()) - .compute(extract.direction()); + var connectionConfig = extract.node() + .getConnectionConfig(extract.side(), ConduitTypes.ConnectionTypes.ITEM.get()); - if (sidedExtractData.isRoundRobin) { - if (inserts.size() <= sidedExtractData.rotatingIndex) { - sidedExtractData.rotatingIndex = 0; + int startingIndex = 0; + if (connectionConfig.isRoundRobin()) { + startingIndex = nodeData.getIndex(extract.side()); + if (senders.size() <= startingIndex) { + startingIndex = 0; } - } else { - sidedExtractData.rotatingIndex = 0; } - for (int j = sidedExtractData.rotatingIndex; j < sidedExtractData.rotatingIndex + inserts.size(); j++) { - int insertIndex = j % inserts.size(); - CapabilityConnection insert = inserts.get(insertIndex); + for (int j = startingIndex; j < startingIndex + senders.size(); j++) { + int insertIndex = j % senders.size(); + Connection insert = senders.get(insertIndex); - if (!sidedExtractData.isSelfFeed && extract.direction() == insert.direction() - && extract.pos() == insert.pos()) { + if (!connectionConfig.isSelfFeed() && extract.side() == insert.side() + && extract.node().getPos().equals(insert.node().getPos())) { continue; } @@ -70,15 +72,15 @@ protected void tickCapabilityGraph(ServerLevel level, ItemConduit conduit, List< } } - ItemStack notInserted = ItemHandlerHelper.insertItem(insert.capability(), extractedItem, false); + ItemStack notInserted = ItemHandlerHelper.insertItem(insert.itemHandler, extractedItem, false); int successfullyInserted = extractedItem.getCount() - notInserted.getCount(); if (successfullyInserted > 0) { extracted += successfullyInserted; extractHandler.extractItem(i, successfullyInserted, false); if (extracted >= speed || isEmpty(extractHandler, i + 1)) { - if (sidedExtractData.isRoundRobin) { - sidedExtractData.rotatingIndex = insertIndex + 1; + if (connectionConfig.isRoundRobin()) { + nodeData.setIndex(extract.side(), insertIndex + 1); } continue toNextExtract; } else { @@ -101,7 +103,34 @@ private boolean isEmpty(IItemHandler itemHandler, int afterIndex) { } @Override - protected BlockCapability getCapability() { - return Capabilities.ItemHandler.BLOCK; + protected void preProcessReceivers(List receivers) { + receivers.sort(Comparator.comparingInt((Connection n) -> n.config().priority()).reversed()); + } + + @Override + @Nullable + protected Connection createConnection(Level level, ConduitNode node, Direction side) { + IItemHandler itemHandler = level.getCapability(Capabilities.ItemHandler.BLOCK, node.getPos().relative(side), + side.getOpposite()); + if (itemHandler != null) { + return new Connection(node, side, node.getConnectionConfig(side, ItemConduitConnectionConfig.TYPE), + itemHandler); + } + + return null; + } + + protected static class Connection extends SimpleConnection { + private final IItemHandler itemHandler; + + public Connection(ConduitNode node, Direction side, ItemConduitConnectionConfig config, + IItemHandler itemHandler) { + super(node, side, config); + this.itemHandler = itemHandler; + } + + public IItemHandler itemHandler() { + return itemHandler; + } } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java index 9b60c4c034..d31684a18b 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduit.java @@ -1,37 +1,42 @@ package com.enderio.conduits.common.conduit.type.redstone; import com.enderio.base.api.filter.ResourceFilter; +import com.enderio.base.api.misc.RedstoneControl; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitMenuData; -import com.enderio.conduits.api.ConduitNode; import com.enderio.conduits.api.ConduitType; -import com.enderio.conduits.api.SlotType; +import com.enderio.conduits.api.bundle.ConduitBundleReader; +import com.enderio.conduits.api.bundle.SlotType; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.network.node.ConduitNode; import com.enderio.conduits.common.init.ConduitTypes; import com.enderio.conduits.common.redstone.RedstoneExtractFilter; import com.enderio.conduits.common.redstone.RedstoneInsertFilter; +import com.enderio.conduits.common.tag.ConduitTags; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import java.util.Set; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public record RedstoneConduit( - ResourceLocation texture, - ResourceLocation activeTexture, - Component description -) implements Conduit { +public record RedstoneConduit(ResourceLocation texture, ResourceLocation activeTexture, Component description) + implements Conduit { - public static MapCodec CODEC = RecordCodecBuilder.mapCodec( - builder -> builder.group( - ResourceLocation.CODEC.fieldOf("texture").forGetter(RedstoneConduit::texture), - ResourceLocation.CODEC.fieldOf("active_texture").forGetter(RedstoneConduit::activeTexture), - ComponentSerialization.CODEC.fieldOf("description").forGetter(RedstoneConduit::description) - ).apply(builder, RedstoneConduit::new) - ); + public static MapCodec CODEC = RecordCodecBuilder.mapCodec(builder -> builder + .group(ResourceLocation.CODEC.fieldOf("texture").forGetter(RedstoneConduit::texture), + ResourceLocation.CODEC.fieldOf("active_texture").forGetter(RedstoneConduit::activeTexture), + ComponentSerialization.CODEC.fieldOf("description").forGetter(RedstoneConduit::description)) + .apply(builder, RedstoneConduit::new)); private static final RedstoneConduitTicker TICKER = new RedstoneConduitTicker(); - private static final ConduitMenuData MENU_DATA = new ConduitMenuData.Simple(true, true, false, true, true, false); @Override public int graphTickRate() { @@ -49,23 +54,74 @@ public RedstoneConduitTicker getTicker() { } @Override - public ConduitMenuData getMenuData() { - return MENU_DATA; + public boolean hasMenu() { + return true; } @Override public boolean canApplyFilter(SlotType slotType, ResourceFilter resourceFilter) { return switch (slotType) { - case FILTER_EXTRACT -> resourceFilter instanceof RedstoneExtractFilter; - case FILTER_INSERT -> resourceFilter instanceof RedstoneInsertFilter; - default -> false; + case FILTER_EXTRACT -> resourceFilter instanceof RedstoneExtractFilter; + case FILTER_INSERT -> resourceFilter instanceof RedstoneInsertFilter; + default -> false; }; } @Override - public ResourceLocation getTexture(ConduitNode node) { - RedstoneConduitData data = node.getData(ConduitTypes.Data.REDSTONE.get()); - return data != null && data.isActive() ? activeTexture() : texture(); + public ResourceLocation getTexture(@Nullable CompoundTag extraWorldData) { + if (extraWorldData != null) { + return extraWorldData.contains("IsActive") && extraWorldData.getBoolean("IsActive") ? activeTexture() + : texture(); + } + + return texture(); + } + + @Override + public void onConnectionsUpdated(ConduitNode node, Level level, BlockPos pos, Set connectedSides) { + node.markDirty(); + } + + @Override + public boolean canConnectToBlock(Level level, BlockPos conduitPos, Direction direction) { + BlockPos neighbor = conduitPos.relative(direction); + BlockState blockState = level.getBlockState(neighbor); + return blockState.is(ConduitTags.Blocks.REDSTONE_CONNECTABLE) + || blockState.canRedstoneConnectTo(level, neighbor, direction.getOpposite()); + } + + @Override + public boolean canForceConnectToBlock(Level level, BlockPos conduitPos, Direction direction) { + BlockPos neighbor = conduitPos.relative(direction); + BlockState blockState = level.getBlockState(neighbor); + return !blockState.isAir(); + } + + @Override + public ConnectionConfigType connectionConfigType() { + return RedstoneConduitConnectionConfig.TYPE; + } + + @Override + public RedstoneConduitConnectionConfig convertConnection(boolean isInsert, boolean isExtract, DyeColor inputChannel, + DyeColor outputChannel, RedstoneControl redstoneControl, DyeColor redstoneChannel) { + return new RedstoneConduitConnectionConfig(isInsert, inputChannel, isExtract, outputChannel, false); + } + + @Override + public CompoundTag getExtraWorldData(ConduitBundleReader conduitBundle, ConduitNode node) { + var tag = new CompoundTag(); + + if (node.getNetwork() == null) { + return tag; + } + + var context = node.getNetwork().getContext(RedstoneConduitNetworkContext.TYPE); + if (context != null) { + tag.putBoolean("IsActive", context.isActive()); + } + + return tag; } @Override diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java new file mode 100644 index 0000000000..979805f93b --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitConnectionConfig.java @@ -0,0 +1,73 @@ +package com.enderio.conduits.common.conduit.type.redstone; + +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.connection.config.IOConnectionConfig; +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.DyeColor; + +public record RedstoneConduitConnectionConfig(boolean isSend, DyeColor sendColor, boolean isReceive, + DyeColor receiveColor, boolean isStrongOutputSignal) implements IOConnectionConfig { + + public static RedstoneConduitConnectionConfig DEFAULT = new RedstoneConduitConnectionConfig(false, DyeColor.GREEN, + true, DyeColor.RED, false); + + public static MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance + .group(Codec.BOOL.fieldOf("is_send").forGetter(RedstoneConduitConnectionConfig::isSend), + DyeColor.CODEC.fieldOf("send_color").forGetter(RedstoneConduitConnectionConfig::sendColor), + Codec.BOOL.fieldOf("is_receive").forGetter(RedstoneConduitConnectionConfig::isReceive), + DyeColor.CODEC.fieldOf("receive_color").forGetter(RedstoneConduitConnectionConfig::receiveColor), + Codec.BOOL.fieldOf("is_strong_output_signal") + .forGetter(RedstoneConduitConnectionConfig::isStrongOutputSignal)) + .apply(instance, RedstoneConduitConnectionConfig::new)); + + public static StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.BOOL, RedstoneConduitConnectionConfig::isSend, DyeColor.STREAM_CODEC, + RedstoneConduitConnectionConfig::sendColor, ByteBufCodecs.BOOL, RedstoneConduitConnectionConfig::isReceive, + DyeColor.STREAM_CODEC, RedstoneConduitConnectionConfig::receiveColor, ByteBufCodecs.BOOL, + RedstoneConduitConnectionConfig::isStrongOutputSignal, RedstoneConduitConnectionConfig::new); + + public static ConnectionConfigType TYPE = new ConnectionConfigType<>(CODEC, + STREAM_CODEC.cast(), () -> DEFAULT); + + @Override + public ConnectionConfig reconnected() { + return new RedstoneConduitConnectionConfig(DEFAULT.isSend, sendColor, DEFAULT.isReceive, receiveColor, + isStrongOutputSignal); + } + + @Override + public ConnectionConfig disconnected() { + return new RedstoneConduitConnectionConfig(false, sendColor, false, receiveColor, isStrongOutputSignal); + } + + public RedstoneConduitConnectionConfig withIsSend(boolean isSend) { + return new RedstoneConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, isStrongOutputSignal); + } + + public RedstoneConduitConnectionConfig withSendColor(DyeColor sendColor) { + return new RedstoneConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, isStrongOutputSignal); + } + + public RedstoneConduitConnectionConfig withIsReceive(boolean isReceive) { + return new RedstoneConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, isStrongOutputSignal); + } + + public RedstoneConduitConnectionConfig withReceiveColor(DyeColor receiveColor) { + return new RedstoneConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, isStrongOutputSignal); + } + + public RedstoneConduitConnectionConfig withIsStrongOutputSignal(boolean isStrongOutputSignal) { + return new RedstoneConduitConnectionConfig(isSend, sendColor, isReceive, receiveColor, isStrongOutputSignal); + } + + @Override + public ConnectionConfigType type() { + return TYPE; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitData.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitData.java deleted file mode 100644 index ed5bed66bb..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitData.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.enderio.conduits.common.conduit.type.redstone; - -import com.enderio.conduits.api.ConduitData; -import com.enderio.conduits.api.ConduitDataType; -import com.enderio.conduits.common.init.ConduitTypes; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.world.item.DyeColor; - -import java.util.EnumMap; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -public class RedstoneConduitData implements ConduitData { - - public static MapCodec CODEC = RecordCodecBuilder.mapCodec( - instance -> instance.group( - Codec.BOOL.fieldOf("is_active").forGetter(i -> i.isActive), - Codec.unboundedMap(DyeColor.CODEC, Codec.INT).fieldOf("active_colors").forGetter(i -> i.activeColors) - ).apply(instance, RedstoneConduitData::new) - ); - - public static StreamCodec STREAM_CODEC = StreamCodec.composite( - ByteBufCodecs.BOOL, - r -> r.isActive, - ByteBufCodecs.map(HashMap::new, DyeColor.STREAM_CODEC, ByteBufCodecs.INT), - r -> r.activeColors, - RedstoneConduitData::new - ); - - private boolean isActive = false; - private final EnumMap activeColors = new EnumMap<>(DyeColor.class); - - public RedstoneConduitData() { - } - - private RedstoneConduitData(boolean isActive, Map activeColors) { - this.isActive = isActive; - this.activeColors.putAll(activeColors); - } - - @Override - public ConduitDataType type() { - return ConduitTypes.Data.REDSTONE.get(); - } - - public boolean isActive() { - return isActive; - } - - public boolean isActive(DyeColor color) { - return activeColors.containsKey(color); - } - - public int getSignal(DyeColor color) { - return activeColors.getOrDefault(color, 0); - } - - public void clearActive() { - activeColors.clear(); - isActive = false; - } - - public void setActiveColor(DyeColor color, int signal) { - if (activeColors.containsKey(color)) { - return; - } - - isActive = true; - activeColors.put(color, signal); - } - - @Override - public RedstoneConduitData deepCopy() { - return new RedstoneConduitData(isActive, new EnumMap<>(activeColors)); - } - - @Override - public int hashCode() { - return Objects.hash(isActive, activeColors); - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java new file mode 100644 index 0000000000..5b13f6cdaf --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitNetworkContext.java @@ -0,0 +1,95 @@ +package com.enderio.conduits.common.conduit.type.redstone; + +import com.enderio.conduits.api.network.ConduitNetworkContext; +import com.enderio.conduits.api.network.ConduitNetworkContextType; +import java.util.HashMap; +import java.util.Map; +import net.minecraft.world.item.DyeColor; + +public class RedstoneConduitNetworkContext implements ConduitNetworkContext { + + // Redstone context is not saved as it is recalculated every graph tick if the + // network is active. + public static ConduitNetworkContextType TYPE = new ConduitNetworkContextType<>(null, + RedstoneConduitNetworkContext::new); + + private boolean isActive; + private Map previousChannelSignals = new HashMap<>(); + private Map channelSignals = new HashMap<>(); + + private NewNetworkDelay newNetworkDelay = NewNetworkDelay.NEW; + + public RedstoneConduitNetworkContext() { + } + + private RedstoneConduitNetworkContext(boolean isActive, HashMap channelSignals) { + this.isActive = isActive; + this.channelSignals = channelSignals; + } + + public boolean isNew() { + return newNetworkDelay != NewNetworkDelay.OLD; + } + + public boolean isActive() { + return isActive; + } + + public boolean isActive(DyeColor color) { + return channelSignals.containsKey(color); + } + + public int getSignal(DyeColor color) { + return channelSignals.getOrDefault(color, 0); + } + + // Used for change detection in the ticker. + public int getSignalLastTick(DyeColor color) { + return previousChannelSignals.getOrDefault(color, 0); + } + + public void nextTick() { + if (newNetworkDelay == NewNetworkDelay.NEW) { + newNetworkDelay = NewNetworkDelay.NEW_DECAY; + } else if (newNetworkDelay == NewNetworkDelay.NEW_DECAY) { + newNetworkDelay = NewNetworkDelay.OLD; + } + + // Save for change detection + for (DyeColor color : DyeColor.values()) { + previousChannelSignals.put(color, getSignal(color)); + } + + channelSignals.clear(); + isActive = false; + } + + public void setSignal(DyeColor color, int signal) { + if (getSignal(color) < signal) { + channelSignals.put(color, signal); + } + + isActive = channelSignals.values().stream().anyMatch(i -> i > 0); + } + + @Override + public RedstoneConduitNetworkContext mergeWith(RedstoneConduitNetworkContext other) { + return copy(); + } + + @Override + public RedstoneConduitNetworkContext copy() { + return new RedstoneConduitNetworkContext(isActive, new HashMap<>(channelSignals)); + } + + @Override + public ConduitNetworkContextType type() { + return TYPE; + } + + // Because the context is cleared before the graph ticks, we need to represent + // "newness" as three states. + private enum NewNetworkDelay { + NEW, NEW_DECAY, OLD + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java index 3c0add1e9b..cac5b16af5 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/type/redstone/RedstoneConduitTicker.java @@ -1,87 +1,88 @@ package com.enderio.conduits.common.conduit.type.redstone; import com.enderio.conduits.api.ColoredRedstoneProvider; -import com.enderio.conduits.api.ConduitNetwork; -import com.enderio.conduits.api.ConduitNode; +import com.enderio.conduits.api.network.ConduitNetwork; +import com.enderio.conduits.api.network.node.ConduitNode; import com.enderio.conduits.api.ticker.IOAwareConduitTicker; import com.enderio.conduits.common.init.ConduitBlocks; -import com.enderio.conduits.common.init.ConduitTypes; import com.enderio.conduits.common.redstone.RedstoneExtractFilter; -import com.enderio.conduits.common.tag.ConduitTags; -import java.util.Collection; -import java.util.EnumMap; import java.util.List; -import java.util.Map; -import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.DyeColor; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.Nullable; -public class RedstoneConduitTicker implements IOAwareConduitTicker { - - private final Map activeColors = new EnumMap<>(DyeColor.class); - - @Override - public boolean canConnectTo(Level level, BlockPos conduitPos, Direction direction) { - BlockPos neighbor = conduitPos.relative(direction); - BlockState blockState = level.getBlockState(neighbor); - return blockState.is(ConduitTags.Blocks.REDSTONE_CONNECTABLE) - || blockState.canRedstoneConnectTo(level, neighbor, direction); - } - - @Override - public boolean canForceConnectTo(Level level, BlockPos conduitPos, Direction direction) { - BlockPos neighbor = conduitPos.relative(direction); - BlockState blockState = level.getBlockState(neighbor); - return !blockState.isAir(); - } +public class RedstoneConduitTicker extends + IOAwareConduitTicker { @Override public void tickGraph(ServerLevel level, RedstoneConduit conduit, ConduitNetwork graph, ColoredRedstoneProvider coloredRedstoneProvider) { - Collection nodeIdentifiers = graph.getNodes(); + var context = graph.getOrCreateContext(RedstoneConduitNetworkContext.TYPE); + boolean isActiveBeforeTick = context.isActive(); + context.nextTick(); - activeColors.clear(); - tickGraph(level, conduit, nodeIdentifiers.stream().filter(node -> isLoaded(level, node.getPos())).toList(), - graph, coloredRedstoneProvider); + super.tickGraph(level, conduit, graph, coloredRedstoneProvider); - for (var nodeIdentifier : nodeIdentifiers) { - RedstoneConduitData data = nodeIdentifier.getOrCreateData(ConduitTypes.Data.REDSTONE.get()); - data.clearActive(); - for (var entry : activeColors.entrySet()) { - data.setActiveColor(entry.getKey(), entry.getValue()); + // If active changed -or- this graph is fresh, nodes need to be synced. + if (context.isNew() || context.isActive() != isActiveBeforeTick) { + for (var node : graph.getNodes()) { + if (node.isLoaded()) { + node.markDirty(); + } } } } @Override - public void tickColoredGraph(ServerLevel level, RedstoneConduit conduit, List inserts, - List extracts, DyeColor color, ConduitNetwork graph, + protected void tickColoredGraph(ServerLevel level, RedstoneConduit conduit, List senders, + List receivers, DyeColor color, ConduitNetwork graph, ColoredRedstoneProvider coloredRedstoneProvider) { - for (Connection extract : extracts) { + RedstoneConduitNetworkContext networkContext = graph.getOrCreateContext(RedstoneConduitNetworkContext.TYPE); + + for (Connection receiver : receivers) { int signal; - if (extract.extractFilter() instanceof RedstoneExtractFilter filter) { - signal = filter.getInputSignal(level, extract.move(), extract.direction()); + if (receiver.extractFilter() instanceof RedstoneExtractFilter filter) { + signal = filter.getInputSignal(level, receiver.neighborPos(), receiver.neighborSide()); } else { - signal = level.getSignal(extract.move(), extract.direction()); + signal = level.getSignal(receiver.neighborPos(), receiver.neighborSide()); } if (signal > 0) { - activeColors.put(color, Math.max(activeColors.getOrDefault(color, 0), signal)); + networkContext.setSignal(color, signal); } } - for (Connection insert : inserts) { - level.neighborChanged(insert.move(), ConduitBlocks.CONDUIT.get(), insert.pos()); + // Only update neighbours if this is a new context or the signal strength + // changed this time. + if (networkContext.isNew() || networkContext.getSignal(color) != networkContext.getSignalLastTick(color)) { + for (Connection sender : senders) { + level.updateNeighborsAt(sender.pos(), ConduitBlocks.CONDUIT.get()); + + // Update blocks connected to strong signals too. + if (sender.config().isStrongOutputSignal()) { + level.updateNeighborsAt(sender.neighborPos(), ConduitBlocks.CONDUIT.get()); + } + } } } @Override - public boolean shouldSkipColor(List extractList, List insertList) { - return extractList.isEmpty() && insertList.isEmpty(); // Only skip if no one uses the channel + protected boolean shouldSkipColor(List senders, List receivers) { + return senders.isEmpty() && receivers.isEmpty(); + } + + @Override + protected @Nullable Connection createConnection(Level level, ConduitNode node, Direction side) { + return new Connection(node, side, node.getConnectionConfig(side, RedstoneConduitConnectionConfig.TYPE)); + } + + protected static class Connection extends SimpleConnection { + public Connection(ConduitNode node, Direction side, RedstoneConduitConnectionConfig config) { + super(node, side, config); + } } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/upgrade/SpeedUpgradeItem.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/upgrade/SpeedUpgradeItem.java deleted file mode 100644 index a332670444..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/upgrade/SpeedUpgradeItem.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.enderio.conduits.common.conduit.upgrade; - -import com.enderio.conduits.api.upgrade.ConduitUpgrade; -import com.enderio.conduits.common.components.ExtractionSpeedUpgrade; -import com.enderio.conduits.common.init.ConduitComponents; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.capabilities.ICapabilityProvider; - -public class SpeedUpgradeItem extends Item { - - public static final ICapabilityProvider CAPABILITY_PROVIDER - = (stack, v) -> new ExtractionSpeedUpgrade(ConduitComponents.EXTRACTION_SPEED_UPGRADE_TIER, stack); - - public SpeedUpgradeItem(Properties pProperties) { - super(pProperties); - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/upgrade/package-info.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/upgrade/package-info.java deleted file mode 100644 index 22a89f8f7b..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/conduit/upgrade/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@javax.annotation.ParametersAreNonnullByDefault -@net.minecraft.MethodsReturnNonnullByDefault - -package com.enderio.conduits.common.conduit.upgrade; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitBlockEntities.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitBlockEntities.java index 27e4a9a3b1..9e5ead741a 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitBlockEntities.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitBlockEntities.java @@ -1,36 +1,35 @@ package com.enderio.conduits.common.init; -import com.enderio.conduits.api.EnderIOConduitsRegistries; import com.enderio.conduits.EnderIOConduits; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity; +import com.enderio.conduits.api.EnderIOConduitsRegistries; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlockEntity; import com.enderio.regilite.holder.RegiliteBlockEntity; import com.enderio.regilite.registry.BlockEntityRegistry; -import net.minecraft.core.Direction; import net.neoforged.bus.api.IEventBus; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.capabilities.BlockCapability; import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; -import java.util.Set; -import java.util.stream.Collectors; - @EventBusSubscriber(modid = EnderIOConduits.MODULE_MOD_ID, bus = EventBusSubscriber.Bus.MOD) public class ConduitBlockEntities { private static final BlockEntityRegistry BLOCK_ENTITY_REGISTRY = EnderIOConduits.REGILITE.blockEntityRegistry(); public static final RegiliteBlockEntity CONDUIT = BLOCK_ENTITY_REGISTRY - .registerBlockEntity("conduit", ConduitBundleBlockEntity::new, ConduitBlocks.CONDUIT); + .registerBlockEntity("conduit", ConduitBundleBlockEntity::new, ConduitBlocks.CONDUIT); @SubscribeEvent public static void registerConduitCapabilities(RegisterCapabilitiesEvent event) { - EnderIOConduitsRegistries.CONDUIT_TYPE.entrySet().stream() - .flatMap(e -> e.getValue().exposedCapabilities().stream()) - .forEach(e -> registerConduitCapability(event, e)); + EnderIOConduitsRegistries.CONDUIT_TYPE.entrySet() + .stream() + .flatMap(e -> e.getValue().exposedCapabilities().stream()) + .forEach(e -> registerConduitCapability(event, e)); } - private static void registerConduitCapability(RegisterCapabilitiesEvent event, BlockCapability capability) { - event.registerBlockEntity(capability, CONDUIT.get(), ConduitBundleBlockEntity.createConduitCap(capability)); + private static void registerConduitCapability(RegisterCapabilitiesEvent event, + BlockCapability capability) { + event.registerBlockEntity(capability, CONDUIT.get(), + ConduitBundleBlockEntity.createCapabilityProvider(capability)); } public static void register(IEventBus bus) { diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitBlocks.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitBlocks.java index e6f76603ef..29cf2b2644 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitBlocks.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitBlocks.java @@ -4,7 +4,7 @@ import com.enderio.conduits.EnderIOConduits; import com.enderio.conduits.client.ConduitFacadeColor; import com.enderio.conduits.common.conduit.ConduitBlockItem; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlock; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlock; import com.enderio.conduits.data.model.ConduitBlockState; import com.enderio.regilite.holder.RegiliteBlock; import com.enderio.regilite.registry.BlockRegistry; diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitComponents.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitComponents.java index 5696d976da..fb547f00bf 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitComponents.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitComponents.java @@ -21,7 +21,7 @@ public class ConduitComponents { private static final DeferredRegister.DataComponents DATA_COMPONENT_TYPES = DeferredRegister .createDataComponents(EnderIO.NAMESPACE); - public static final DeferredHolder, DataComponentType>>> CONDUIT = DATA_COMPONENT_TYPES + public static final DeferredHolder, DataComponentType>>> CONDUIT = DATA_COMPONENT_TYPES .registerComponentType("conduit", builder -> builder.persistent(Conduit.CODEC).networkSynchronized(Conduit.STREAM_CODEC)); diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitItems.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitItems.java index c63b3a3323..2c66aeab4a 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitItems.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitItems.java @@ -9,7 +9,6 @@ import com.enderio.conduits.client.ConduitFacadeColor; import com.enderio.conduits.common.conduit.facades.ComponentBackedConduitFacadeProvider; import com.enderio.conduits.common.conduit.facades.ConduitFacadeItem; -import com.enderio.conduits.common.conduit.upgrade.SpeedUpgradeItem; import com.enderio.conduits.common.redstone.DoubleRedstoneChannel; import com.enderio.conduits.common.redstone.RedstoneCountFilter; import com.enderio.conduits.common.redstone.RedstoneFilterItem; @@ -52,38 +51,6 @@ private static RegiliteItem conduitFacade(String name, Facade ComponentBackedConduitFacadeProvider.PROVIDER); } - public static final RegiliteItem EXTRACTION_SPEED_UPGRADE_1 = ITEM_REGISTRY - .registerItem("extraction_speed_upgrade_1", - properties -> new SpeedUpgradeItem( - properties.component(ConduitComponents.EXTRACTION_SPEED_UPGRADE_TIER, 1))) - .setTranslation("Tier 1 Extraction Speed Upgrade") - .setTab(EIOCreativeTabs.CONDUITS) - .addCapability(ConduitCapabilities.CONDUIT_UPGRADE, SpeedUpgradeItem.CAPABILITY_PROVIDER); - - public static final RegiliteItem EXTRACTION_SPEED_UPGRADE_2 = ITEM_REGISTRY - .registerItem("extraction_speed_upgrade_2", - properties -> new SpeedUpgradeItem( - properties.component(ConduitComponents.EXTRACTION_SPEED_UPGRADE_TIER, 2))) - .setTranslation("Tier 2 Extraction Speed Upgrade") - .setTab(EIOCreativeTabs.CONDUITS) - .addCapability(ConduitCapabilities.CONDUIT_UPGRADE, SpeedUpgradeItem.CAPABILITY_PROVIDER); - - public static final RegiliteItem EXTRACTION_SPEED_UPGRADE_3 = ITEM_REGISTRY - .registerItem("extraction_speed_upgrade_3", - properties -> new SpeedUpgradeItem( - properties.component(ConduitComponents.EXTRACTION_SPEED_UPGRADE_TIER, 3))) - .setTranslation("Tier 3 Extraction Speed Upgrade") - .setTab(EIOCreativeTabs.CONDUITS) - .addCapability(ConduitCapabilities.CONDUIT_UPGRADE, SpeedUpgradeItem.CAPABILITY_PROVIDER); - - public static final RegiliteItem EXTRACTION_SPEED_UPGRADE_4 = ITEM_REGISTRY - .registerItem("extraction_speed_upgrade_4", - properties -> new SpeedUpgradeItem( - properties.component(ConduitComponents.EXTRACTION_SPEED_UPGRADE_TIER, 4))) - .setTranslation("Tier 4 Extraction Speed Upgrade") - .setTab(EIOCreativeTabs.CONDUITS) - .addCapability(ConduitCapabilities.CONDUIT_UPGRADE, SpeedUpgradeItem.CAPABILITY_PROVIDER); - public static final RegiliteItem NOT_FILTER = createRedstoneFilter("redstone_not_filter", ConduitComponents.REDSTONE_NOT_FILTER, Unit.INSTANCE, RedstoneFilterItem.NOT_FILTER_PROVIDER, null); public static final RegiliteItem OR_FILTER = createRedstoneFilter("redstone_or_filter", diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java index 68887a2d2b..9216dfe0c1 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitLang.java @@ -18,21 +18,42 @@ public class ConduitLang { "Ender Energy Conduit"); public static final Component REDSTONE_CONDUIT = addTranslation("item", EnderIO.loc("conduit.redstone"), "Redstone Conduit"); - public static final Component FLUID_CONDUIT = addTranslation("item", EnderIO.loc("conduit.fluid"), - "Fluid Conduit"); + public static final Component FLUID_CONDUIT = addTranslation("item", EnderIO.loc("conduit.fluid"), "Fluid Conduit"); public static final Component PRESSURIZED_FLUID_CONDUIT = addTranslation("item", EnderIO.loc("conduit.pressurized_fluid"), "Pressurized Fluid Conduit"); public static final Component ENDER_FLUID_CONDUIT = addTranslation("item", EnderIO.loc("conduit.ender_fluid"), "Ender Fluid Conduit"); - public static final Component ITEM_CONDUIT = addTranslation("item", EnderIO.loc("conduit.item"), - "Item Conduit"); - public static final Component ENHANCED_ITEM_CONDUIT = addTranslation("item", - EnderIO.loc("conduit.enhanced_item"), "Enhanced Item Conduit"); + public static final Component ITEM_CONDUIT = addTranslation("item", EnderIO.loc("conduit.item"), "Item Conduit"); + public static final Component ENHANCED_ITEM_CONDUIT = addTranslation("item", EnderIO.loc("conduit.enhanced_item"), + "Enhanced Item Conduit"); public static final Component ENDER_ITEM_CONDUIT = addTranslation("item", EnderIO.loc("conduit.ender_item"), "Ender Item Conduit"); // endregion + // region Conduit Screen Tooltips + + public static final Component CONDUIT_CHANNEL = addTranslation("gui", EnderIO.loc("conduit_channel"), "Channel"); + public static final Component REDSTONE_CHANNEL = addTranslation("gui", EnderIO.loc("redstone_channel"), + "Signal Color"); + + public static final Component ROUND_ROBIN_ENABLED = addTranslation("gui", EnderIO.loc("round_robin.enabled"), + "Round Robin Enabled"); + public static final Component ROUND_ROBIN_DISABLED = addTranslation("gui", EnderIO.loc("round_robin.disabled"), + "Round Robin Disabled"); + public static final Component SELF_FEED_ENABLED = addTranslation("gui", EnderIO.loc("self_feed.enabled"), + "Self Feed Enabled"); + public static final Component SELF_FEED_DISABLED = addTranslation("gui", EnderIO.loc("self_feed.disabled"), + "Self Feed Disabled"); + public static final Component FLUID_CONDUIT_CHANGE_FLUID1 = addTranslation("gui", + EnderIO.loc("fluid_conduit.change_fluid1"), "Locked Fluid:"); + public static final Component FLUID_CONDUIT_CHANGE_FLUID2 = addTranslation("gui", + EnderIO.loc("fluid_conduit.change_fluid2"), "Click to reset!"); + public static final MutableComponent FLUID_CONDUIT_CHANGE_FLUID3 = addTranslation("gui", + EnderIO.loc("fluid_conduit.change_fluid3"), "Fluid: %s"); + + // endregion + public static final MutableComponent GRAPH_TICK_RATE_TOOLTIP = addTranslation("tooltip", EnderIO.loc("conduit.debug.tick_rate"), "Graph Ticks: %s/sec"); @@ -44,17 +65,28 @@ public class ConduitLang { public static final MutableComponent FLUID_EFFECTIVE_RATE_TOOLTIP = addTranslation("tooltip", EnderIO.loc("conduit.fluid.effective_rate"), "Effective Rate: %s mB/t"); - public static final Component MULTI_FLUID_TOOLTIP = addTranslation("tooltip", - EnderIO.loc("conduit.fluid.multi"), "Allows multiple fluids to be transported on the same line"); + public static final Component MULTI_FLUID_TOOLTIP = addTranslation("tooltip", EnderIO.loc("conduit.fluid.multi"), + "Allows multiple fluids to be transported on the same line"); public static final MutableComponent ITEM_RAW_RATE_TOOLTIP = addTranslation("tooltip", EnderIO.loc("conduit.item.raw_rate"), "Rate: %s Items/graph tick"); public static final MutableComponent ITEM_EFFECTIVE_RATE_TOOLTIP = addTranslation("tooltip", EnderIO.loc("conduit.item.effective_rate"), "Effective Rate: %s Items/sec"); + public static final Component CONDUIT_ERROR_NO_SCREEN_TYPE = addTranslation("gui", + EnderIO.loc("conduit.error.no_screen_type"), "Error: No screen type defined"); + + public static final Component CONDUIT_ENABLED = addTranslation("gui", EnderIO.loc("conduit.enabled"), "Enabled"); public static final Component CONDUIT_INSERT = addTranslation("gui", EnderIO.loc("conduit.insert"), "Insert"); - public static final Component CONDUIT_EXTRACT = addTranslation("gui", EnderIO.loc("conduit.extract"), - "Extract"); + public static final Component CONDUIT_EXTRACT = addTranslation("gui", EnderIO.loc("conduit.extract"), "Extract"); + public static final Component CONDUIT_INPUT = addTranslation("gui", EnderIO.loc("conduit.input"), "Input"); + public static final Component CONDUIT_OUTPUT = addTranslation("gui", EnderIO.loc("conduit.output"), "Output"); + + // Redstone Conduit + public static final Component CONDUIT_REDSTONE_SIGNAL_COLOR = addTranslation("gui", + EnderIO.loc("conduit.redstone.signal_color"), "Signal Color"); + public static final Component CONDUIT_REDSTONE_STRONG_SIGNAL = addTranslation("gui", + EnderIO.loc("conduit.redstone.strong_signal"), "Strong Signal"); public static final MutableComponent TRANSPARENT_FACADE_TOOLTIP = addTranslation("tooltip", EnderIO.loc("conduit_facade.transparent"), diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitMenus.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitMenus.java index 09904ef6aa..80ab369140 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitMenus.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitMenus.java @@ -1,11 +1,11 @@ package com.enderio.conduits.common.init; import com.enderio.conduits.EnderIOConduits; -import com.enderio.conduits.client.gui.ConduitScreen; -import com.enderio.conduits.client.gui.RedstoneCountFilterScreen; -import com.enderio.conduits.client.gui.RedstoneDoubleChannelFilterScreen; -import com.enderio.conduits.client.gui.RedstoneTimerFilterScreen; -import com.enderio.conduits.common.menu.ConduitMenu; +import com.enderio.conduits.client.gui.screen.ConduitScreen; +import com.enderio.conduits.client.gui.screen.filter.RedstoneCountFilterScreen; +import com.enderio.conduits.client.gui.screen.filter.RedstoneDoubleChannelFilterScreen; +import com.enderio.conduits.client.gui.screen.filter.RedstoneTimerFilterScreen; +import com.enderio.conduits.common.conduit.menu.ConduitMenu; import com.enderio.conduits.common.menu.RedstoneCountFilterMenu; import com.enderio.conduits.common.menu.RedstoneDoubleChannelFilterMenu; import com.enderio.conduits.common.menu.RedstoneTimerFilterMenu; @@ -16,17 +16,18 @@ public class ConduitMenus { private static final MenuRegistry MENU_REGISTRY = EnderIOConduits.REGILITE.menuRegistry(); - public static final RegiliteMenu CONDUIT_MENU = MENU_REGISTRY - .registerMenu("conduit", ConduitMenu::factory, () -> ConduitScreen::new); + public static final RegiliteMenu CONDUIT_MENU = MENU_REGISTRY.registerMenu("conduit", ConduitMenu::new, + () -> ConduitScreen::new); public static final RegiliteMenu REDSTONE_DOUBLE_CHANNEL_FILTER = MENU_REGISTRY - .registerMenu("redstone_and_filter", RedstoneDoubleChannelFilterMenu::factory, () -> RedstoneDoubleChannelFilterScreen::new); + .registerMenu("redstone_and_filter", RedstoneDoubleChannelFilterMenu::factory, + () -> RedstoneDoubleChannelFilterScreen::new); - public static final RegiliteMenu REDSTONE_TIMER_FILTER = MENU_REGISTRY - .registerMenu("redstone_timer_filter", RedstoneTimerFilterMenu::factory, () -> RedstoneTimerFilterScreen::new); + public static final RegiliteMenu REDSTONE_TIMER_FILTER = MENU_REGISTRY.registerMenu( + "redstone_timer_filter", RedstoneTimerFilterMenu::factory, () -> RedstoneTimerFilterScreen::new); - public static final RegiliteMenu REDSTONE_COUNT_FILTER = MENU_REGISTRY - .registerMenu("redstone_count_filter", RedstoneCountFilterMenu::factory, () -> RedstoneCountFilterScreen::new); + public static final RegiliteMenu REDSTONE_COUNT_FILTER = MENU_REGISTRY.registerMenu( + "redstone_count_filter", RedstoneCountFilterMenu::factory, () -> RedstoneCountFilterScreen::new); public static void register(IEventBus bus) { MENU_REGISTRY.register(bus); diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitTypes.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitTypes.java index d173107bd4..e14cc6dc0c 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitTypes.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/ConduitTypes.java @@ -1,16 +1,27 @@ package com.enderio.conduits.common.init; import com.enderio.base.api.EnderIO; -import com.enderio.conduits.api.ConduitDataType; import com.enderio.conduits.api.ConduitType; import com.enderio.conduits.api.EnderIOConduitsRegistries; +import com.enderio.conduits.api.connection.config.ConnectionConfigType; +import com.enderio.conduits.api.network.ConduitNetworkContextType; +import com.enderio.conduits.api.network.node.NodeDataType; +import com.enderio.conduits.api.network.node.legacy.ConduitDataType; +import com.enderio.conduits.common.conduit.legacy.LegacyFluidConduitData; +import com.enderio.conduits.common.conduit.legacy.LegacyItemConduitData; +import com.enderio.conduits.common.conduit.legacy.LegacyRedstoneConduitData; import com.enderio.conduits.common.conduit.type.energy.EnergyConduit; +import com.enderio.conduits.common.conduit.type.energy.EnergyConduitConnectionConfig; +import com.enderio.conduits.common.conduit.type.energy.EnergyConduitNetworkContext; import com.enderio.conduits.common.conduit.type.fluid.FluidConduit; -import com.enderio.conduits.common.conduit.type.fluid.FluidConduitData; +import com.enderio.conduits.common.conduit.type.fluid.FluidConduitConnectionConfig; +import com.enderio.conduits.common.conduit.type.fluid.FluidConduitNetworkContext; import com.enderio.conduits.common.conduit.type.item.ItemConduit; -import com.enderio.conduits.common.conduit.type.item.ItemConduitData; +import com.enderio.conduits.common.conduit.type.item.ItemConduitConnectionConfig; +import com.enderio.conduits.common.conduit.type.item.ItemConduitNodeData; import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduit; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitConnectionConfig; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import java.util.function.Supplier; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.capabilities.Capabilities; @@ -36,20 +47,63 @@ public static class Data { private static final DeferredRegister> CONDUIT_DATA_TYPES = DeferredRegister .create(EnderIOConduitsRegistries.CONDUIT_DATA_TYPE, EnderIO.NAMESPACE); - public static final Supplier> ITEM = CONDUIT_DATA_TYPES.register("item", - () -> new ConduitDataType<>(ItemConduitData.CODEC, ItemConduitData.STREAM_CODEC, ItemConduitData::new)); + public static final Supplier> ITEM = CONDUIT_DATA_TYPES.register("item", + () -> new ConduitDataType<>(LegacyItemConduitData.CODEC, LegacyItemConduitData.STREAM_CODEC, + LegacyItemConduitData::new)); - public static final Supplier> FLUID = CONDUIT_DATA_TYPES.register("fluid", - () -> new ConduitDataType<>(FluidConduitData.CODEC, FluidConduitData.STREAM_CODEC, - FluidConduitData::new)); + public static final Supplier> FLUID = CONDUIT_DATA_TYPES + .register("fluid", () -> new ConduitDataType<>(LegacyFluidConduitData.CODEC, + LegacyFluidConduitData.STREAM_CODEC, LegacyFluidConduitData::new)); - public static final Supplier> REDSTONE = CONDUIT_DATA_TYPES - .register("redstone", () -> new ConduitDataType<>(RedstoneConduitData.CODEC, - RedstoneConduitData.STREAM_CODEC, RedstoneConduitData::new)); + public static final Supplier> REDSTONE = CONDUIT_DATA_TYPES + .register("redstone", () -> new ConduitDataType<>(LegacyRedstoneConduitData.CODEC, + LegacyRedstoneConduitData.STREAM_CODEC, LegacyRedstoneConduitData::new)); + } + + public static class ConnectionTypes { + private static final DeferredRegister> CONNECTION_TYPES = DeferredRegister + .create(EnderIOConduitsRegistries.CONDUIT_CONNECTION_CONFIG_TYPE, EnderIO.NAMESPACE); + + public static final Supplier> ITEM = CONNECTION_TYPES + .register("item", () -> ItemConduitConnectionConfig.TYPE); + + public static final Supplier> ENERGY = CONNECTION_TYPES + .register("energy", () -> EnergyConduitConnectionConfig.TYPE); + + public static final Supplier> REDSTONE = CONNECTION_TYPES + .register("redstone", () -> RedstoneConduitConnectionConfig.TYPE); + + public static final Supplier> FLUID = CONNECTION_TYPES + .register("fluid", () -> FluidConduitConnectionConfig.TYPE); + } + + public static class NodeData { + private static final DeferredRegister> NODE_DATA_TYPES = DeferredRegister + .create(EnderIOConduitsRegistries.CONDUIT_NODE_DATA_TYPE, EnderIO.NAMESPACE); + + public static final Supplier> ITEM = NODE_DATA_TYPES.register("item", + () -> ItemConduitNodeData.TYPE); + } + + public static class ContextTypes { + public static final DeferredRegister> CONDUIT_NETWORK_CONTEXT_TYPES = DeferredRegister + .create(EnderIOConduitsRegistries.CONDUIT_NETWORK_CONTEXT_TYPE, EnderIO.NAMESPACE); + + public static final Supplier> ENERGY = CONDUIT_NETWORK_CONTEXT_TYPES + .register("energy", () -> EnergyConduitNetworkContext.TYPE); + + public static final Supplier> REDSTONE = CONDUIT_NETWORK_CONTEXT_TYPES + .register("redstone", () -> RedstoneConduitNetworkContext.TYPE); + + public static final Supplier> FLUID = CONDUIT_NETWORK_CONTEXT_TYPES + .register("fluid", () -> FluidConduitNetworkContext.TYPE); } public static void register(IEventBus bus) { CONDUIT_TYPES.register(bus); Data.CONDUIT_DATA_TYPES.register(bus); + ConnectionTypes.CONNECTION_TYPES.register(bus); + NodeData.NODE_DATA_TYPES.register(bus); + ContextTypes.CONDUIT_NETWORK_CONTEXT_TYPES.register(bus); } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/Conduits.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/Conduits.java index 325a9dc42b..ffd76aa44d 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/init/Conduits.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/init/Conduits.java @@ -2,50 +2,45 @@ import com.enderio.base.api.EnderIO; import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.ConduitNetworkContextType; import com.enderio.conduits.api.EnderIOConduitsRegistries; import com.enderio.conduits.common.conduit.type.energy.EnergyConduit; -import com.enderio.conduits.common.conduit.type.energy.EnergyConduitNetworkContext; import com.enderio.conduits.common.conduit.type.fluid.FluidConduit; import com.enderio.conduits.common.conduit.type.item.ItemConduit; import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduit; -import java.util.function.Supplier; import net.minecraft.data.worldgen.BootstrapContext; import net.minecraft.resources.ResourceKey; -import net.neoforged.bus.api.IEventBus; -import net.neoforged.neoforge.registries.DeferredRegister; public class Conduits { - public static ResourceKey> ENERGY = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> ENERGY = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("energy")); - public static ResourceKey> ENHANCED_ENERGY = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, - EnderIO.loc("enhanced_energy")); - public static ResourceKey> ENDER_ENERGY = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> ENHANCED_ENERGY = ResourceKey + .create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("enhanced_energy")); + public static ResourceKey> ENDER_ENERGY = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("ender_energy")); - public static ResourceKey> REDSTONE = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> REDSTONE = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("redstone")); - public static ResourceKey> FLUID = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> FLUID = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("fluid")); - public static ResourceKey> PRESSURIZED_FLUID = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, - EnderIO.loc("pressurized_fluid")); - public static ResourceKey> ENDER_FLUID = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> PRESSURIZED_FLUID = ResourceKey + .create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("pressurized_fluid")); + public static ResourceKey> ENDER_FLUID = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("ender_fluid")); - public static ResourceKey> ITEM = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> ITEM = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("item")); - public static ResourceKey> ENHANCED_ITEM = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> ENHANCED_ITEM = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("enhanced_item")); - public static ResourceKey> ENDER_ITEM = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, + public static ResourceKey> ENDER_ITEM = ResourceKey.create(EnderIOConduitsRegistries.Keys.CONDUIT, EnderIO.loc("ender_item")); - public static void bootstrap(BootstrapContext> context) { + public static void bootstrap(BootstrapContext> context) { // TODO: These rates are still up for change, but will refine through testing. context.register(ENERGY, new EnergyConduit(EnderIO.loc("block/conduit/energy"), ConduitLang.ENERGY_CONDUIT, 1_000)); context.register(ENHANCED_ENERGY, new EnergyConduit(EnderIO.loc("block/conduit/enhanced_energy"), ConduitLang.ENHANCED_ENERGY_CONDUIT, 12_000)); - context.register(ENDER_ENERGY, new EnergyConduit(EnderIO.loc("block/conduit/ender_energy"), - ConduitLang.ENDER_ENERGY_CONDUIT, 48_000)); + context.register(ENDER_ENERGY, + new EnergyConduit(EnderIO.loc("block/conduit/ender_energy"), ConduitLang.ENDER_ENERGY_CONDUIT, 48_000)); context.register(REDSTONE, new RedstoneConduit(EnderIO.loc("block/conduit/redstone"), EnderIO.loc("block/conduit/redstone_active"), ConduitLang.REDSTONE_CONDUIT)); @@ -54,8 +49,8 @@ public static void bootstrap(BootstrapContext> context) { new FluidConduit(EnderIO.loc("block/conduit/fluid"), ConduitLang.FLUID_CONDUIT, 50, false)); context.register(PRESSURIZED_FLUID, new FluidConduit(EnderIO.loc("block/conduit/pressurized_fluid"), ConduitLang.PRESSURIZED_FLUID_CONDUIT, 100, false)); - context.register(ENDER_FLUID, new FluidConduit(EnderIO.loc("block/conduit/ender_fluid"), - ConduitLang.ENDER_FLUID_CONDUIT, 200, true)); + context.register(ENDER_FLUID, + new FluidConduit(EnderIO.loc("block/conduit/ender_fluid"), ConduitLang.ENDER_FLUID_CONDUIT, 200, true)); context.register(ITEM, new ItemConduit(EnderIO.loc("block/conduit/item"), ConduitLang.ITEM_CONDUIT, 4, 20)); @@ -68,16 +63,6 @@ public static void bootstrap(BootstrapContext> context) { // ConduitLang.ENDER_ITEM_CONDUIT, 4, 5)); } - public static class ContextSerializers { - public static final DeferredRegister> CONDUIT_NETWORK_CONTEXT_TYPES = DeferredRegister - .create(EnderIOConduitsRegistries.CONDUIT_NETWORK_CONTEXT_TYPE, EnderIO.NAMESPACE); - - public static final Supplier> ENERGY = CONDUIT_NETWORK_CONTEXT_TYPES - .register("energy", () -> new ConduitNetworkContextType<>(EnergyConduitNetworkContext.CODEC, - EnergyConduitNetworkContext::new)); - } - - public static void register(IEventBus bus) { - ContextSerializers.CONDUIT_NETWORK_CONTEXT_TYPES.register(bus); + public static void register() { } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java index d99f82b2af..7ebc630e62 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/cctweaked/EIOBundledRedstoneProvider.java @@ -1,16 +1,14 @@ package com.enderio.conduits.common.integrations.cctweaked; -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity; -import com.enderio.conduits.common.conduit.connection.ConnectionState; -import com.enderio.conduits.common.conduit.connection.DynamicConnectionState; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.api.ConduitRedstoneSignalAware; +import com.enderio.conduits.api.connection.ConnectionStatus; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlockEntity; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitConnectionConfig; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import com.enderio.conduits.common.init.ConduitTypes; -import com.enderio.conduits.common.init.Conduits; import dan200.computercraft.api.redstone.BundledRedstoneProvider; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.core.Holder; import net.minecraft.world.item.DyeColor; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; @@ -23,24 +21,40 @@ public class EIOBundledRedstoneProvider implements BundledRedstoneProvider { public int getBundledRedstoneOutput(Level world, BlockPos pos, Direction side) { BlockEntity be = world.getBlockEntity(pos); - Holder> redstoneConduit = world.holderOrThrow(Conduits.REDSTONE); - if (be instanceof ConduitBundleBlockEntity conduit) { - ConnectionState connectionState = conduit.getBundle().getConnectionState(side, redstoneConduit); - if (connectionState instanceof DynamicConnectionState dyn && dyn.isInsert()) { - RedstoneConduitData data = conduit.getBundle().getNodeFor(redstoneConduit).getData(ConduitTypes.Data.REDSTONE.get()); - if (data == null) { - return -1; - } - - int out = 0; - - for (DyeColor color : DyeColor.values()) { - out |= (data.isActive(color) ? 1 : 0) << color.getId(); - } - return out; + var redstoneConduit = conduit.getConduitByType(ConduitTypes.REDSTONE.get()); + if (redstoneConduit == null) { + return -1; + } + + if (conduit.getConnectionStatus(side, redstoneConduit) != ConnectionStatus.CONNECTED_BLOCK) { + return -1; + } + + var config = conduit.getConnectionConfig(side, redstoneConduit, RedstoneConduitConnectionConfig.TYPE); + if (!config.canSend(ConduitRedstoneSignalAware.NONE)) { + return -1; + } + + var node = conduit.getConduitNode(redstoneConduit); + var network = node.getNetwork(); + if (network == null) { + return -1; } + + var context = network.getContext(RedstoneConduitNetworkContext.TYPE); + if (context == null) { + return -1; + } + + int out = 0; + + for (DyeColor color : DyeColor.values()) { + out |= (context.isActive(color) ? 1 : 0) << color.getId(); + } + return out; } + return -1; } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/jei/ConduitSubtypeInterpreter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/jei/ConduitSubtypeInterpreter.java index 55d53bfa61..c56961d4b4 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/jei/ConduitSubtypeInterpreter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/integrations/jei/ConduitSubtypeInterpreter.java @@ -10,7 +10,7 @@ public class ConduitSubtypeInterpreter implements IIngredientSubtypeInterpreter { @Override public String apply(ItemStack ingredient, UidContext context) { - Holder> conduit = ingredient.get(ConduitComponents.CONDUIT); + Holder> conduit = ingredient.get(ConduitComponents.CONDUIT); if (conduit != null) { return conduit.getRegisteredName(); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/menu/ConduitMenu.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/menu/ConduitMenu.java deleted file mode 100644 index 1bbe0698bf..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/menu/ConduitMenu.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.enderio.conduits.common.menu; - -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.api.SlotType; -import com.enderio.conduits.common.conduit.ConduitBundle; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlock; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity; -import com.enderio.conduits.common.init.ConduitMenus; -import com.enderio.conduits.common.network.ConduitMenuSelectionPacket; -import com.enderio.core.common.menu.LegacyBaseBlockEntityMenu; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.neoforged.neoforge.items.IItemHandler; -import net.neoforged.neoforge.network.PacketDistributor; -import org.apache.logging.log4j.LogManager; -import org.jetbrains.annotations.Nullable; - -public class ConduitMenu extends LegacyBaseBlockEntityMenu { - private Direction direction; - private Holder> conduit; - - public ConduitMenu(@Nullable ConduitBundleBlockEntity blockEntity, Inventory inventory, int pContainerId, - Direction direction, Holder> conduit) { - super(ConduitMenus.CONDUIT_MENU.get(), pContainerId, blockEntity, inventory); - - this.direction = direction; - this.conduit = conduit; - if (blockEntity != null) { - IItemHandler conduitItemHandler = blockEntity.getConduitItemHandler(); - for (Direction forDirection : Direction.values()) { - for (int i = 0; i < ConduitBundle.MAX_CONDUITS; i++) { - for (SlotType slotType : SlotType.values()) { - addSlot(new ConduitSlot(blockEntity.getBundle(), conduitItemHandler, () -> this.direction, - forDirection, () -> blockEntity.getBundle().getConduits().indexOf(this.conduit), i, - slotType)); - } - } - } - } - - addPlayerInventorySlots(23, 113); - } - - @Override - public ItemStack quickMoveStack(Player pPlayer, int pIndex) { - ItemStack resultItemStack = ItemStack.EMPTY; - Slot slot = this.slots.get(pIndex); - if (slot.hasItem()) { - ItemStack itemInSlot = slot.getItem(); - resultItemStack = itemInSlot.copy(); - if (pIndex < this.slots.size() - PLAYER_INVENTORY_SIZE) { - if (!this.moveItemStackTo(itemInSlot, this.slots.size() - PLAYER_INVENTORY_SIZE, this.slots.size(), - true)) { - return ItemStack.EMPTY; - } - } else if (!this.moveItemStackTo(itemInSlot, 0, this.slots.size() - PLAYER_INVENTORY_SIZE, false)) { - return ItemStack.EMPTY; - } - - if (itemInSlot.isEmpty()) { - slot.setByPlayer(ItemStack.EMPTY); - } else { - slot.setChanged(); - } - } - - return resultItemStack; - } - - @Override - public boolean stillValid(Player player) { - return getBlockEntity() != null && getBlockEntity().stillValid(player) - // usually called serverside only, but I want to have a clientcheck for type, to - // close the screen clientside immediatly - && (player instanceof ServerPlayer || clientValid()); - } - - private boolean clientValid() { - return getBlockEntity().getBundle().getConduits().contains(conduit) - && ConduitBundleBlock.canBeOrIsValidConnection(getBlockEntity(), conduit, direction); - } - - public static ConduitMenu factory(int pContainerId, Inventory inventory, RegistryFriendlyByteBuf buf) { - BlockEntity entity = inventory.player.level().getBlockEntity(buf.readBlockPos()); - Direction direction = buf.readEnum(Direction.class); - Holder> type = Conduit.STREAM_CODEC.decode(buf); - if (entity instanceof ConduitBundleBlockEntity castBlockEntity) { - return new ConduitMenu(castBlockEntity, inventory, pContainerId, direction, type); - } - - LogManager.getLogger().warn("couldn't find BlockEntity"); - return new ConduitMenu(null, inventory, pContainerId, direction, type); - } - - public Holder> getConduit() { - return conduit; - } - - public void setConduit(Holder> conduit) { - this.conduit = conduit; - - if (getBlockEntity().hasLevel() && getBlockEntity().getLevel().isClientSide()) { - PacketDistributor.sendToServer(new ConduitMenuSelectionPacket(this.conduit)); - } - } - - public Direction getDirection() { - return direction; - } - - public void setDirection(Direction direction) { - this.direction = direction; - } - - @Override - public void removed(Player player) { - super.removed(player); - if (getBlockEntity() != null && player instanceof ServerPlayer serverPlayer - && serverPlayer.serverLevel() - .players() - .stream() - .filter(p -> p != player) - .noneMatch(p -> p.containerMenu instanceof ConduitMenu)) { - getBlockEntity().updateEmptyDynConnection(); - } - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/menu/ConduitSlot.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/menu/ConduitSlot.java index 1eafc8d759..c0134e7d63 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/menu/ConduitSlot.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/menu/ConduitSlot.java @@ -1,58 +1,58 @@ -package com.enderio.conduits.common.menu; - -import com.enderio.conduits.api.SlotType; -import com.enderio.conduits.common.conduit.ConduitBundle; -import com.enderio.conduits.common.conduit.SlotData; -import net.minecraft.core.Direction; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.neoforged.neoforge.items.IItemHandler; -import net.neoforged.neoforge.items.SlotItemHandler; -import org.jetbrains.annotations.NotNull; - -import java.util.function.Supplier; - -public class ConduitSlot extends SlotItemHandler { - - private final Supplier visibleDirection; - private final Supplier visibleType; - - private final Direction visibleForDirection; - private final int visibleForType; - private final SlotType slotType; - private final ConduitBundle bundle; - - public ConduitSlot(ConduitBundle bundle, IItemHandler itemHandler, Supplier visibleDirection, Direction visibleForDirection, Supplier visibleType, int visibleForType, SlotType slotType) { - super(itemHandler, new SlotData(visibleForDirection, visibleForType, slotType).slotIndex(), slotType.getX(), slotType.getY()); - this.visibleDirection = visibleDirection; - this.visibleType = visibleType; - this.visibleForDirection = visibleForDirection; - this.visibleForType = visibleForType; - this.slotType = slotType; - this.bundle = bundle; - } - - @Override - public boolean mayPlace(ItemStack stack) { - return isActive() && super.mayPlace(stack); - } - - @Override - public boolean mayPickup(Player playerIn) { - return isActive() && super.mayPickup(playerIn); - } - - @Override - @NotNull - public ItemStack remove(int amount) { - return isActive() ? super.remove(amount) : ItemStack.EMPTY; - } - - @Override - public boolean isActive() { - return visibleDirection.get() == visibleForDirection - && visibleType.get() == visibleForType - && bundle.getConduits().size() > visibleForType - && slotType.isAvailableFor(bundle.getConduits().get(visibleForType).value().getMenuData()); - } -} +//package com.enderio.conduits.common.menu; +// +//import com.enderio.conduits.api.bundle.SlotType; +//import com.enderio.conduits.common.conduit.ConduitBundle; +//import com.enderio.conduits.common.conduit.SlotData; +//import net.minecraft.core.Direction; +//import net.minecraft.world.entity.player.Player; +//import net.minecraft.world.item.ItemStack; +//import net.neoforged.neoforge.items.IItemHandler; +//import net.neoforged.neoforge.items.SlotItemHandler; +//import org.jetbrains.annotations.NotNull; +// +//import java.util.function.Supplier; +// +//public class ConduitSlot extends SlotItemHandler { +// +// private final Supplier visibleDirection; +// private final Supplier visibleType; +// +// private final Direction visibleForDirection; +// private final int visibleForType; +// private final SlotType slotType; +// private final ConduitBundle bundle; +// +// public ConduitSlot(ConduitBundle bundle, IItemHandler itemHandler, Supplier visibleDirection, Direction visibleForDirection, Supplier visibleType, int visibleForType, SlotType slotType) { +// super(itemHandler, new SlotData(visibleForDirection, visibleForType, slotType).slotIndex(), slotType.getX(), slotType.getY()); +// this.visibleDirection = visibleDirection; +// this.visibleType = visibleType; +// this.visibleForDirection = visibleForDirection; +// this.visibleForType = visibleForType; +// this.slotType = slotType; +// this.bundle = bundle; +// } +// +// @Override +// public boolean mayPlace(ItemStack stack) { +// return isActive() && super.mayPlace(stack); +// } +// +// @Override +// public boolean mayPickup(Player playerIn) { +// return isActive() && super.mayPickup(playerIn); +// } +// +// @Override +// @NotNull +// public ItemStack remove(int amount) { +// return isActive() ? super.remove(amount) : ItemStack.EMPTY; +// } +// +// @Override +// public boolean isActive() { +// return visibleDirection.get() == visibleForDirection +// && visibleType.get() == visibleForType +// && bundle.getConduits().size() > visibleForType +// && slotType.isAvailableFor(bundle.getConduits().get(visibleForType).value().getMenuData()); +// } +//} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/C2SClearLockedFluidPacket.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/C2SClearLockedFluidPacket.java new file mode 100644 index 0000000000..2ae96491bd --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/C2SClearLockedFluidPacket.java @@ -0,0 +1,20 @@ +package com.enderio.conduits.common.network; + +import com.enderio.base.api.EnderIO; +import io.netty.buffer.ByteBuf; +import net.minecraft.core.BlockPos; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; + +public record C2SClearLockedFluidPacket(BlockPos pos) implements CustomPacketPayload { + + public static final Type TYPE = new Type<>(EnderIO.loc("clear_locked_fluid")); + + public static StreamCodec STREAM_CODEC = BlockPos.STREAM_CODEC + .map(C2SClearLockedFluidPacket::new, C2SClearLockedFluidPacket::pos); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/C2SSetConduitConnectionState.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/C2SSetConduitConnectionState.java deleted file mode 100644 index 2f60362d8c..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/C2SSetConduitConnectionState.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.enderio.conduits.common.network; - -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.common.conduit.connection.DynamicConnectionState; -import com.enderio.core.EnderCore; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - -public record C2SSetConduitConnectionState( - BlockPos pos, - Direction direction, - Holder> conduit, - DynamicConnectionState connectionState -) implements CustomPacketPayload { - - public static final Type TYPE = new Type<>(EnderCore.loc("c2s_conduit_connection_state")); - - public static final StreamCodec STREAM_CODEC = StreamCodec.composite( - BlockPos.STREAM_CODEC, - C2SSetConduitConnectionState::pos, - Direction.STREAM_CODEC, - C2SSetConduitConnectionState::direction, - Conduit.STREAM_CODEC, - C2SSetConduitConnectionState::conduit, - DynamicConnectionState.STREAM_CODEC, - C2SSetConduitConnectionState::connectionState, - C2SSetConduitConnectionState::new - ); - - @Override - public Type type() { - return TYPE; - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/C2SSetConduitExtendedData.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/C2SSetConduitExtendedData.java deleted file mode 100644 index b9afba0b17..0000000000 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/C2SSetConduitExtendedData.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.enderio.conduits.common.network; - -import com.enderio.base.api.EnderIO; -import com.enderio.conduits.api.Conduit; -import com.enderio.conduits.common.conduit.ConduitDataContainer; -import com.enderio.core.EnderCore; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - -public record C2SSetConduitExtendedData( - BlockPos pos, - Holder> conduit, - ConduitDataContainer conduitDataContainer -) implements CustomPacketPayload { - - public static final Type TYPE = new Type<>(EnderIO.loc("c2s_conduit_extended_data")); - - public static StreamCodec STREAM_CODEC = StreamCodec.composite( - BlockPos.STREAM_CODEC, - C2SSetConduitExtendedData::pos, - Conduit.STREAM_CODEC, - C2SSetConduitExtendedData::conduit, - ConduitDataContainer.STREAM_CODEC, - C2SSetConduitExtendedData::conduitDataContainer, - C2SSetConduitExtendedData::new - ); - - @Override - public Type type() { - return TYPE; - } -} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitClientPayloadHandler.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitClientPayloadHandler.java index 40d3866930..1ca0651a9b 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitClientPayloadHandler.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitClientPayloadHandler.java @@ -1,8 +1,31 @@ package com.enderio.conduits.common.network; +import com.enderio.conduits.common.conduit.menu.ConduitMenu; +import net.neoforged.neoforge.network.handling.IPayloadContext; + public class ConduitClientPayloadHandler { private static final ConduitClientPayloadHandler INSTANCE = new ConduitClientPayloadHandler(); + public void handle(S2CConduitExtraGuiDataPacket packet, IPayloadContext context) { + context.enqueueWork(() -> { + if (packet.containerId() == context.player().containerMenu.containerId) { + if (context.player().containerMenu instanceof ConduitMenu menu) { + menu.setExtraGuiData(packet.extraGuiData()); + } + } + }); + } + + public void handle(S2CConduitListPacket packet, IPayloadContext context) { + context.enqueueWork(() -> { + if (packet.containerId() == context.player().containerMenu.containerId) { + if (context.player().containerMenu instanceof ConduitMenu menu) { + menu.setConnectedConduits(packet.conduits()); + } + } + }); + } + public static ConduitClientPayloadHandler getInstance() { return INSTANCE; } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java new file mode 100644 index 0000000000..f94fd063b9 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitCommonPayloadHandler.java @@ -0,0 +1,22 @@ +package com.enderio.conduits.common.network; + +import com.enderio.conduits.common.conduit.menu.ConduitMenu; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +public class ConduitCommonPayloadHandler { + private static final ConduitCommonPayloadHandler INSTANCE = new ConduitCommonPayloadHandler(); + + public void handle(SetConduitConnectionConfigPacket packet, IPayloadContext context) { + context.enqueueWork(() -> { + if (packet.containerId() == context.player().containerMenu.containerId) { + if (context.player().containerMenu instanceof ConduitMenu menu) { + menu.setConnectionConfig(packet.connectionConfig()); + } + } + }); + } + + public static ConduitCommonPayloadHandler getInstance() { + return INSTANCE; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitMenuSelectionPacket.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitMenuSelectionPacket.java index 4f3a4feeb1..e5a6c9cd0a 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitMenuSelectionPacket.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitMenuSelectionPacket.java @@ -9,14 +9,12 @@ import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -public record ConduitMenuSelectionPacket( - Holder> conduit -) implements CustomPacketPayload { +public record ConduitMenuSelectionPacket(Holder> conduit) implements CustomPacketPayload { public static final Type TYPE = new Type<>(EnderIO.loc("conduit_menu_selection")); - public static StreamCodec STREAM_CODEC = - ByteBufCodecs.holderRegistry(EnderIOConduitsRegistries.Keys.CONDUIT) + public static StreamCodec STREAM_CODEC = ByteBufCodecs + .holderRegistry(EnderIOConduitsRegistries.Keys.CONDUIT) .map(ConduitMenuSelectionPacket::new, ConduitMenuSelectionPacket::conduit); @Override diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitNetwork.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitNetwork.java index 0193d8349f..2f7e181cf8 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitNetwork.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitNetwork.java @@ -13,27 +13,31 @@ public class ConduitNetwork { @SubscribeEvent public static void register(final RegisterPayloadHandlersEvent event) { - final PayloadRegistrar registrar = event - .registrar(EnderCore.MOD_ID) - .versioned(PROTOCOL_VERSION); + final PayloadRegistrar registrar = event.registrar(EnderCore.MOD_ID).versioned(PROTOCOL_VERSION); - registrar.playToServer(C2SSetConduitConnectionState.TYPE, C2SSetConduitConnectionState.STREAM_CODEC, - ConduitServerPayloadHandler.getInstance()::handleConduitConnectionState); + registrar.playToServer(DoubleChannelPacket.TYPE, DoubleChannelPacket.STREAM_CODEC, + ConduitServerPayloadHandler.getInstance()::handleDoubleChannelFilter); + + registrar.playToServer(TimerFilterPacket.TYPE, TimerFilterPacket.STREAM_CODEC, + ConduitServerPayloadHandler.getInstance()::handleTimerFilter); - registrar.playToServer(C2SSetConduitExtendedData.TYPE, C2SSetConduitExtendedData.STREAM_CODEC, - ConduitServerPayloadHandler.getInstance()::handleConduitExtendedData); + registrar.playToServer(CountFilterPacket.TYPE, CountFilterPacket.STREAM_CODEC, + ConduitServerPayloadHandler.getInstance()::handleCountFilter); registrar.playToServer(ConduitMenuSelectionPacket.TYPE, ConduitMenuSelectionPacket.STREAM_CODEC, - ConduitServerPayloadHandler.getInstance()::handleConduitMenuSelection); + ConduitServerPayloadHandler.getInstance()::handleConduitMenuSelection); - registrar.playToServer(DoubleChannelPacket.TYPE, DoubleChannelPacket.STREAM_CODEC, - ConduitServerPayloadHandler.getInstance()::handleDoubleChannelFilter); + registrar.playToServer(C2SClearLockedFluidPacket.TYPE, C2SClearLockedFluidPacket.STREAM_CODEC, + ConduitServerPayloadHandler.getInstance()::handle); - registrar.playToServer(TimerFilterPacket.TYPE, TimerFilterPacket.STREAM_CODEC, - ConduitServerPayloadHandler.getInstance()::handleTimerFilter); + registrar.playToClient(S2CConduitExtraGuiDataPacket.TYPE, S2CConduitExtraGuiDataPacket.STREAM_CODEC, + ConduitClientPayloadHandler.getInstance()::handle); - registrar.playToServer(CountFilterPacket.TYPE, CountFilterPacket.STREAM_CODEC, - ConduitServerPayloadHandler.getInstance()::handleCountFilter); + registrar.playToClient(S2CConduitListPacket.TYPE, S2CConduitListPacket.STREAM_CODEC, + ConduitClientPayloadHandler.getInstance()::handle); + + registrar.playBidirectional(SetConduitConnectionConfigPacket.TYPE, + SetConduitConnectionConfigPacket.STREAM_CODEC, ConduitCommonPayloadHandler.getInstance()::handle); } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitServerPayloadHandler.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitServerPayloadHandler.java index 11cef6a3a2..77b1b5e8e9 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitServerPayloadHandler.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/ConduitServerPayloadHandler.java @@ -1,13 +1,14 @@ package com.enderio.conduits.common.network; import com.enderio.base.common.init.EIOCapabilities; -import com.enderio.conduits.common.conduit.block.ConduitBundleBlockEntity; -import com.enderio.conduits.common.menu.ConduitMenu; +import com.enderio.conduits.api.bundle.ConduitBundleAccessor; +import com.enderio.conduits.common.conduit.type.fluid.FluidConduitNetworkContext; +import com.enderio.conduits.common.init.ConduitTypes; import com.enderio.conduits.common.redstone.DoubleRedstoneChannel; import com.enderio.conduits.common.redstone.RedstoneCountFilter; import com.enderio.conduits.common.redstone.RedstoneTimerFilter; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.material.Fluids; import net.neoforged.neoforge.network.handling.IPayloadContext; public class ConduitServerPayloadHandler { @@ -17,31 +18,11 @@ public static ConduitServerPayloadHandler getInstance() { return INSTANCE; } - public void handleConduitConnectionState(final C2SSetConduitConnectionState packet, final IPayloadContext context) { - context.enqueueWork(() -> { - var level = context.player().level(); - BlockEntity be = level.getBlockEntity(packet.pos()); - if (be instanceof ConduitBundleBlockEntity conduitBundleBlockEntity) { - conduitBundleBlockEntity.handleConnectionStateUpdate(packet.direction(), packet.conduit(), packet.connectionState()); - } - }); - } - - public void handleConduitExtendedData(final C2SSetConduitExtendedData packet, final IPayloadContext context) { - context.enqueueWork(() -> { - var level = context.player().level(); - BlockEntity be = level.getBlockEntity(packet.pos()); - if (be instanceof ConduitBundleBlockEntity conduitBundleBlockEntity) { - conduitBundleBlockEntity.handleConduitDataUpdate(packet.conduit(), packet.conduitDataContainer()); - } - }); - } - public void handleConduitMenuSelection(final ConduitMenuSelectionPacket packet, final IPayloadContext context) { context.enqueueWork(() -> { - if (context.player().containerMenu instanceof ConduitMenu menu) { - menu.setConduit(packet.conduit()); - } +// if (context.player().containerMenu instanceof ConduitMenu menu) { +// menu.setConduit(packet.conduit()); +// } }); } @@ -74,4 +55,25 @@ public void handleCountFilter(CountFilterPacket packet, IPayloadContext context) } }); } + + public void handle(C2SClearLockedFluidPacket packet, IPayloadContext context) { + context.enqueueWork(() -> { + var level = context.player().level(); + var be = level.getBlockEntity(packet.pos()); + if (be instanceof ConduitBundleAccessor conduitBundle) { + var fluidConduit = conduitBundle.getConduitByType(ConduitTypes.FLUID.get()); + if (fluidConduit != null) { + var node = conduitBundle.getConduitNode(fluidConduit); + + var network = node.getNetwork(); + if (network != null) { + var networkContext = network.getContext(FluidConduitNetworkContext.TYPE); + if (networkContext != null) { + networkContext.setLockedFluid(Fluids.EMPTY); + } + } + } + } + }); + } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/S2CConduitExtraGuiDataPacket.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/S2CConduitExtraGuiDataPacket.java new file mode 100644 index 0000000000..e20ed35f13 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/S2CConduitExtraGuiDataPacket.java @@ -0,0 +1,26 @@ +package com.enderio.conduits.common.network; + +import com.enderio.base.api.EnderIO; +import io.netty.buffer.ByteBuf; +import java.util.Optional; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import org.jetbrains.annotations.Nullable; + +public record S2CConduitExtraGuiDataPacket(int containerId, @Nullable CompoundTag extraGuiData) + implements CustomPacketPayload { + + public static Type TYPE = new Type<>(EnderIO.loc("conduit_extra_gui_data")); + + public static StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.INT, S2CConduitExtraGuiDataPacket::containerId, + ByteBufCodecs.optional(ByteBufCodecs.COMPOUND_TAG).map(opt -> opt.orElse(null), Optional::ofNullable), + S2CConduitExtraGuiDataPacket::extraGuiData, S2CConduitExtraGuiDataPacket::new); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/S2CConduitListPacket.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/S2CConduitListPacket.java new file mode 100644 index 0000000000..518708a49e --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/S2CConduitListPacket.java @@ -0,0 +1,27 @@ +package com.enderio.conduits.common.network; + +import com.enderio.base.api.EnderIO; +import com.enderio.conduits.api.Conduit; +import com.enderio.conduits.common.conduit.bundle.ConduitBundleBlockEntity; +import java.util.List; +import net.minecraft.core.Holder; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; + +public record S2CConduitListPacket(int containerId, List>> conduits) + implements CustomPacketPayload { + + public static final Type TYPE = new Type<>(EnderIO.loc("conduit_list")); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.INT, S2CConduitListPacket::containerId, + Conduit.STREAM_CODEC.apply(ByteBufCodecs.list(ConduitBundleBlockEntity.MAX_CONDUITS)), + S2CConduitListPacket::conduits, S2CConduitListPacket::new); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/network/SetConduitConnectionConfigPacket.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/SetConduitConnectionConfigPacket.java new file mode 100644 index 0000000000..7f38edac32 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/network/SetConduitConnectionConfigPacket.java @@ -0,0 +1,24 @@ +package com.enderio.conduits.common.network; + +import com.enderio.base.api.EnderIO; +import com.enderio.conduits.api.connection.config.ConnectionConfig; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; + +public record SetConduitConnectionConfigPacket(int containerId, ConnectionConfig connectionConfig) + implements CustomPacketPayload { + + public static Type TYPE = new Type<>( + EnderIO.loc("client_set_conduit_conection_config")); + + public static StreamCodec STREAM_CODEC = StreamCodec + .composite(ByteBufCodecs.INT, SetConduitConnectionConfigPacket::containerId, ConnectionConfig.STREAM_CODEC, + SetConduitConnectionConfigPacket::connectionConfig, SetConduitConnectionConfigPacket::new); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/recipe/ConduitIngredient.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/recipe/ConduitIngredient.java index 2562f5b2dd..c42275ab7c 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/recipe/ConduitIngredient.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/recipe/ConduitIngredient.java @@ -20,17 +20,17 @@ public class ConduitIngredient implements ICustomIngredient { builder -> builder.group(Conduit.CODEC.fieldOf("conduit_type").forGetter(ConduitIngredient::conduit)) .apply(builder, ConduitIngredient::new)); - private final Holder> conduit; + private final Holder> conduit; - private ConduitIngredient(Holder> conduit) { + private ConduitIngredient(Holder> conduit) { this.conduit = conduit; } - public static Ingredient of(Holder> conduit) { + public static Ingredient of(Holder> conduit) { return new ConduitIngredient(conduit).toVanilla(); } - public Holder> conduit() { + public Holder> conduit() { return conduit; } @@ -44,7 +44,7 @@ public boolean test(ItemStack stack) { return false; } - Holder> conduit = stack.get(ConduitComponents.CONDUIT); + Holder> conduit = stack.get(ConduitComponents.CONDUIT); if (conduit == null) { return false; } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneANDFilter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneANDFilter.java index b3b4e5d907..ba11d7f9fd 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneANDFilter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneANDFilter.java @@ -1,6 +1,6 @@ package com.enderio.conduits.common.redstone; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import com.enderio.conduits.common.init.ConduitComponents; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; @@ -12,8 +12,8 @@ public RedstoneANDFilter(ItemStack stack) { } @Override - public int getOutputSignal(RedstoneConduitData data, DyeColor control) { - boolean b = data.isActive(getFirstChannel()) && data.isActive(getSecondChannel()); - return b ? 15 : 0 ; + public int getOutputSignal(RedstoneConduitNetworkContext context, DyeColor control) { + boolean b = context.isActive(getFirstChannel()) && context.isActive(getSecondChannel()); + return b ? 15 : 0; } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneCountFilter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneCountFilter.java index fd146e2da1..4d8267d05c 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneCountFilter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneCountFilter.java @@ -1,6 +1,6 @@ package com.enderio.conduits.common.redstone; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import com.enderio.conduits.common.init.ConduitComponents; import com.enderio.conduits.common.network.CountFilterPacket; import com.mojang.serialization.Codec; @@ -22,16 +22,16 @@ public RedstoneCountFilter(ItemStack stack) { } @Override - public int getOutputSignal(RedstoneConduitData data, DyeColor control) { + public int getOutputSignal(RedstoneConduitNetworkContext context, DyeColor control) { DyeColor channel = getChannel(); int maxCount = getMaxCount(); boolean deactivated = isDeactivated(); int count = getCount(); - if (data.isActive(channel) && deactivated) { + if (context.isActive(channel) && deactivated) { count++; deactivated = false; } - if (!data.isActive(channel)) { + if (!context.isActive(channel)) { deactivated = true; } if (count > maxCount) { @@ -52,7 +52,8 @@ public int getMaxCount() { public void setMaxCount(int maxCount) { var component = stack.get(ConduitComponents.REDSTONE_COUNT_FILTER); - stack.set(ConduitComponents.REDSTONE_COUNT_FILTER, new Component(component.channel1, maxCount, component.count, component.deactivated)); + stack.set(ConduitComponents.REDSTONE_COUNT_FILTER, + new Component(component.channel1, maxCount, component.count, component.deactivated)); } public int getCount() { @@ -61,7 +62,8 @@ public int getCount() { public void setCount(int count) { var component = stack.get(ConduitComponents.REDSTONE_COUNT_FILTER); - stack.set(ConduitComponents.REDSTONE_COUNT_FILTER, new Component(component.channel1, component.maxCount, count, component.deactivated)); + stack.set(ConduitComponents.REDSTONE_COUNT_FILTER, + new Component(component.channel1, component.maxCount, count, component.deactivated)); } public boolean isDeactivated() { @@ -70,37 +72,31 @@ public boolean isDeactivated() { public void setDeactivated(boolean lastActive) { var component = stack.get(ConduitComponents.REDSTONE_COUNT_FILTER); - stack.set(ConduitComponents.REDSTONE_COUNT_FILTER, new Component(component.channel1, component.maxCount, component.count, lastActive)); + stack.set(ConduitComponents.REDSTONE_COUNT_FILTER, + new Component(component.channel1, component.maxCount, component.count, lastActive)); } public void setState(CountFilterPacket packet) { - stack.set(ConduitComponents.REDSTONE_COUNT_FILTER, new Component(packet.channel1(), packet.maxCount(), packet.count(), packet.active())); + stack.set(ConduitComponents.REDSTONE_COUNT_FILTER, + new Component(packet.channel1(), packet.maxCount(), packet.count(), packet.active())); } public void setChannel(DyeColor channel) { var component = stack.get(ConduitComponents.REDSTONE_COUNT_FILTER); - stack.set(ConduitComponents.REDSTONE_COUNT_FILTER, new Component(channel, component.maxCount, component.count, component.deactivated)); + stack.set(ConduitComponents.REDSTONE_COUNT_FILTER, + new Component(channel, component.maxCount, component.count, component.deactivated)); } public record Component(DyeColor channel1, int maxCount, int count, boolean deactivated) { - public static final Codec CODEC = RecordCodecBuilder.create(instance -> - instance.group(DyeColor.CODEC.fieldOf("channel1").forGetter(Component::channel1), - ExtraCodecs.NON_NEGATIVE_INT.fieldOf("maxCount").forGetter(Component::maxCount), - ExtraCodecs.NON_NEGATIVE_INT.fieldOf("ticks").forGetter(Component::count), - Codec.BOOL.fieldOf("deactivated").forGetter(Component::deactivated)) - .apply(instance, Component::new) - ); - - public static final StreamCodec STREAM_CODEC = StreamCodec.composite( - DyeColor.STREAM_CODEC, - Component::channel1, - ByteBufCodecs.VAR_INT, - Component::maxCount, - ByteBufCodecs.VAR_INT, - Component::count, - ByteBufCodecs.BOOL, - Component::deactivated, - Component::new - ); + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance + .group(DyeColor.CODEC.fieldOf("channel1").forGetter(Component::channel1), + ExtraCodecs.NON_NEGATIVE_INT.fieldOf("maxCount").forGetter(Component::maxCount), + ExtraCodecs.NON_NEGATIVE_INT.fieldOf("ticks").forGetter(Component::count), + Codec.BOOL.fieldOf("deactivated").forGetter(Component::deactivated)) + .apply(instance, Component::new)); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite(DyeColor.STREAM_CODEC, + Component::channel1, ByteBufCodecs.VAR_INT, Component::maxCount, ByteBufCodecs.VAR_INT, + Component::count, ByteBufCodecs.BOOL, Component::deactivated, Component::new); } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneInsertFilter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneInsertFilter.java index d8705d8063..2c43750a0c 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneInsertFilter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneInsertFilter.java @@ -1,10 +1,10 @@ package com.enderio.conduits.common.redstone; import com.enderio.base.api.filter.ResourceFilter; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import net.minecraft.world.item.DyeColor; public interface RedstoneInsertFilter extends ResourceFilter { - int getOutputSignal(RedstoneConduitData data, DyeColor control); + int getOutputSignal(RedstoneConduitNetworkContext context, DyeColor control); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNANDFilter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNANDFilter.java index ad58772e3f..d6cb755dae 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNANDFilter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNANDFilter.java @@ -1,6 +1,6 @@ package com.enderio.conduits.common.redstone; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import com.enderio.conduits.common.init.ConduitComponents; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; @@ -12,8 +12,8 @@ public RedstoneNANDFilter(ItemStack stack) { } @Override - public int getOutputSignal(RedstoneConduitData data, DyeColor control) { - boolean b = data.isActive(getFirstChannel()) && data.isActive(getSecondChannel()); + public int getOutputSignal(RedstoneConduitNetworkContext context, DyeColor control) { + boolean b = context.isActive(getFirstChannel()) && context.isActive(getSecondChannel()); return b ? 0 : 15; } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNORFilter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNORFilter.java index bf3bda2954..1df423a6f0 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNORFilter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNORFilter.java @@ -1,6 +1,6 @@ package com.enderio.conduits.common.redstone; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import com.enderio.conduits.common.init.ConduitComponents; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; @@ -12,8 +12,8 @@ public RedstoneNORFilter(ItemStack stack) { } @Override - public int getOutputSignal(RedstoneConduitData data, DyeColor control) { - boolean b = data.isActive(getFirstChannel()) || data.isActive(getSecondChannel()); + public int getOutputSignal(RedstoneConduitNetworkContext context, DyeColor control) { + boolean b = context.isActive(getFirstChannel()) || context.isActive(getSecondChannel()); return b ? 0 : 15; } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNOTFilter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNOTFilter.java index d68145fd3d..2aebd16478 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNOTFilter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneNOTFilter.java @@ -1,6 +1,6 @@ package com.enderio.conduits.common.redstone; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.item.DyeColor; @@ -14,8 +14,8 @@ private RedstoneNOTFilter() { } @Override - public int getOutputSignal(RedstoneConduitData data, DyeColor control) { - return data.isActive(control) ? 0 : 15; + public int getOutputSignal(RedstoneConduitNetworkContext context, DyeColor control) { + return context.isActive(control) ? 0 : 15; } @Override diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneORFilter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneORFilter.java index afa4b64580..00541408f4 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneORFilter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneORFilter.java @@ -1,6 +1,6 @@ package com.enderio.conduits.common.redstone; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import com.enderio.conduits.common.init.ConduitComponents; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; @@ -12,8 +12,8 @@ public RedstoneORFilter(ItemStack stack) { } @Override - public int getOutputSignal(RedstoneConduitData data, DyeColor control) { - boolean b = data.isActive(getFirstChannel()) || data.isActive(getSecondChannel()); + public int getOutputSignal(RedstoneConduitNetworkContext context, DyeColor control) { + boolean b = context.isActive(getFirstChannel()) || context.isActive(getSecondChannel()); return b ? 15 : 0; } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneTLatchFilter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneTLatchFilter.java index f516b49a72..c2c0575ca5 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneTLatchFilter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneTLatchFilter.java @@ -1,6 +1,6 @@ package com.enderio.conduits.common.redstone; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import com.enderio.conduits.common.init.ConduitComponents; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; @@ -20,14 +20,14 @@ public RedstoneTLatchFilter(ItemStack stack) { } @Override - public int getOutputSignal(RedstoneConduitData data, DyeColor control) { + public int getOutputSignal(RedstoneConduitNetworkContext context, DyeColor control) { boolean output = isActive(); - if (data.isActive(control) && isDeactivated()) { + if (context.isActive(control) && isDeactivated()) { output = !output; setState(output, false); } - if (!data.isActive(control) && !isDeactivated()) { + if (!context.isActive(control) && !isDeactivated()) { setState(output, true); } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneXNORFilter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneXNORFilter.java index f8e51215c3..f0000cbdd7 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneXNORFilter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneXNORFilter.java @@ -1,6 +1,6 @@ package com.enderio.conduits.common.redstone; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import com.enderio.conduits.common.init.ConduitComponents; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; @@ -12,8 +12,8 @@ public RedstoneXNORFilter(ItemStack stack) { } @Override - public int getOutputSignal(RedstoneConduitData data, DyeColor control) { - boolean b = data.isActive(getFirstChannel()) ^ data.isActive(getSecondChannel()); + public int getOutputSignal(RedstoneConduitNetworkContext context, DyeColor control) { + boolean b = context.isActive(getFirstChannel()) ^ context.isActive(getSecondChannel()); return b ? 0 : 15; } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneXORFilter.java b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneXORFilter.java index f2658a7d81..d435be2713 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneXORFilter.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/common/redstone/RedstoneXORFilter.java @@ -1,6 +1,6 @@ package com.enderio.conduits.common.redstone; -import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitData; +import com.enderio.conduits.common.conduit.type.redstone.RedstoneConduitNetworkContext; import com.enderio.conduits.common.init.ConduitComponents; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemStack; @@ -12,8 +12,8 @@ public RedstoneXORFilter(ItemStack stack) { } @Override - public int getOutputSignal(RedstoneConduitData data, DyeColor control) { - boolean b = data.isActive(getFirstChannel()) ^ data.isActive(getSecondChannel()); + public int getOutputSignal(RedstoneConduitNetworkContext context, DyeColor control) { + boolean b = context.isActive(getFirstChannel()) ^ context.isActive(getSecondChannel()); return b ? 15 : 0; } } diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/data/recipe/ConduitRecipes.java b/enderio-conduits/src/main/java/com/enderio/conduits/data/recipe/ConduitRecipes.java index bb11dfb8fa..10de36c939 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/data/recipe/ConduitRecipes.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/data/recipe/ConduitRecipes.java @@ -25,9 +25,7 @@ import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.level.ItemLike; -import net.neoforged.neoforge.common.Tags; import net.neoforged.neoforge.common.conditions.ModLoadedCondition; -import net.neoforged.neoforge.common.crafting.SizedIngredient; public class ConduitRecipes extends RecipeProvider { @@ -42,7 +40,8 @@ public ConduitRecipes(PackOutput packOutput, CompletableFuture> conduitRegistry = lookupProvider.lookupOrThrow(EnderIOConduitsRegistries.Keys.CONDUIT); + HolderGetter> conduitRegistry = lookupProvider + .lookupOrThrow(EnderIOConduitsRegistries.Keys.CONDUIT); var itemConduit = conduitRegistry.getOrThrow(Conduits.ITEM); var fluidConduit = conduitRegistry.getOrThrow(Conduits.FLUID); @@ -53,7 +52,6 @@ protected void buildRecipes(RecipeOutput recipeOutput) { var enderEnergyConduit = conduitRegistry.getOrThrow(Conduits.ENDER_ENERGY); var redstoneConduit = conduitRegistry.getOrThrow(Conduits.REDSTONE); - buildUpgradeRecipes(recipeOutput); buildFilterRecipes(recipeOutput); buildFilterConversionRecipes(recipeOutput); buildFacadeCraftingRecipes(recipeOutput); @@ -157,76 +155,6 @@ protected void buildRecipes(RecipeOutput recipeOutput) { } - private void buildUpgradeRecipes(RecipeOutput recipeOutput) { - ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ConduitItems.EXTRACTION_SPEED_UPGRADE_1.get(), 2) - .pattern("III") - .pattern("APA") - .pattern("ATA") - .define('I', Tags.Items.INGOTS_IRON) - .define('P', Items.PISTON) - .define('T', Items.REDSTONE_TORCH) - .define('A', EIOTags.Items.INGOTS_REDSTONE_ALLOY) - .unlockedBy("has_ingredient", - InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.REDSTONE_ALLOY_INGOT)) - .save(recipeOutput); - - ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ConduitItems.EXTRACTION_SPEED_UPGRADE_2.get(), 2) - .pattern("III") - .pattern("APA") - .pattern("ATA") - .define('I', Tags.Items.INGOTS_IRON) - .define('P', Items.PISTON) - .define('T', Items.REDSTONE_TORCH) - .define('A', EIOTags.Items.INGOTS_CONDUCTIVE_ALLOY) - .unlockedBy("has_ingredient", - InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUCTIVE_ALLOY_INGOT)) - .save(recipeOutput); - - ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ConduitItems.EXTRACTION_SPEED_UPGRADE_2) - .requires(ConduitItems.EXTRACTION_SPEED_UPGRADE_1) - .requires(Ingredient.of(EIOTags.Items.INGOTS_CONDUCTIVE_ALLOY), 2) - .unlockedBy("has_ingredient", - InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUCTIVE_ALLOY_INGOT)) - .save(recipeOutput, EnderIO.loc("extraction_speed_upgrade_1_upgrade")); - - ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ConduitItems.EXTRACTION_SPEED_UPGRADE_3.get(), 2) - .pattern("III") - .pattern("APA") - .pattern("ATA") - .define('I', EIOTags.Items.INGOTS_DARK_STEEL) - .define('P', Items.PISTON) - .define('T', Items.REDSTONE_TORCH) - .define('A', EIOTags.Items.INGOTS_SOULARIUM) - .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.SOULARIUM_INGOT)) - .save(recipeOutput); - - ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ConduitItems.EXTRACTION_SPEED_UPGRADE_3) - .requires(ConduitItems.EXTRACTION_SPEED_UPGRADE_2) - .requires(Ingredient.of(EIOTags.Items.INGOTS_SOULARIUM), 2) - .unlockedBy("has_ingredient", - InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUCTIVE_ALLOY_INGOT)) - .save(recipeOutput, EnderIO.loc("extraction_speed_upgrade_2_upgrade")); - - ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ConduitItems.EXTRACTION_SPEED_UPGRADE_4.get(), 2) - .pattern("III") - .pattern("APA") - .pattern("ATA") - .define('I', EIOTags.Items.INGOTS_DARK_STEEL) - .define('P', Items.PISTON) - .define('T', Items.REDSTONE_TORCH) - .define('A', EIOTags.Items.INGOTS_ENERGETIC_ALLOY) - .unlockedBy("has_ingredient", - InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.ENERGETIC_ALLOY_INGOT)) - .save(recipeOutput); - - ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ConduitItems.EXTRACTION_SPEED_UPGRADE_4) - .requires(ConduitItems.EXTRACTION_SPEED_UPGRADE_3) - .requires(Ingredient.of(EIOTags.Items.INGOTS_ENERGETIC_ALLOY), 2) - .unlockedBy("has_ingredient", - InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.ENERGETIC_ALLOY_INGOT)) - .save(recipeOutput, EnderIO.loc("extraction_speed_upgrade_3_upgrade")); - } - private void buildFilterRecipes(RecipeOutput recipeOutput) { ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ConduitItems.NOT_FILTER) .define('T', Items.REDSTONE_TORCH) @@ -338,51 +266,51 @@ private void buildFilterConversionRecipes(RecipeOutput recipeOutput) { private void buildFacadeCraftingRecipes(RecipeOutput recipeOutput) { ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ConduitItems.CONDUIT_FACADE) - .pattern("BBB") - .pattern("B B") - .pattern("BBB") - .define('B', EIOItems.CONDUIT_BINDER) - .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) - .save(recipeOutput); + .pattern("BBB") + .pattern("B B") + .pattern("BBB") + .define('B', EIOItems.CONDUIT_BINDER) + .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) + .save(recipeOutput); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ConduitItems.HARDENED_CONDUIT_FACADE) - .pattern(" O ") - .pattern("OFO") - .pattern(" O ") - .define('O', EIOTags.Items.DUSTS_OBSIDIAN) - .define('F', ConduitItems.CONDUIT_FACADE) - .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) - .save(recipeOutput); + .pattern(" O ") + .pattern("OFO") + .pattern(" O ") + .define('O', EIOTags.Items.DUSTS_OBSIDIAN) + .define('F', ConduitItems.CONDUIT_FACADE) + .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) + .save(recipeOutput); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ConduitItems.TRANSPARENT_CONDUIT_FACADE) - .pattern("BBB") - .pattern("BGB") - .pattern("BBB") - .define('B', EIOItems.CONDUIT_BINDER) - .define('G', EIOTags.Items.CLEAR_GLASS) - .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) - .save(recipeOutput); + .pattern("BBB") + .pattern("BGB") + .pattern("BBB") + .define('B', EIOItems.CONDUIT_BINDER) + .define('G', EIOTags.Items.CLEAR_GLASS) + .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) + .save(recipeOutput); ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ConduitItems.TRANSPARENT_CONDUIT_FACADE) - .requires(ConduitItems.CONDUIT_FACADE) - .requires(EIOTags.Items.CLEAR_GLASS) - .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) - .save(recipeOutput, EnderIO.loc("transparent_conduit_facade_from_conduit_facade")); + .requires(ConduitItems.CONDUIT_FACADE) + .requires(EIOTags.Items.CLEAR_GLASS) + .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) + .save(recipeOutput, EnderIO.loc("transparent_conduit_facade_from_conduit_facade")); ShapedRecipeBuilder.shaped(RecipeCategory.MISC, ConduitItems.TRANSPARENT_HARDENED_CONDUIT_FACADE) - .pattern(" O ") - .pattern("OFO") - .pattern(" O ") - .define('O', EIOTags.Items.DUSTS_OBSIDIAN) - .define('F', ConduitItems.TRANSPARENT_CONDUIT_FACADE) - .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) - .save(recipeOutput); + .pattern(" O ") + .pattern("OFO") + .pattern(" O ") + .define('O', EIOTags.Items.DUSTS_OBSIDIAN) + .define('F', ConduitItems.TRANSPARENT_CONDUIT_FACADE) + .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) + .save(recipeOutput); ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ConduitItems.TRANSPARENT_HARDENED_CONDUIT_FACADE) - .requires(ConduitItems.HARDENED_CONDUIT_FACADE) - .requires(EIOTags.Items.CLEAR_GLASS) - .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) - .save(recipeOutput, EnderIO.loc("transparent_hardened_conduit_facade_from_hardened_conduit_facade")); + .requires(ConduitItems.HARDENED_CONDUIT_FACADE) + .requires(EIOTags.Items.CLEAR_GLASS) + .unlockedBy("has_ingredient", InventoryChangeTrigger.TriggerInstance.hasItems(EIOItems.CONDUIT_BINDER)) + .save(recipeOutput, EnderIO.loc("transparent_hardened_conduit_facade_from_hardened_conduit_facade")); } private void buildFacadePaintingRecipes(RecipeOutput recipeOutput) { diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/integration/jade/EIOConduitsJadePlugin.java b/enderio-conduits/src/main/java/com/enderio/conduits/integration/jade/EIOConduitsJadePlugin.java new file mode 100644 index 0000000000..d7c0102131 --- /dev/null +++ b/enderio-conduits/src/main/java/com/enderio/conduits/integration/jade/EIOConduitsJadePlugin.java @@ -0,0 +1,35 @@ +package com.enderio.conduits.integration.jade; + +import com.enderio.conduits.api.bundle.ConduitBundleReader; +import com.enderio.conduits.client.model.conduit.facades.FacadeHelper; +import com.enderio.conduits.common.init.ConduitBlocks; +import snownee.jade.api.BlockAccessor; +import snownee.jade.api.IWailaClientRegistration; +import snownee.jade.api.IWailaPlugin; +import snownee.jade.api.WailaPlugin; + +@WailaPlugin +public class EIOConduitsJadePlugin implements IWailaPlugin { + + // TODO: Could implement stuff like a waila tooltip for bound souls. + + @Override + public void registerClient(IWailaClientRegistration registration) { + // Show the correct conduit (or facade item) + registration.usePickedResult(ConduitBlocks.CONDUIT.get()); + + // Completely replace the block accessor with the facade block if it exists + registration.addRayTraceCallback((hitResult, accessor, originalAccessor) -> { + if (accessor instanceof BlockAccessor blockAccessor) { + if (blockAccessor.getBlockEntity() instanceof ConduitBundleReader conduitBundle + && conduitBundle.hasFacade() && FacadeHelper.areFacadesVisible()) { + return registration.blockAccessor() + .from(blockAccessor) + .blockState(conduitBundle.getFacadeBlock().defaultBlockState()) + .build(); + } + } + return accessor; + }); + } +} diff --git a/enderio-conduits/src/main/java/com/enderio/conduits/common/components/package-info.java b/enderio-conduits/src/main/java/com/enderio/conduits/integration/jade/package-info.java similarity index 66% rename from enderio-conduits/src/main/java/com/enderio/conduits/common/components/package-info.java rename to enderio-conduits/src/main/java/com/enderio/conduits/integration/jade/package-info.java index b21beca9c9..debf9e145c 100644 --- a/enderio-conduits/src/main/java/com/enderio/conduits/common/components/package-info.java +++ b/enderio-conduits/src/main/java/com/enderio/conduits/integration/jade/package-info.java @@ -1,4 +1,4 @@ @javax.annotation.ParametersAreNonnullByDefault @net.minecraft.MethodsReturnNonnullByDefault -package com.enderio.conduits.common.components; +package com.enderio.conduits.integration.jade; diff --git a/enderio-machines/src/main/java/com/enderio/machines/common/block/LegacyMachineBlock.java b/enderio-machines/src/main/java/com/enderio/machines/common/block/LegacyMachineBlock.java index 5fbe4452f5..41abc8c205 100644 --- a/enderio-machines/src/main/java/com/enderio/machines/common/block/LegacyMachineBlock.java +++ b/enderio-machines/src/main/java/com/enderio/machines/common/block/LegacyMachineBlock.java @@ -1,6 +1,5 @@ package com.enderio.machines.common.block; -import com.enderio.base.common.tag.EIOTags; import com.enderio.machines.common.blockentity.base.LegacyMachineBlockEntity; import com.enderio.regilite.holder.RegiliteBlockEntity; import com.mojang.serialization.Codec; @@ -108,13 +107,6 @@ protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Lev return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; } - if (!level.isClientSide && stack.is(EIOTags.Items.WRENCH)) { - var res = machineBlockEntity.onWrenched(player, hit.getDirection()); - if (res != ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION) { - return res; - } - } - var result = machineBlockEntity.onBlockEntityUsed(state, level, pos, player, interactionHand, hit); if (result != ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION) { return result; diff --git a/enderio-machines/src/main/java/com/enderio/machines/common/blockentity/base/LegacyMachineBlockEntity.java b/enderio-machines/src/main/java/com/enderio/machines/common/blockentity/base/LegacyMachineBlockEntity.java index 68e0c6353a..04365434cb 100644 --- a/enderio-machines/src/main/java/com/enderio/machines/common/blockentity/base/LegacyMachineBlockEntity.java +++ b/enderio-machines/src/main/java/com/enderio/machines/common/blockentity/base/LegacyMachineBlockEntity.java @@ -38,6 +38,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.ItemContainerContents; +import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; @@ -535,7 +536,8 @@ public boolean stillValid(Player pPlayer) { @EnsureSide(EnsureSide.Side.CLIENT) @Override - public ItemInteractionResult onWrenched(@Nullable Player player, @Nullable Direction side) { + public ItemInteractionResult onWrenched(UseOnContext context) { + var player = context.getPlayer(); if (player == null || level == null) { return ItemInteractionResult.SUCCESS; } @@ -561,8 +563,8 @@ public ItemInteractionResult onWrenched(@Nullable Player player, @Nullable Direc } else { // Cycle side config if (level.isClientSide()) { - if (side != null && isIOConfigMutable()) { - cycleIOMode(side); + if (isIOConfigMutable()) { + cycleIOMode(context.getClickedFace()); } } } diff --git a/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/block/MachineBlock.java b/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/block/MachineBlock.java index 6befbdee67..65f31d3841 100644 --- a/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/block/MachineBlock.java +++ b/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/block/MachineBlock.java @@ -7,14 +7,10 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; -import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; @@ -76,6 +72,7 @@ protected boolean canOpenMenu() { @Override protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { + // Attempt to open machine menu. if (canOpenMenu()) { // var menuProvider = this.getMenuProvider(state, level, pos); diff --git a/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/block/WrenchableBlockHandler.java b/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/block/WrenchableBlockHandler.java index fe3e4223d7..ad6a950afb 100644 --- a/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/block/WrenchableBlockHandler.java +++ b/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/block/WrenchableBlockHandler.java @@ -19,8 +19,7 @@ public static void onItemUse(UseItemOnBlockEvent event) { } if (level.getBlockEntity(event.getPos()) instanceof Wrenchable blockEntity) { - var direction = event.getUseOnContext().getHitResult().getDirection(); - var result = blockEntity.onWrenched(event.getPlayer(), direction); + var result = blockEntity.onWrenched(event.getUseOnContext()); if (result != ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION) { event.cancelWithResult(result); } diff --git a/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/blockentity/MachineBlockEntity.java b/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/blockentity/MachineBlockEntity.java index ba47aed073..770ee6161b 100644 --- a/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/blockentity/MachineBlockEntity.java +++ b/enderio-machines/src/main/java/com/enderio/machines/common/blocks/base/blockentity/MachineBlockEntity.java @@ -38,9 +38,9 @@ import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.component.ItemContainerContents; +import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -462,7 +462,8 @@ protected boolean isRedstoneBlocked() { // region Wrenchable Implementation @Override - public ItemInteractionResult onWrenched(@Nullable Player player, @Nullable Direction side) { + public ItemInteractionResult onWrenched(UseOnContext context) { + var player = context.getPlayer(); if (player == null || level == null) { return ItemInteractionResult.SUCCESS; } @@ -490,8 +491,8 @@ public ItemInteractionResult onWrenched(@Nullable Player player, @Nullable Direc return ItemInteractionResult.sidedSuccess(level.isClientSide()); } else { if (level.isClientSide()) { - if (side != null && isIOConfigMutable()) { - PacketDistributor.sendToServer(new CycleIOConfigPacket(worldPosition, side)); + if (isIOConfigMutable()) { + PacketDistributor.sendToServer(new CycleIOConfigPacket(worldPosition, context.getClickedFace())); } } diff --git a/enderio-machines/src/main/java/com/enderio/machines/common/init/MachineTravelTargets.java b/enderio-machines/src/main/java/com/enderio/machines/common/init/MachineTravelTargets.java index 2cb1d2ea9a..e75994ceec 100644 --- a/enderio-machines/src/main/java/com/enderio/machines/common/init/MachineTravelTargets.java +++ b/enderio-machines/src/main/java/com/enderio/machines/common/init/MachineTravelTargets.java @@ -13,6 +13,7 @@ public class MachineTravelTargets { public static final DeferredRegister> TRAVEL_TARGET_TYPES = DeferredRegister .create(EnderIORegistries.TRAVEL_TARGET_TYPES, EnderIO.NAMESPACE); + public static final DeferredRegister> TRAVEL_TARGET_SERIALIZERS = DeferredRegister .create(EnderIORegistries.TRAVEL_TARGET_SERIALIZERS, EnderIO.NAMESPACE); diff --git a/enderio/build.gradle.kts b/enderio/build.gradle.kts index f43df0b153..5690509dd0 100644 --- a/enderio/build.gradle.kts +++ b/enderio/build.gradle.kts @@ -34,12 +34,14 @@ dependencies { jarJar(project(":enderio-base")) jarJar(project(":enderio-machines")) jarJar(project(":enderio-conduits")) - jarJar(project(":enderio-conduits-modded")) + // TODO: REENABLE! +// jarJar(project(":enderio-conduits-modded")) jarJar(project(":enderio-armory")) implementation(project(":enderio-base")) implementation(project(":enderio-machines")) implementation(project(":enderio-conduits")) - implementation(project(":enderio-conduits-modded")) + // TODO! +// implementation(project(":enderio-conduits-modded")) implementation(project(":enderio-armory")) // JEI @@ -121,9 +123,10 @@ neoForge { sourceSet(project(":enderio-conduits").sourceSets["main"]) } - create("enderio_conduits_modded") { - sourceSet(project(":enderio-conduits-modded").sourceSets["main"]) - } + // TODO! +// create("enderio_conduits_modded") { +// sourceSet(project(":enderio-conduits-modded").sourceSets["main"]) +// } create("enderio_armory") { sourceSet(project(":enderio-armory").sourceSets["main"]) diff --git a/gradle.properties b/gradle.properties index 48c989b22b..c3052b4ae2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ neoForgeVersion=21.1.58 # The general release series for development build versioning # TODO: Could use this for tag sanity checks during CI builds? -versionSeries=7.1 +versionSeries=7.2 # Version ranges for mods.toml minecraftVersionRange=[1.21.1] diff --git a/settings.gradle b/settings.gradle index 76ead61fdb..05698113ed 100644 --- a/settings.gradle +++ b/settings.gradle @@ -33,7 +33,8 @@ include("enderio-armory") include("enderio-base") include("enderio-machines") include("enderio-conduits") -include("enderio-conduits-modded") +// TODO: Reenable once API is more stable again. +//include("enderio-conduits-modded") // Combined project include("enderio")