From d9d9ca07c6a290d71f4a092295590fc9b948f0c4 Mon Sep 17 00:00:00 2001 From: maxryan008 Date: Tue, 12 Aug 2025 22:17:21 +1000 Subject: [PATCH 01/11] Airlock overhaul using new machine lib api --- .../assets/galacticraft/lang/en_us.json | 5 +- .../tags/block/airlock_blocks.json | 6 + .../java/dev/galacticraft/mod/Constant.java | 2 + .../ingame/AirlockControllerScreen.java | 85 +++- .../mod/content/GCBlockEntityTypes.java | 2 +- .../galacticraft/mod/content/GCBlocks.java | 4 +- .../content/block/entity/AirLockProtocol.java | 226 --------- .../entity/AirlockControllerBlockEntity.java | 466 ++++++++---------- .../machine/airlock/AirlockFrameScanner.java | 372 ++++++++++++++ .../content/block/special/AirlockBlock.java | 100 ---- .../block/special/AirlockSealBlock.java | 39 +- .../mod/data/GCTranslationProvider.java | 4 +- .../mod/data/tag/GCBlockTagProvider.java | 2 + .../galacticraft/mod/network/GCPackets.java | 2 + .../mod/network/GCServerPacketReceivers.java | 1 + .../c2s/AirlockSetProximityPayload.java | 41 ++ .../mod/screen/AirlockControllerMenu.java | 29 +- .../galacticraft/mod/screen/GCMenuTypes.java | 2 +- .../dev/galacticraft/mod/tag/GCBlockTags.java | 2 + .../galacticraft/mod/util/Translations.java | 4 +- .../blockstates/air_lock_seal.json | 10 +- .../models/block/air_lock_seal.json | 12 +- .../models/block/air_lock_seal_ud.json | 23 + 23 files changed, 795 insertions(+), 644 deletions(-) create mode 100644 src/main/generated/data/galacticraft/tags/block/airlock_blocks.json delete mode 100644 src/main/java/dev/galacticraft/mod/content/block/entity/AirLockProtocol.java create mode 100644 src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java delete mode 100644 src/main/java/dev/galacticraft/mod/content/block/special/AirlockBlock.java create mode 100644 src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java create mode 100644 src/main/resources/assets/galacticraft/models/block/air_lock_seal_ud.json diff --git a/src/main/generated/assets/galacticraft/lang/en_us.json b/src/main/generated/assets/galacticraft/lang/en_us.json index b0a3ee1ae8..0cc0ebc5f8 100644 --- a/src/main/generated/assets/galacticraft/lang/en_us.json +++ b/src/main/generated/assets/galacticraft/lang/en_us.json @@ -882,8 +882,11 @@ "tooltip.galacticraft.standard_wrench": "Most Galacticraft machines can be rotated by right-clicking with the Standard Wrench.", "tooltip.galacticraft.time_until_cool": "Time Until Cool: %s", "tooltip.galacticraft.waila_oxygen_tank": "O₂ Tank %s", + "ui.galacticraft.airlock.disable_with_redstone": "Disables from redstone.", + "ui.galacticraft.airlock.disabled": "Airlock Disabled.", + "ui.galacticraft.airlock.enabled": "Airlock Enabled.", + "ui.galacticraft.airlock.open_when_near": "Airlock opens near player.", "ui.galacticraft.airlock.owner": "%s's Airlock Controller", - "ui.galacticraft.airlock.redstone_signal": "Opens on Redstone Signal", "ui.galacticraft.alpha_warning.content1": "Galacticraft is currently in ALPHA.", "ui.galacticraft.alpha_warning.content2": "Please report all issues you find.", "ui.galacticraft.alpha_warning.content3": "Press [ESC] or click to continue.", diff --git a/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json b/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json new file mode 100644 index 0000000000..e131c7b5de --- /dev/null +++ b/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json @@ -0,0 +1,6 @@ +{ + "values": [ + "galacticraft:air_lock_frame", + "galacticraft:air_lock_controller" + ] +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/Constant.java b/src/main/java/dev/galacticraft/mod/Constant.java index d38d87a408..d5b793b54b 100644 --- a/src/main/java/dev/galacticraft/mod/Constant.java +++ b/src/main/java/dev/galacticraft/mod/Constant.java @@ -594,6 +594,8 @@ interface ScreenTexture { ResourceLocation ROCKET_WORKBENCH_SCREEN = id("textures/gui/rocket_workbench.png"); ResourceLocation ROCKET_SELECTION = id("textures/gui/rocket_part_selection.png"); + ResourceLocation AIRLOCK_CONTROLLER_SCREEN = id("textures/gui/airlock_controller_screen.png"); + ResourceLocation PLAYER_INVENTORY_SCREEN = id("textures/gui/player_inventory_screen.png"); ResourceLocation PET_INVENTORY_SCREEN = id("textures/gui/pet_inventory_screen.png"); ResourceLocation ROCKET_INVENTORY = id("textures/gui/rocket.png"); diff --git a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java index 511860cf7b..760af3d85c 100644 --- a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java +++ b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java @@ -22,46 +22,87 @@ package dev.galacticraft.mod.client.gui.screen.ingame; +import dev.galacticraft.mod.network.c2s.AirlockSetProximityPayload; +import dev.galacticraft.machinelib.client.api.screen.MachineScreen; import dev.galacticraft.mod.Constant; -import dev.galacticraft.mod.client.gui.widget.CheckboxButton; +import dev.galacticraft.mod.content.block.entity.AirlockControllerBlockEntity; import dev.galacticraft.mod.screen.AirlockControllerMenu; +import dev.galacticraft.mod.util.DrawableUtil; import dev.galacticraft.mod.util.Translations; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.minecraft.ChatFormatting; -import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Inventory; -public class AirlockControllerScreen extends AbstractContainerScreen { - - private static final ResourceLocation TEXTURE = Constant.id("textures/gui/air_lock_controller.png"); - - - public AirlockControllerScreen(AirlockControllerMenu abstractContainerMenu, Inventory inventory, Component component) { - super(abstractContainerMenu, inventory, component); +public class AirlockControllerScreen extends MachineScreen { + public AirlockControllerScreen(AirlockControllerMenu menu, Inventory inv, Component title) { + super(menu, title, Constant.ScreenTexture.AIRLOCK_CONTROLLER_SCREEN); } @Override protected void init() { super.init(); - addRenderableWidget(new CheckboxButton(this.leftPos + 8, this.topPos + 20)); + this.titleLabelX += 20; + // If MachineLib has its own redstone mode button bar, add it here if needed. } @Override - protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) { - graphics.blit(TEXTURE, this.leftPos, this.topPos, 0, 0, 256, 256); - graphics.drawString(this.font, Component.translatable(Translations.Ui.AIRLOCK_REDSTONE_SIGNAL), this.leftPos + 25, this.topPos + 23, ChatFormatting.DARK_GRAY.getColor(), false); - } + protected void renderMachineBackground(GuiGraphics g, int mouseX, int mouseY, float delta) { + // Status + boolean enabled = this.menu.enabled; + var label = enabled ? Component.translatable(Translations.Ui.AIRLOCK_ENABLED) + : Component.translatable(Translations.Ui.AIRLOCK_DISABLED); + int color = enabled ? ChatFormatting.GREEN.getColor() : ChatFormatting.RED.getColor(); + g.drawString(this.font, label, this.leftPos + 60, this.topPos + 18, color, false); - @Override - protected void renderLabels(GuiGraphics graphics, int i, int j) { - graphics.drawString(this.font, Component.translatable(Translations.Ui.AIRLOCK_OWNER, Minecraft.getInstance().player.getGameProfile().getName()), this.titleLabelX, this.titleLabelY, 4210752, false); + // Proximity (up/down) + int upX = this.leftPos + 158, upY = this.topPos + 59; + int downX = this.leftPos + 158, downY = this.topPos + 69; + boolean hoverUp = DrawableUtil.isWithin(mouseX, mouseY, upX, upY, Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT); + boolean hoverDown = DrawableUtil.isWithin(mouseX, mouseY, downX, downY, Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT); + + g.blit(Constant.ScreenTexture.OVERLAY, upX, upY, + hoverUp ? Constant.TextureCoordinate.ARROW_UP_HOVER_X : Constant.TextureCoordinate.ARROW_UP_X, + hoverUp ? Constant.TextureCoordinate.ARROW_UP_HOVER_Y : Constant.TextureCoordinate.ARROW_UP_Y, + Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT); + + g.blit(Constant.ScreenTexture.OVERLAY, downX, downY, + hoverDown ? Constant.TextureCoordinate.ARROW_DOWN_HOVER_X : Constant.TextureCoordinate.ARROW_DOWN_X, + hoverDown ? Constant.TextureCoordinate.ARROW_DOWN_HOVER_Y : Constant.TextureCoordinate.ARROW_DOWN_Y, + Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT); + + g.drawString(this.font, + Component.translatable(Translations.Ui.AIRLOCK_OPEN_WHEN_NEAR, this.menu.proximityOpen), + this.leftPos + 60, this.topPos + 64, ChatFormatting.DARK_GRAY.getColor(), false); } @Override - public boolean mouseClicked(double d, double e, int i) { - return super.mouseClicked(d, e, i); + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (button == 0) { + // Up + int upX = this.leftPos + 158, upY = this.topPos + 59; + if (DrawableUtil.isWithin(mouseX, mouseY, upX, upY, Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT)) { + if (this.menu.proximityOpen < 5) { + byte next = (byte)(this.menu.proximityOpen + 1); + this.menu.proximityOpen = next; + ClientPlayNetworking.send(new AirlockSetProximityPayload(next)); + this.playButtonSound(); + return true; + } + } + // Down + int downX = this.leftPos + 158, downY = this.topPos + 69; + if (DrawableUtil.isWithin(mouseX, mouseY, downX, downY, Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT)) { + if (this.menu.proximityOpen > 0) { + byte next = (byte)(this.menu.proximityOpen - 1); + this.menu.proximityOpen = next; + ClientPlayNetworking.send(new AirlockSetProximityPayload(next)); + this.playButtonSound(); + return true; + } + } + } + return super.mouseClicked(mouseX, mouseY, button); } -} +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java b/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java index b480bb3d80..885c46e8da 100644 --- a/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java +++ b/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java @@ -77,11 +77,11 @@ public class GCBlockEntityTypes { public static final BlockEntityType CRYOGENIC_CHAMBER = register(Constant.Block.CRYOGENIC_CHAMBER, CryogenicChamberBlockEntity::new, GCBlocks.CRYOGENIC_CHAMBER); public static final BlockEntityType CRYOGENIC_CHAMBER_PART = register(Constant.Block.CRYOGENIC_CHAMBER_PART, CryogenicChamberPartBlockEntity::new, GCBlocks.CRYOGENIC_CHAMBER_PART); public static final BlockEntityType DUNGEON_BOSS_SPAWNER = register(Constant.Block.BOSS_SPAWNER, DungeonSpawnerBlockEntity::new, GCBlocks.BOSS_SPAWNER); + public static final BlockEntityType AIRLOCK_CONTROLLER = register(Constant.Block.AIR_LOCK_CONTROLLER, AirlockControllerBlockEntity::new, GCBlocks.AIR_LOCK_CONTROLLER); // DECORATION public static final BlockEntityType CANNED_FOOD = register(Constant.Block.CANNED_FOOD, CannedFoodBlockEntity::new, GCBlocks.CANNED_FOOD); - public static final BlockEntityType AIRLOCK_CONTROLLER = register(Constant.Block.AIR_LOCK_CONTROLLER, AirlockControllerBlockEntity::new, GCBlocks.AIR_LOCK_CONTROLLER); public static final BlockEntityType ROCKET_WORKBENCH = register(Constant.Block.ROCKET_WORKBENCH, RocketWorkbenchBlockEntity::new, GCBlocks.ROCKET_WORKBENCH); private static BlockEntityType register(String id, BlockEntityType.BlockEntitySupplier supplier, Block... compatibleBlocks) { diff --git a/src/main/java/dev/galacticraft/mod/content/GCBlocks.java b/src/main/java/dev/galacticraft/mod/content/GCBlocks.java index c526775301..05c7100242 100644 --- a/src/main/java/dev/galacticraft/mod/content/GCBlocks.java +++ b/src/main/java/dev/galacticraft/mod/content/GCBlocks.java @@ -361,8 +361,8 @@ public class GCBlocks { public static final Block OXYGEN_STORAGE_MODULE = BLOCKS.registerWithItem(Constant.Block.OXYGEN_STORAGE_MODULE, new ResourceStorageBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GRAY).strength(3.0F, 5.0F).sound(SoundType.METAL).requiresCorrectToolForDrops(), Constant.id(Constant.Block.OXYGEN_STORAGE_MODULE))); public static final Block FOOD_CANNER = BLOCKS.registerWithItem(Constant.Block.FOOD_CANNER, new FoodCannerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).strength(3.0F, 5.0F).sound(SoundType.METAL).mapColor(MapColor.COLOR_GRAY).requiresCorrectToolForDrops())); - public static final AirlockBlock AIR_LOCK_FRAME = BLOCKS.registerWithItem(Constant.Block.AIR_LOCK_FRAME, new AirlockBlock(false, BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK).mapColor(MapColor.COLOR_GRAY))); - public static final AirlockBlock AIR_LOCK_CONTROLLER = BLOCKS.registerWithItem(Constant.Block.AIR_LOCK_CONTROLLER, new AirlockBlock(true, BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK).mapColor(MapColor.COLOR_GRAY))); + public static final Block AIR_LOCK_FRAME = BLOCKS.registerWithItem(Constant.Block.AIR_LOCK_FRAME, new Block(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK).mapColor(MapColor.COLOR_GRAY))); + public static final Block AIR_LOCK_CONTROLLER = BLOCKS.registerWithItem(Constant.Block.AIR_LOCK_CONTROLLER, new SimpleMachineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GRAY).strength(3.0F, 5.0F).sound(SoundType.METAL).requiresCorrectToolForDrops(), Constant.id(Constant.Block.AIR_LOCK_CONTROLLER))); public static final Block AIR_LOCK_SEAL = BLOCKS.register(Constant.Block.AIR_LOCK_SEAL, new AirlockSealBlock(BlockBehaviour.Properties.ofFullCopy(AIR_LOCK_FRAME).strength(-1.0f, 3600000.0f).noLootTable().isValidSpawn(GCBlocks::never))); // TORCHES diff --git a/src/main/java/dev/galacticraft/mod/content/block/entity/AirLockProtocol.java b/src/main/java/dev/galacticraft/mod/content/block/entity/AirLockProtocol.java deleted file mode 100644 index b459ba7afd..0000000000 --- a/src/main/java/dev/galacticraft/mod/content/block/entity/AirLockProtocol.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2019-2025 Team Galacticraft - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package dev.galacticraft.mod.content.block.entity; - -import dev.galacticraft.mod.content.block.special.AirlockBlock; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; - -import java.util.ArrayList; -import java.util.HashSet; - -class AirLockProtocol { - private ArrayList adjacentAirLocks; - private HashSet checked; - private final Level world; - private final BlockEntity head; - private final int maxLoops; - - public int minX = 6000000; - public int maxX = -6000000; - public int minY = 6000000; - public int maxY = -6000000; - public int minZ = 6000000; - public int maxZ = -6000000; - - public AirLockProtocol(BlockEntity head) { - this.adjacentAirLocks = new ArrayList<>(); - this.checked = new HashSet<>(); - this.world = head.getLevel(); - this.head = head; - this.maxLoops = 26; - } - - public boolean isValidFrame(BlockPos pos) { - return this.world.getBlockState(pos).getBlock() instanceof AirlockBlock; - } - - private void loopThrough(BlockPos pos, int loops) { - int xAligned = this.head.getBlockPos().getX(); - int zAligned = this.head.getBlockPos().getZ(); - for (int x = -1; x <= 1; x++) { - int xTest = pos.getX() + x; - for (int z = -1; z <= 1; z++) { - int zTest = pos.getZ() + z; - - if ((xTest == xAligned || zTest == zAligned)) { - for (int y = -1; y <= 1; y++) { - if (!(x == 0 && y == 0 && z == 0)) { - final BlockPos testPos = new BlockPos(xTest, pos.getY() + y, zTest); - if (!this.checked.contains(testPos)) { - this.checked.add(testPos); - if (this.isValidFrame(testPos)) { - this.adjacentAirLocks.add(testPos); - if (loops > 1) { - this.loopThrough(testPos, loops - 1); - } - } - } - } - } - } - } - } - } - - private void loopThroughHorizontal(BlockPos pos, int loops) { - int yTest = pos.getY(); - for (int x = -1; x <= 1; x++) { - int xTest = pos.getX() + x; - for (int z = -1; z <= 1; z++) { - if (!(x == 0 && z == 0)) { - final BlockPos testPos = new BlockPos(xTest, yTest, pos.getZ() + z); - if (!this.checked.contains(testPos)) { - this.checked.add(testPos); - if (this.isValidFrame(testPos)) { - this.adjacentAirLocks.add(testPos); - if (loops > 1) { - this.loopThroughHorizontal(testPos, loops - 1); - } - } - } - } - } - } - } - - public int calculate(boolean horizontal) { - if (this.world.isClientSide()) { - return -1; - } - - this.minX = 6000000; - this.maxX = -6000000; - this.minY = 6000000; - this.maxY = -6000000; - this.minZ = 6000000; - this.maxZ = -6000000; - - this.adjacentAirLocks = new ArrayList<>(); - this.checked.clear(); - final BlockPos headPos = this.head.getBlockPos(); - this.checked.add(headPos); - this.adjacentAirLocks.add(headPos); - - if (horizontal) { - this.loopThroughHorizontal(headPos, this.maxLoops); - } else { - this.loopThrough(headPos, this.maxLoops); - } - - for (final BlockPos airLock : this.adjacentAirLocks) { - if (airLock.getX() < this.minX) { - this.minX = airLock.getX(); - } - - if (airLock.getX() > this.maxX) { - this.maxX = airLock.getX(); - } - - if (airLock.getY() < this.minY) { - this.minY = airLock.getY(); - } - - if (airLock.getY() > this.maxY) { - this.maxY = airLock.getY(); - } - - if (airLock.getZ() < this.minZ) { - this.minZ = airLock.getZ(); - } - - if (airLock.getZ() > this.maxZ) { - this.maxZ = airLock.getZ(); - } - } - - final int count = this.maxX - this.minX + this.maxZ - this.minZ + this.maxY - this.minY; - - if (count > 24 || this.maxX - this.minX <= 1 && this.maxZ - this.minZ <= 1 || !horizontal && this.maxY - this.minY <= 1) { - return -1; - } - - if (horizontal && (this.maxX - this.minX <= 1 || this.maxZ - this.minZ <= 1)) { - return -1; - } - - if (horizontal ? this.incompleteFrameHorizontal() : this.incompleteFrame()) { - return -1; - } - - return this.adjacentAirLocks.size(); - } - - private boolean incompleteFrame() { - for (int y = this.minY + 1; y < this.maxY; y++) { - if (!this.isValidFrame(new BlockPos(this.minX, y, this.minZ))) { - return true; - } else if (!this.isValidFrame(new BlockPos(this.maxX, y, this.maxZ))) { - return true; - } - } - - if (this.minX < this.maxX) { - for (int x = this.minX + 1; x < this.maxX; x++) { - if (!this.isValidFrame(new BlockPos(x, this.maxY, this.maxZ))) { - return true; - } else if (!this.isValidFrame(new BlockPos(x, this.minY, this.maxZ))) { - return true; - } - } - } else if (this.minZ < this.maxZ) { - for (int z = this.minZ + 1; z < this.maxZ; z++) { - if (!this.isValidFrame(new BlockPos(this.maxX, this.maxY, z))) { - return true; - } else if (!this.isValidFrame(new BlockPos(this.maxX, this.minY, z))) { - return true; - } - } - } else { - return true; - } - - return false; - } - - private boolean incompleteFrameHorizontal() { - for (int x = this.minX + 1; x < this.maxX; x++) { - if (!this.isValidFrame(new BlockPos(x, this.maxY, this.maxZ))) { - return true; - } else if (!this.isValidFrame(new BlockPos(x, this.minY, this.maxZ))) { - return true; - } - } - - for (int z = this.minZ + 1; z < this.maxZ; z++) { - if (!this.isValidFrame(new BlockPos(this.maxX, this.maxY, z))) { - return true; - } else if (!this.isValidFrame(new BlockPos(this.maxX, this.minY, z))) { - return true; - } - } - - return false; - } -} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java index e854da4d63..5afe8492b2 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java +++ b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java @@ -22,15 +22,27 @@ package dev.galacticraft.mod.content.block.entity; +import dev.galacticraft.machinelib.api.block.entity.MachineBlockEntity; +import dev.galacticraft.machinelib.api.machine.MachineStatus; +import dev.galacticraft.machinelib.api.machine.configuration.RedstoneMode; +import dev.galacticraft.machinelib.api.storage.MachineEnergyStorage; +import dev.galacticraft.machinelib.api.storage.MachineItemStorage; +import dev.galacticraft.machinelib.api.storage.StorageSpec; +import dev.galacticraft.mod.Galacticraft; import dev.galacticraft.mod.content.GCBlockEntityTypes; import dev.galacticraft.mod.content.GCBlocks; import dev.galacticraft.mod.content.GCSounds; +import dev.galacticraft.mod.content.block.machine.airlock.AirlockFrameScanner; +import dev.galacticraft.mod.machine.GCMachineStatuses; import dev.galacticraft.mod.screen.AirlockControllerMenu; import dev.galacticraft.mod.util.Translations; import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundSource; +import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -38,317 +50,265 @@ 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.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; +import java.util.Collections; import java.util.List; import static dev.galacticraft.mod.content.block.special.AirlockSealBlock.FACING; -public class AirlockControllerBlockEntity extends BlockEntity implements MenuProvider { - public boolean redstoneActivation; - public boolean playerDistanceActivation = true; - public int playerDistanceSelection; - public boolean playerNameMatches; - public String playerToOpenFor = ""; - public boolean invertSelection; - public boolean horizontalModeEnabled; - public boolean lastHorizontalModeEnabled; - public String ownerName = ""; +public class AirlockControllerBlockEntity extends MachineBlockEntity { + // Proximity setting (0..5). 0 = off. + private byte proximityOpen = 0; + // DUMMY storage so MachineLib doesn't crash on zero-slot specs + private static final StorageSpec SPEC = StorageSpec.of( + MachineEnergyStorage.spec( + 0, 0 + ) + ); + + // runtime public boolean active; public boolean lastActive; - private int otherAirLocks; - private int lastOtherAirLocks; - private AirLockProtocol protocol; - private AirLockProtocol lastProtocol; + private List lastFrames = java.util.Collections.emptyList(); public int ticks = 0; - public AirlockControllerBlockEntity(BlockPos blockPos, BlockState blockState) { - super(GCBlockEntityTypes.AIRLOCK_CONTROLLER, blockPos, blockState); + public AirlockControllerBlockEntity(BlockPos pos, BlockState state) { + super(GCBlockEntityTypes.AIRLOCK_CONTROLLER, pos, state, SPEC); } + public byte getProximityOpen() { return this.proximityOpen; } + public void setProximityOpen(byte v) { this.proximityOpen = (byte)Math.max(0, Math.min(5, v)); setChanged(); } - public static void tick(Level level, BlockPos blockPos, BlockState blockState, AirlockControllerBlockEntity blockEntity) { - blockEntity.tick(); + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider lookup) { + super.saveAdditional(tag, lookup); + tag.putByte("ProximityOpen", this.proximityOpen); } - public void tick() { - ticks++; - if (!this.level.isClientSide()) { - this.active = false; + @Override + public void loadAdditional(CompoundTag tag, HolderLookup.Provider lookup) { + super.loadAdditional(tag, lookup); + if (tag.contains("ProximityOpen")) this.proximityOpen = tag.getByte("ProximityOpen"); + } - if (this.redstoneActivation) { - this.active = this.level.getBestNeighborSignal(this.getBlockPos()) > 0; - } + private void serverTick() { + if (this.level == null || this.level.isClientSide()) return; + this.ticks++; - if ((this.active || !this.redstoneActivation) && this.playerDistanceActivation) { - double distance = switch (this.playerDistanceSelection) { - case 0 -> 1.0D; - case 1 -> 2.0D; - case 2 -> 5.0D; - case 3 -> 10.0D; - default -> 0D; - }; - - Vec3 minPos = new Vec3(getBlockPos().getX() + 0.5D - distance, getBlockPos().getY() + 0.5D - distance, getBlockPos().getZ() + 0.5D - distance); - Vec3 maxPos = new Vec3(getBlockPos().getX() + 0.5D + distance, getBlockPos().getY() + 0.5D + distance, getBlockPos().getZ() + 0.5D + distance); - AABB matchingRegion = new AABB(minPos.x, minPos.y, minPos.z, maxPos.x, maxPos.y, maxPos.z); - List playersWithin = this.level.getEntitiesOfClass(Player.class, matchingRegion); - - if (this.playerNameMatches) { - boolean foundPlayer = false; - for (Player p : playersWithin) { - if (p.getUUID().equals(this.playerToOpenFor)) { - foundPlayer = true; - break; - } - } - this.active = foundPlayer; - } else { - this.active = !playersWithin.isEmpty(); - } - } + if (this.ticks % 5 != 0) return; - if (!this.invertSelection) { - this.active = !this.active; - } + // Re-scan frames + List frames = AirlockFrameScanner.scanAll(this.level, this.worldPosition); + boolean framesChanged = !sameFrames(frames, this.lastFrames); - if (this.protocol == null) { - this.protocol = this.lastProtocol = new AirLockProtocol(this); - } + // RedstoneMode: true = machine should be "active" given powered state + boolean powered = this.level.getBestNeighborSignal(this.worldPosition) > 0; + RedstoneMode mode = this.getRedstoneMode(); + boolean redstoneAllows = mode.isActive(powered); - if (this.ticks % 5 == 0) { - if (this.horizontalModeEnabled != this.lastHorizontalModeEnabled) { - this.unsealAirLock(); - } else if (this.active || this.lastActive) { - this.lastOtherAirLocks = this.otherAirLocks; - this.otherAirLocks = this.protocol.calculate(this.horizontalModeEnabled); - - if (this.active) { - if (this.otherAirLocks != this.lastOtherAirLocks || !this.lastActive) { - this.unsealAirLock(); - if (this.otherAirLocks >= 0) { - this.sealAirLock(); - } - } - } else { - if (this.lastActive) { - this.unsealAirLock(); - } + // Proximity: open (disable) only for *authorized* nearby players + boolean near = false; + if (!frames.isEmpty() && this.proximityOpen > 0) { + double r = this.proximityOpen; + AABB big = null; + for (var f : frames) { + AABB expanded = getExpanded(f, r); + big = (big == null) ? expanded : big.minmax(expanded); + } + if (big != null) { + // ONLY count players who are allowed by MachineLib security + var players = this.level.getEntitiesOfClass(Player.class, big); + for (Player p : players) { + if (this.getSecurity().hasAccess(p)) { + near = true; + break; } } + } + } - if (this.active != this.lastActive) { - BlockState state = this.level.getBlockState(this.getBlockPos()); - this.level.sendBlockUpdated(this.getBlockPos(), state, state, 3); - } + boolean newActive = redstoneAllows && !near; + boolean activeChanged = (newActive != this.active); + + if (activeChanged || framesChanged) { + // Unseal old frames + for (var f : this.lastFrames) unseal(f); - this.lastActive = this.active; - this.lastProtocol = this.protocol; - this.lastHorizontalModeEnabled = this.horizontalModeEnabled; + // Seal new if active + if (newActive) { + for (var f : frames) seal(f); } + + this.active = newActive; + this.lastFrames = frames; + + BlockState s = this.level.getBlockState(this.worldPosition); + this.level.sendBlockUpdated(this.worldPosition, s, s, Block.UPDATE_CLIENTS); } } - private void sealAirLock() { - int x = (this.lastProtocol.maxX + this.lastProtocol.minX) / 2; - int y = (this.lastProtocol.maxY + this.lastProtocol.minY) / 2; - int z = (this.lastProtocol.maxZ + this.lastProtocol.minZ) / 2; + private static @NotNull AABB getExpanded(AirlockFrameScanner.Result f, double r) { + AABB interior = switch (f.plane) { + case XY -> new AABB(f.minX + 1, f.minY + 1, f.minZ, f.maxX, f.maxY, f.maxZ); + case XZ -> new AABB(f.minX + 1, f.minY, f.minZ + 1, f.maxX, f.maxY, f.maxZ); + case YZ -> new AABB(f.minX, f.minY + 1, f.minZ + 1, f.maxX, f.maxY, f.maxZ); + }; + interior.inflate(0.0001); // avoid zero-thickness issues on the plane + return interior.inflate(r); + } - boolean facingNorth = (this.lastProtocol.maxX - this.lastProtocol.minX) == 0; + private static boolean sameFrames(List a, List b) { + if (a == b) return true; + if (a == null || b == null || a.size() != b.size()) return false; + for (int i = 0; i < a.size(); i++) { + var x = a.get(i); var y = b.get(i); + if (x.plane != y.plane) return false; + if (x.minX != y.minX || x.minY != y.minY || x.minZ != y.minZ) return false; + if (x.maxX != y.maxX || x.maxY != y.maxY || x.maxZ != y.maxZ) return false; + } + return true; + } - BlockPos pos = new BlockPos(x, y, z); - if (this.level.getBlockState(pos).isAir()) { - this.level.playSound(null, pos, GCSounds.PLAYER_CLOSEAIRLOCK, SoundSource.BLOCKS, 1.0F, 1.0F); + private void seal(AirlockFrameScanner.Result f) { + boolean anyAir = false; + switch (f.plane) { + case XY -> { + int z = f.minZ; + for (int x = f.minX + 1; x <= f.maxX - 1; x++) + for (int y = f.minY + 1; y <= f.maxY - 1; y++) + if (this.level.getBlockState(new BlockPos(x, y, z)).isAir()) anyAir = true; + } + case XZ -> { + int y = f.minY; + for (int x = f.minX + 1; x <= f.maxX - 1; x++) + for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) + if (this.level.getBlockState(new BlockPos(x, y, z)).isAir()) anyAir = true; + } + case YZ -> { + int x = f.minX; + for (int y = f.minY + 1; y <= f.maxY - 1; y++) + for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) + if (this.level.getBlockState(new BlockPos(x, y, z)).isAir()) anyAir = true; + } + } + if (anyAir) { + BlockPos center = new BlockPos((f.minX + f.maxX)/2, (f.minY + f.maxY)/2, (f.minZ + f.maxZ)/2); + this.level.playSound(null, center, GCSounds.PLAYER_CLOSEAIRLOCK, SoundSource.BLOCKS, 1.0F, 1.0F); } - if (this.horizontalModeEnabled) { - if (this.protocol.minY == this.protocol.maxY && this.protocol.minX != this.protocol.maxX && this.protocol.minZ != this.protocol.maxZ) { - for (x = this.protocol.minX + 1; x <= this.protocol.maxX - 1; x++) { - for (z = this.protocol.minZ + 1; z <= this.protocol.maxZ - 1; z++) { - pos = new BlockPos(x, y, z); - if (this.level.getBlockState(pos).isAir()) { - if (facingNorth) { - this.level.setBlock(pos, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, Direction.EAST), Block.UPDATE_ALL); - } else { - this.level.setBlock(pos, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, Direction.NORTH), Block.UPDATE_ALL); - } - } + switch (f.plane) { + case XY -> { + int z = f.minZ; + for (int x = f.minX + 1; x <= f.maxX - 1; x++) + for (int y = f.minY + 1; y <= f.maxY - 1; y++) { + BlockPos p = new BlockPos(x, y, z); + if (this.level.getBlockState(p).isAir()) + this.level.setBlock(p, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, f.sealFacing), Block.UPDATE_ALL); } - } } - } else { - if (this.protocol.minX != this.protocol.maxX) { - for (x = this.protocol.minX + 1; x <= this.protocol.maxX - 1; x++) { - for (y = this.protocol.minY + 1; y <= this.protocol.maxY - 1; y++) { - pos = new BlockPos(x, y, z); - if (this.level.getBlockState(pos).isAir()) { - if (facingNorth) { - this.level.setBlock(pos, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, Direction.EAST), Block.UPDATE_ALL); - } else { - this.level.setBlock(pos, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, Direction.NORTH), Block.UPDATE_ALL); - } - } + case XZ -> { + int y = f.minY; + for (int x = f.minX + 1; x <= f.maxX - 1; x++) + for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) { + BlockPos p = new BlockPos(x, y, z); + if (this.level.getBlockState(p).isAir()) + this.level.setBlock(p, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, f.sealFacing), Block.UPDATE_ALL); } - } - } else if (this.protocol.minZ != this.protocol.maxZ) { - for (z = this.protocol.minZ + 1; z <= this.protocol.maxZ - 1; z++) { - for (y = this.protocol.minY + 1; y <= this.protocol.maxY - 1; y++) { - pos = new BlockPos(x, y, z); - if (this.level.getBlockState(pos).isAir()) { - if (facingNorth) { - this.level.setBlock(pos, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, Direction.EAST), Block.UPDATE_ALL); - } else { - this.level.setBlock(pos, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, Direction.NORTH), Block.UPDATE_ALL); - } - } + } + case YZ -> { + int x = f.minX; + for (int y = f.minY + 1; y <= f.maxY - 1; y++) + for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) { + BlockPos p = new BlockPos(x, y, z); + if (this.level.getBlockState(p).isAir()) + this.level.setBlock(p, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, f.sealFacing), Block.UPDATE_ALL); } - } } } } - public void unsealAirLock() { - if (this.lastProtocol == null) { - return; - } - - int x = this.lastProtocol.minX + (this.lastProtocol.maxX - this.lastProtocol.minX) / 2; - int y = this.lastProtocol.minY + (this.lastProtocol.maxY - this.lastProtocol.minY) / 2; - int z = this.lastProtocol.minZ + (this.lastProtocol.maxZ - this.lastProtocol.minZ) / 2; - - BlockPos pos = new BlockPos(x, y, z); - if (this.level.getBlockState(pos).is(GCBlocks.AIR_LOCK_SEAL)) { - this.level.playSound(null, pos, GCSounds.PLAYER_OPENAIRLOCK, SoundSource.BLOCKS, 1.0F, 1.0F); - } - - boolean sealedSide = false; - boolean breathable; - if (this.lastHorizontalModeEnabled) { - if (this.protocol.minY == this.protocol.maxY && this.protocol.minX != this.protocol.maxX && this.protocol.minZ != this.protocol.maxZ) { - // First test if there is sealed air to either side - for (x = this.protocol.minX + 1; x <= this.protocol.maxX - 1; x++) { - for (z = this.protocol.minZ + 1; z <= this.protocol.maxZ - 1; z++) { - pos = new BlockPos(x, y, z); - breathable = this.level.isBreathable(pos.above()); - if (breathable) { - if (this.level.getBlockState(pos).getBlock() == GCBlocks.AIR_LOCK_SEAL) { - sealedSide = true; - break; - } - continue; - } - breathable = this.level.isBreathable(pos.below()); - if (breathable) { - if (this.level.getBlockState(pos).getBlock() == GCBlocks.AIR_LOCK_SEAL) { - sealedSide = true; - break; - } + public void unseal(AirlockFrameScanner.Result f) { + boolean hadSeal = false; + switch (f.plane) { + case XY -> { + int z = f.minZ; + for (int x = f.minX + 1; x <= f.maxX - 1; x++) + for (int y = f.minY + 1; y <= f.maxY - 1; y++) { + BlockPos p = new BlockPos(x, y, z); + if (this.level.getBlockState(p).is(GCBlocks.AIR_LOCK_SEAL)) { + hadSeal = true; + this.level.setBlock(p, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); } } - if (sealedSide) - break; - } - // Now replace the airlock blocks with either air, or sealed air - for (x = this.protocol.minX + 1; x <= this.protocol.maxX - 1; x++) { - for (z = this.protocol.minZ + 1; z <= this.protocol.maxZ - 1; z++) { - pos = new BlockPos(x, y, z); - if (this.level.getBlockState(pos).getBlock() == GCBlocks.AIR_LOCK_SEAL) { - if (sealedSide) - this.level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); - else - this.level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); - } - } - } } - } else { - if (this.lastProtocol.minX != this.lastProtocol.maxX) { - // First test if there is sealed air to either side - for (x = this.lastProtocol.minX + 1; x <= this.lastProtocol.maxX - 1; x++) { - for (y = this.lastProtocol.minY + 1; y <= this.lastProtocol.maxY - 1; y++) { - pos = new BlockPos(x, y, z); - breathable = this.level.isBreathable(pos.north()); - if (breathable) { - if (this.level.getBlockState(pos).is(GCBlocks.AIR_LOCK_SEAL)) { - sealedSide = true; - break; - } - continue; - } - breathable = this.level.isBreathable(pos.south()); - if (breathable) { - if (this.level.getBlockState(pos).is(GCBlocks.AIR_LOCK_SEAL)) { - sealedSide = true; - break; - } - } - } - if (sealedSide) - break; - } - // Now replace the airlock blocks with either air, or sealed air - for (x = this.lastProtocol.minX + 1; x <= this.lastProtocol.maxX - 1; x++) { - for (y = this.lastProtocol.minY + 1; y <= this.lastProtocol.maxY - 1; y++) { - pos = new BlockPos(x, y, z); - if (this.level.getBlockState(pos).is(GCBlocks.AIR_LOCK_SEAL)) { - if (sealedSide) - this.level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); - else - this.level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); - } - } - } - } else if (this.lastProtocol.minZ != this.lastProtocol.maxZ) { - // First test if there is sealed air to either side - for (z = this.lastProtocol.minZ + 1; z <= this.lastProtocol.maxZ - 1; z++) { - for (y = this.lastProtocol.minY + 1; y <= this.lastProtocol.maxY - 1; y++) { - pos = new BlockPos(x, y, z); - breathable = this.level.isBreathable(pos.west()); - if (breathable) { - if (this.level.getBlockState(pos).is(GCBlocks.AIR_LOCK_SEAL)) { - sealedSide = true; - break; - } - continue; - } - breathable = this.level.isBreathable(pos.east()); - if (breathable) { - if (this.level.getBlockState(pos).is(GCBlocks.AIR_LOCK_SEAL)) { - sealedSide = true; - break; - } + case XZ -> { + int y = f.minY; + for (int x = f.minX + 1; x <= f.maxX - 1; x++) + for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) { + BlockPos p = new BlockPos(x, y, z); + if (this.level.getBlockState(p).is(GCBlocks.AIR_LOCK_SEAL)) { + hadSeal = true; + this.level.setBlock(p, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); } } - if (sealedSide) - break; - } - // Now replace the airlock blocks with either air, or sealed air - for (z = this.lastProtocol.minZ + 1; z <= this.lastProtocol.maxZ - 1; z++) { - for (y = this.lastProtocol.minY + 1; y <= this.lastProtocol.maxY - 1; y++) { - pos = new BlockPos(x, y, z); - if (this.level.getBlockState(pos).is(GCBlocks.AIR_LOCK_SEAL)) { - if (sealedSide) - this.level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); - else - this.level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); + } + case YZ -> { + int x = f.minX; + for (int y = f.minY + 1; y <= f.maxY - 1; y++) + for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) { + BlockPos p = new BlockPos(x, y, z); + if (this.level.getBlockState(p).is(GCBlocks.AIR_LOCK_SEAL)) { + hadSeal = true; + this.level.setBlock(p, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); } } - } } } + if (hadSeal) { + BlockPos center = new BlockPos((f.minX + f.maxX)/2, (f.minY + f.maxY)/2, (f.minZ + f.maxZ)/2); + this.level.playSound(null, center, GCSounds.PLAYER_OPENAIRLOCK, SoundSource.BLOCKS, 1.0F, 1.0F); + } } @Override public @NotNull Component getDisplayName() { - return Component.translatable(Translations.Ui.AIRLOCK_OWNER, ownerName); + return Component.translatable(Translations.Ui.AIRLOCK_OWNER, ""); // fill in owner if you track it + } + + @Override + protected @NotNull MachineStatus tick(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { + serverTick(); + return GCMachineStatuses.SEALED; + } + + @Override + protected void tickDisabled(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { + serverTick(); + + super.tickDisabled(level, pos, state, profiler); } @Override public AirlockControllerMenu createMenu(int syncId, Inventory inventory, Player player) { - return new AirlockControllerMenu(syncId, inventory); + return new AirlockControllerMenu(syncId, player, this); + } + + public List getLastFrames() { + return this.lastFrames; + } + + /** Unseal on BE removal (block broken / replaced). */ + @Override + public void setRemoved() { + if (!this.level.isClientSide() && this.lastFrames != null) { + for (var f : this.lastFrames) unseal(f); + } + super.setRemoved(); } -} +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java b/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java new file mode 100644 index 0000000000..9ee72e343c --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java @@ -0,0 +1,372 @@ +package dev.galacticraft.mod.content.block.machine.airlock; + +import dev.galacticraft.mod.content.GCBlocks; +import dev.galacticraft.mod.content.block.entity.AirlockControllerBlockEntity; +import dev.galacticraft.mod.tag.GCBlockTags; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; +import net.minecraft.core.HolderSet; +import net.minecraft.world.level.Level; + +import java.util.*; + +/** + * Finds the smallest possible rectangular frame(s) on each axis-aligned plane that include the controller + * on the perimeter (not a corner). Supports up to two rectangles per plane (controller edge used by two frames), + * and returns all planes (XY, XZ, YZ). + */ +public final class AirlockFrameScanner { + + public static final Comparator ORDER = Comparator + .comparing((Result r) -> r.plane.ordinal()) + .thenComparingInt(r -> r.minX).thenComparingInt(r -> r.minY).thenComparingInt(r -> r.minZ) + .thenComparingInt(r -> r.maxX).thenComparingInt(r -> r.maxY).thenComparingInt(r -> r.maxZ); + + public enum Plane { + XY(Axis.Z), // z is constant + XZ(Axis.Y), // y is constant + YZ(Axis.X); // x is constant + final Axis normal; + Plane(Axis normal) { this.normal = normal; } + } + + public static final class Result { + public final Plane plane; + public final int minX, minY, minZ, maxX, maxY, maxZ; + public final Direction sealFacing; // FACING for AIR_LOCK_SEAL + + Result(Plane plane, + int minX, int minY, int minZ, + int maxX, int maxY, int maxZ, + Direction sealFacing) { + this.plane = plane; + this.minX = minX; this.minY = minY; this.minZ = minZ; + this.maxX = maxX; this.maxY = maxY; this.maxZ = maxZ; + this.sealFacing = sealFacing; + } + } + + private static boolean isFrame(Level level, BlockPos pos) { + return level.getBlockState(pos).getBlock().defaultBlockState().is(GCBlockTags.AIRLOCK_BLOCKS); + } + private static boolean isController(Level level, BlockPos pos) { + return level.getBlockEntity(pos) instanceof AirlockControllerBlockEntity; + } + + /** Scan all planes; return up to two smallest rectangles per plane. */ + public static List scanAll(Level level, BlockPos controller) { + if (!isFrame(level, controller)) return List.of(); + List out = new ArrayList<>(6); + for (Plane plane : Plane.values()) { + out.addAll(scanPlane(level, controller, plane)); + } + out.sort(ORDER); // stable order across ticks + return out; + } + + /** Back-compat helper if you still call scan(): returns first found or invalid. */ + public static Result scan(Level level, BlockPos controller) { + List all = scanAll(level, controller); + return all.isEmpty() + ? new Result(Plane.XY, 0,0,0, 0,0,0, Direction.NORTH) + : all.get(0); + } + + // ---------- Plane scanning ---------- + + private record Axes(Axis u, Axis v, Axis wConst) {} + + private static List scanPlane(Level level, BlockPos controller, Plane plane) { + final int fixed = coord(controller, plane.normal); + final Axes axes = axesFor(plane); // u,v are the in-plane axes + + // 1) Gather all frame blocks connected to controller within this plane + Set frames = floodInPlane(level, controller, plane, fixed); + if (frames.isEmpty()) return List.of(); + + // Require exactly one controller in this connected set + int controllers = 0; + for (BlockPos p : frames) if (isController(level, p)) controllers++; + if (controllers != 1) return List.of(); + + // 2) in-plane coords of controller + int u0 = proj(controller, axes.u); + int v0 = proj(controller, axes.v); + + // Search bounds (tight box around connected set) + int minU = Integer.MAX_VALUE, maxU = Integer.MIN_VALUE, minV = Integer.MAX_VALUE, maxV = Integer.MIN_VALUE; + for (BlockPos p : frames) { + int u = proj(p, axes.u), v = proj(p, axes.v); + if (u < minU) minU = u; if (u > maxU) maxU = u; + if (v < minV) minV = v; if (v > maxV) maxV = v; + } + + // 3) Find up to two rectangles with controller on an edge + Result bestVMin = findBestRectWithFixedEdge(level, frames, plane, axes, fixed, u0, v0, EdgeKind.VMIN, minU, maxU, minV, maxV); + Result bestVMax = findBestRectWithFixedEdge(level, frames, plane, axes, fixed, u0, v0, EdgeKind.VMAX, minU, maxU, minV, maxV); + Result bestUMin = findBestRectWithFixedEdge(level, frames, plane, axes, fixed, u0, v0, EdgeKind.UMIN, minU, maxU, minV, maxV); + Result bestUMax = findBestRectWithFixedEdge(level, frames, plane, axes, fixed, u0, v0, EdgeKind.UMAX, minU, maxU, minV, maxV); + + List planeOut = new ArrayList<>(2); + addIfNotNullDistinct(planeOut, bestVMin); + addIfNotNullDistinct(planeOut, bestVMax); + if (planeOut.size() < 2) addIfNotNullDistinct(planeOut, bestUMin); + if (planeOut.size() < 2) addIfNotNullDistinct(planeOut, bestUMax); + + planeOut.sort(ORDER); // stable per plane + return planeOut; + } + + // ---------- Plane scanning ---------- + private enum EdgeKind { VMIN, VMAX, UMIN, UMAX } + + private static void addIfNotNullDistinct(List list, Result r) { + if (r == null) return; + for (Result e : list) { + if (r.plane == e.plane && + r.minX == e.minX && r.minY == e.minY && r.minZ == e.minZ && + r.maxX == e.maxX && r.maxY == e.maxY && r.maxZ == e.maxZ) { + return; + } + } + if (list.size() < 2) list.add(r); + } + + private static Result findBestRectWithFixedEdge( + Level level, Set frames, Plane plane, Axes axes, int fixed, + int u0, int v0, EdgeKind kind, + int minU, int maxU, int minV, int maxV + ) { + // Fix one edge to pass through the controller (so controller sits on that edge). + final boolean edgeIsAlongU; // true if the fixed edge is a U-span (constant v), false if a V-span (constant u) + switch (kind) { + case VMIN, VMAX -> edgeIsAlongU = true; // edge is v == v0, spans in U + case UMIN, UMAX -> edgeIsAlongU = false; // edge is u == u0, spans in V + default -> throw new IllegalStateException(); + } + + long bestArea = Long.MAX_VALUE; + int bestMinU = 0, bestMaxU = 0, bestMinV = 0, bestMaxV = 0; + + if (edgeIsAlongU) { + // Controller sits on v == v0 edge; search vOpp on the other side. + if (kind == EdgeKind.VMIN) { + // expand toward +V (vOpp > v0) + for (int vOpp = v0 + 1; vOpp <= maxV; vOpp++) { + // sweep U around the controller, ensuring the fixed edge is continuous + for (int uMin = u0; uMin >= minU; uMin--) { + if (!isFrame(level, unproj(uMin, v0, fixed, axes))) break; + for (int uMax = u0; uMax <= maxU; uMax++) { + if (!isFrame(level, unproj(uMax, v0, fixed, axes))) break; + if ((uMax - uMin + 1) < 3 || (vOpp - v0 + 1) < 3) continue; + + int vMinRect = v0; + int vMaxRect = vOpp; + + if (perimeterIsFrames(frames, axes, fixed, uMin, vMinRect, uMax, vMaxRect) + && interiorHasNoFrames(frames, axes, fixed, uMin, vMinRect, uMax, vMaxRect)) { + long area = (long)(uMax - uMin + 1) * (long)(vMaxRect - vMinRect + 1); + if (area < bestArea) { + bestArea = area; + bestMinU = uMin; bestMaxU = uMax; + bestMinV = vMinRect; bestMaxV = vMaxRect; + } + } + } + } + } + } else { // VMAX: expand toward -V (vOpp < v0) --- FIXED LOOPS --- + for (int vOpp = v0 - 1; vOpp >= minV; vOpp--) { + for (int uMin = u0; uMin >= minU; uMin--) { + if (!isFrame(level, unproj(uMin, v0, fixed, axes))) break; + for (int uMax = u0; uMax <= maxU; uMax++) { + if (!isFrame(level, unproj(uMax, v0, fixed, axes))) break; + if ((uMax - uMin + 1) < 3 || (v0 - vOpp + 1) < 3) continue; + + int vMinRect = vOpp; + int vMaxRect = v0; + + if (perimeterIsFrames(frames, axes, fixed, uMin, vMinRect, uMax, vMaxRect) + && interiorHasNoFrames(frames, axes, fixed, uMin, vMinRect, uMax, vMaxRect)) { + long area = (long)(uMax - uMin + 1) * (long)(vMaxRect - vMinRect + 1); + if (area < bestArea) { + bestArea = area; + bestMinU = uMin; bestMaxU = uMax; + bestMinV = vMinRect; bestMaxV = vMaxRect; + } + } + } + } + } + } + } else { + // Controller sits on u == u0 edge; search uOpp on the other side. + if (kind == EdgeKind.UMIN) { + // expand toward +U (uOpp > u0) + for (int uOpp = u0 + 1; uOpp <= maxU; uOpp++) { + for (int vMin2 = v0; vMin2 >= minV; vMin2--) { + if (!isFrame(level, unproj(u0, vMin2, fixed, axes))) break; + for (int vMax2 = v0; vMax2 <= maxV; vMax2++) { + if (!isFrame(level, unproj(u0, vMax2, fixed, axes))) break; + if ((uOpp - u0 + 1) < 3 || (vMax2 - vMin2 + 1) < 3) continue; + + int uMinRect = u0; + int uMaxRect = uOpp; + + if (perimeterIsFrames(frames, axes, fixed, uMinRect, vMin2, uMaxRect, vMax2) + && interiorHasNoFrames(frames, axes, fixed, uMinRect, vMin2, uMaxRect, vMax2)) { + long area = (long)(uMaxRect - uMinRect + 1) * (long)(vMax2 - vMin2 + 1); + if (area < bestArea) { + bestArea = area; + bestMinU = uMinRect; bestMaxU = uMaxRect; + bestMinV = vMin2; bestMaxV = vMax2; + } + } + } + } + } + } else { // UMAX: expand toward -U (uOpp < u0) --- FIXED LOOPS --- + for (int uOpp = u0 - 1; uOpp >= minU; uOpp--) { + for (int vMin2 = v0; vMin2 >= minV; vMin2--) { + if (!isFrame(level, unproj(u0, vMin2, fixed, axes))) break; + for (int vMax2 = v0; vMax2 <= maxV; vMax2++) { + if (!isFrame(level, unproj(u0, vMax2, fixed, axes))) break; + if ((u0 - uOpp + 1) < 3 || (vMax2 - vMin2 + 1) < 3) continue; + + int uMinRect = uOpp; + int uMaxRect = u0; + + if (perimeterIsFrames(frames, axes, fixed, uMinRect, vMin2, uMaxRect, vMax2) + && interiorHasNoFrames(frames, axes, fixed, uMinRect, vMin2, uMaxRect, vMax2)) { + long area = (long)(uMaxRect - uMinRect + 1) * (long)(vMax2 - vMin2 + 1); + if (area < bestArea) { + bestArea = area; + bestMinU = uMinRect; bestMaxU = uMaxRect; + bestMinV = vMin2; bestMaxV = vMax2; + } + } + } + } + } + } + } + + if (bestArea == Long.MAX_VALUE) return null; + + // Map u/v bounds back to xyz bounds + int minX, minY, minZ, maxX, maxY, maxZ; + switch (plane) { + case XY -> { + minX = bestMinU; maxX = bestMaxU; + minY = bestMinV; maxY = bestMaxV; + minZ = maxZ = fixed; + } + case XZ -> { + minX = bestMinU; maxX = bestMaxU; + minZ = bestMinV; maxZ = bestMaxV; + minY = maxY = fixed; + } + case YZ -> { + minY = bestMinU; maxY = bestMaxU; + minZ = bestMinV; maxZ = bestMaxV; + minX = maxX = fixed; + } + default -> throw new IllegalStateException(); + } + + // Seal facing = plane normal direction + Direction facing = switch (plane) { + case XY -> Direction.NORTH; // ⟂ Z + case XZ -> Direction.UP; // ⟂ Y + case YZ -> Direction.EAST; // ⟂ X + }; + + return new Result(plane, minX, minY, minZ, maxX, maxY, maxZ, facing); + } + + // ---------- Geometry helpers ---------- + + private static Axes axesFor(Plane plane) { + return switch (plane) { + case XY -> new Axes(Axis.X, Axis.Y, Axis.Z); + case XZ -> new Axes(Axis.X, Axis.Z, Axis.Y); + case YZ -> new Axes(Axis.Y, Axis.Z, Axis.X); + }; + } + + private static Set floodInPlane(Level level, BlockPos start, Plane plane, int fixed) { + Set visited = new HashSet<>(); + ArrayDeque q = new ArrayDeque<>(); + q.add(start); + visited.add(start); + for (int guard = 0; guard < 32768 && !q.isEmpty(); guard++) { + BlockPos p = q.removeFirst(); + for (Direction d : inPlaneDirections(plane)) { + BlockPos n = p.relative(d); + if (coord(n, plane.normal) != fixed) continue; + if (!visited.contains(n) && isFrame(level, n)) { + visited.add(n); + q.add(n); + } + } + } + return visited; + } + + private static Direction[] inPlaneDirections(Plane plane) { + return switch (plane) { + case XY -> new Direction[]{Direction.EAST, Direction.WEST, Direction.UP, Direction.DOWN}; + case XZ -> new Direction[]{Direction.EAST, Direction.WEST, Direction.NORTH, Direction.SOUTH}; + case YZ -> new Direction[]{Direction.NORTH, Direction.SOUTH, Direction.UP, Direction.DOWN}; + }; + } + + private static int coord(BlockPos p, Axis a) { + return switch (a) { + case X -> p.getX(); + case Y -> p.getY(); + case Z -> p.getZ(); + }; + } + private static int proj(BlockPos p, Axis axis) { + return coord(p, axis); + } + private static BlockPos unproj(int u, int v, int wConst, Axes axes) { + // inverse mapping from (u,v) back to (x,y,z) + int x=0,y=0,z=0; + for (Axis a : new Axis[]{axes.u, axes.v, axes.wConst}) { + int val = (a == axes.u) ? u : (a == axes.v) ? v : wConst; + switch (a) { + case X -> x = val; + case Y -> y = val; + case Z -> z = val; + } + } + return new BlockPos(x,y,z); + } + + private static boolean perimeterIsFrames(Set frames, Axes axes, int wConst, + int uMin, int vMin, int uMax, int vMax) { + // edges: u in [uMin..uMax] at v=vMin and v=vMax; v in [vMin..vMax] at u=uMin and u=uMax + for (int u = uMin; u <= uMax; u++) { + if (!frames.contains(unproj(u, vMin, wConst, axes))) return false; + if (!frames.contains(unproj(u, vMax, wConst, axes))) return false; + } + for (int v = vMin; v <= vMax; v++) { + if (!frames.contains(unproj(uMin, v, wConst, axes))) return false; + if (!frames.contains(unproj(uMax, v, wConst, axes))) return false; + } + return true; + } + + private static boolean interiorHasNoFrames(Set frames, Axes axes, int wConst, + int uMin, int vMin, int uMax, int vMax) { + for (int u = uMin + 1; u <= uMax - 1; u++) { + for (int v = vMin + 1; v <= vMax - 1; v++) { + if (frames.contains(unproj(u, v, wConst, axes))) return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/content/block/special/AirlockBlock.java b/src/main/java/dev/galacticraft/mod/content/block/special/AirlockBlock.java deleted file mode 100644 index a659ae3c42..0000000000 --- a/src/main/java/dev/galacticraft/mod/content/block/special/AirlockBlock.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2019-2025 Team Galacticraft - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package dev.galacticraft.mod.content.block.special; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.galacticraft.mod.content.GCBlockEntityTypes; -import dev.galacticraft.mod.content.block.entity.AirlockControllerBlockEntity; -import net.minecraft.core.BlockPos; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.BaseEntityBlock; -import net.minecraft.world.level.block.RenderShape; -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.phys.BlockHitResult; -import org.jetbrains.annotations.Nullable; - -public class AirlockBlock extends BaseEntityBlock { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - Codec.BOOL.fieldOf("controller").forGetter(airlockBlock -> airlockBlock.controller), - propertiesCodec() - ).apply(instance, AirlockBlock::new)); - private final boolean controller; - - public AirlockBlock(boolean controller, Properties properties) { - super(properties); - this.controller = controller; - } - - @Nullable - @Override - public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { - if (this.controller) - return new AirlockControllerBlockEntity(blockPos, blockState); - return null; - } - - @Override - public void onRemove(BlockState blockState, Level level, BlockPos blockPos, BlockState newState, boolean moved) { - if (this.controller) { - BlockEntity blockEntity = level.getBlockEntity(blockPos); - if (blockEntity instanceof AirlockControllerBlockEntity airlockController) { - airlockController.unsealAirLock(); - } - } - super.onRemove(blockState, level, blockPos, newState, moved); - } - - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hit) { - if (!this.controller) - return InteractionResult.PASS; - if (level.isClientSide) { - return InteractionResult.SUCCESS; - } else { - player.openMenu(state.getMenuProvider(level, pos)); - return InteractionResult.CONSUME; - } - } - - @Override - public BlockEntityTicker getTicker(Level level, BlockState blockState, BlockEntityType blockEntityType) { - return createTickerHelper(blockEntityType, GCBlockEntityTypes.AIRLOCK_CONTROLLER, AirlockControllerBlockEntity::tick); - } - - @Override - protected MapCodec codec() { - return CODEC; - } - - @Override - public RenderShape getRenderShape(BlockState blockState) { - return RenderShape.MODEL; - } -} diff --git a/src/main/java/dev/galacticraft/mod/content/block/special/AirlockSealBlock.java b/src/main/java/dev/galacticraft/mod/content/block/special/AirlockSealBlock.java index f6c77649c6..fe5fdebf68 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/special/AirlockSealBlock.java +++ b/src/main/java/dev/galacticraft/mod/content/block/special/AirlockSealBlock.java @@ -28,6 +28,7 @@ import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; @@ -38,8 +39,7 @@ public class AirlockSealBlock extends Block { public static final MapCodec CODEC = simpleCodec(AirlockSealBlock::new); - public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; - + public static final DirectionProperty FACING = BlockStateProperties.FACING; @Override public MapCodec codec() { @@ -52,31 +52,38 @@ public AirlockSealBlock(Properties properties) { } - protected static final VoxelShape SHAPE_NORTH = Block.box(0.0, 0.0, 4.0, 16.0, 16.0, 12.0); - protected static final VoxelShape SHAPE_EAST = Block.box(4.0, 0.0, 0.0, 12.0, 16.0, 16.0); + // 8px-thick plate centered in the block, oriented per face normal + private static final VoxelShape SHAPE_NORTH_SOUTH = Block.box(0.0, 0.0, 4.0, 16.0, 16.0, 12.0); // limits Z + private static final VoxelShape SHAPE_EAST_WEST = Block.box(4.0, 0.0, 0.0, 12.0, 16.0, 16.0); // limits X + private static final VoxelShape SHAPE_UP_DOWN = Block.box(0.0, 4.0, 0.0, 16.0, 12.0, 16.0); // limits Y @Override - public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { - if (state.getValue(FACING) == Direction.NORTH || state.getValue(FACING) == Direction.SOUTH) { - return SHAPE_NORTH; - } else { - return SHAPE_EAST; - } + public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext ctx) { + return switch (state.getValue(FACING)) { + case NORTH, SOUTH -> SHAPE_NORTH_SOUTH; + case EAST, WEST -> SHAPE_EAST_WEST; + case UP, DOWN -> SHAPE_UP_DOWN; // <-- support facing up/down + }; } @Override - public BlockState rotate(BlockState blockState, Rotation rotation) { - return blockState.setValue(FACING, rotation.rotate(blockState.getValue(FACING))); + public BlockState rotate(BlockState state, Rotation rotation) { + return state.setValue(FACING, rotation.rotate(state.getValue(FACING))); } @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(FACING); + public BlockState mirror(BlockState state, Mirror mirror) { + return state.rotate(mirror.getRotation(state.getValue(FACING))); } @Override - public BlockState getStateForPlacement(BlockPlaceContext blockPlaceContext) { - return this.defaultBlockState().setValue(FACING, blockPlaceContext.getHorizontalDirection().getOpposite()); + protected void createBlockStateDefinition(StateDefinition.Builder b) { + b.add(FACING); } + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + Direction look = ctx.getNearestLookingDirection().getOpposite(); + return this.defaultBlockState().setValue(FACING, look); + } } \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java index 3490f31602..1d389ffbf8 100644 --- a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java @@ -1126,7 +1126,9 @@ protected void generateUiTranslations() { this.add(Ui.UPGRADE, "Upgrade"); this.add(Ui.COLOR, "Color"); - this.add(Ui.AIRLOCK_REDSTONE_SIGNAL, "Opens on Redstone Signal"); + this.add(Ui.AIRLOCK_ENABLED, "Airlock Enabled."); + this.add(Ui.AIRLOCK_DISABLED, "Airlock Disabled."); + this.add(Ui.AIRLOCK_OPEN_WHEN_NEAR, "Airlock opens near player."); this.add(Ui.AIRLOCK_OWNER, "%s's Airlock Controller"); this.add(Ui.ALPHA_WARNING_1, "Galacticraft is currently in ALPHA."); this.add(Ui.ALPHA_WARNING_2, "Please report all issues you find."); diff --git a/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java b/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java index 0550f5f535..01ab7a9ffa 100644 --- a/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java @@ -398,6 +398,8 @@ protected void addTags(HolderLookup.Provider provider) { this.tag(GCBlockTags.STAINED_GLASS_FLUID_PIPES).add(pipe); } + this.tag(GCBlockTags.AIRLOCK_BLOCKS).add(GCBlocks.AIR_LOCK_FRAME, GCBlocks.AIR_LOCK_CONTROLLER); + this.tag(ConventionalBlockTags.VILLAGER_JOB_SITES) .add(GCBlocks.LUNAR_CARTOGRAPHY_TABLE); diff --git a/src/main/java/dev/galacticraft/mod/network/GCPackets.java b/src/main/java/dev/galacticraft/mod/network/GCPackets.java index a70dd8cb83..f89bfc3557 100644 --- a/src/main/java/dev/galacticraft/mod/network/GCPackets.java +++ b/src/main/java/dev/galacticraft/mod/network/GCPackets.java @@ -44,5 +44,7 @@ public static void register() { PayloadTypeRegistry.playC2S().register(OpenRocketPayload.TYPE, OpenRocketPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(PlanetTeleportPayload.TYPE, PlanetTeleportPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(SatelliteCreationPayload.TYPE, SatelliteCreationPayload.STREAM_CODEC); + + PayloadTypeRegistry.playC2S().register(AirlockSetProximityPayload.TYPE, AirlockSetProximityPayload.STREAM_CODEC); } } diff --git a/src/main/java/dev/galacticraft/mod/network/GCServerPacketReceivers.java b/src/main/java/dev/galacticraft/mod/network/GCServerPacketReceivers.java index 620be5b40d..5b8a2c6800 100644 --- a/src/main/java/dev/galacticraft/mod/network/GCServerPacketReceivers.java +++ b/src/main/java/dev/galacticraft/mod/network/GCServerPacketReceivers.java @@ -41,6 +41,7 @@ public static void register() { registerPacket(OpenRocketPayload.TYPE); registerPacket(PlanetTeleportPayload.TYPE); registerPacket(SatelliteCreationPayload.TYPE); + registerPacket(AirlockSetProximityPayload.TYPE); } public static

void registerPacket(CustomPacketPayload.Type

type) { diff --git a/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java b/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java new file mode 100644 index 0000000000..2270c684ec --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java @@ -0,0 +1,41 @@ +package dev.galacticraft.mod.network.c2s; + +import dev.galacticraft.impl.network.c2s.C2SPayload; +import dev.galacticraft.mod.Constant; +import dev.galacticraft.mod.content.block.entity.AirlockControllerBlockEntity; +import dev.galacticraft.mod.screen.AirlockControllerMenu; +import io.netty.buffer.ByteBuf; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +public record AirlockSetProximityPayload(byte proximity) implements C2SPayload { + public static final StreamCodec STREAM_CODEC = + ByteBufCodecs.BYTE.map(AirlockSetProximityPayload::new, AirlockSetProximityPayload::proximity); + public static final ResourceLocation ID = Constant.id("airlock_set_proximity"); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ID); + + @Override + public void handle(ServerPlayNetworking.@NotNull Context context) { + if (context.player().containerMenu instanceof AirlockControllerMenu menu) { + AirlockControllerBlockEntity be = menu.be; + if (be != null && be.getLevel() != null && be.getLevel().isLoaded(be.getBlockPos())) { + if (!(be instanceof dev.galacticraft.machinelib.api.block.entity.MachineBlockEntity mb) + || mb.getSecurity().hasAccess(context.player())) { + // clamp 0..5 on the server just in case + byte clamped = (byte) Math.max(0, Math.min(5, this.proximity)); + be.setProximityOpen(clamped); + be.setChanged(); + } + } + } + } + + @Override + public @NotNull Type type() { + return TYPE; + } +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java b/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java index 1c516da824..650313ad06 100644 --- a/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java +++ b/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java @@ -22,23 +22,34 @@ package dev.galacticraft.mod.screen; +import dev.galacticraft.machinelib.api.menu.MachineMenu; +import dev.galacticraft.machinelib.api.menu.MenuData; +import dev.galacticraft.mod.content.block.entity.AirlockControllerBlockEntity; +import net.minecraft.core.BlockPos; 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 org.jetbrains.annotations.NotNull; -public class AirlockControllerMenu extends AbstractContainerMenu { - public AirlockControllerMenu(int syncId, Inventory inventory) { - super(GCMenuTypes.AIRLOCK_CONTROLLER_MENU, syncId); +public class AirlockControllerMenu extends MachineMenu { + public byte proximityOpen; // 0..5 + public boolean enabled; + + public AirlockControllerMenu(int syncId, Player player, AirlockControllerBlockEntity be) { + super(GCMenuTypes.AIRLOCK_CONTROLLER_MENU, syncId, player, be); + this.proximityOpen = be.getProximityOpen(); + this.enabled = be.active; } - @Override - public ItemStack quickMoveStack(Player player, int i) { - return ItemStack.EMPTY; + public AirlockControllerMenu(int syncId, Inventory inv, BlockPos pos) { + super(GCMenuTypes.AIRLOCK_CONTROLLER_MENU, syncId, inv, pos, 8, 84); } @Override - public boolean stillValid(Player player) { - return true; + public void registerData(@NotNull MenuData data) { + super.registerData(data); + data.registerByte(this.be::getProximityOpen, b -> this.proximityOpen = (byte) b); + data.registerBoolean(() -> this.be.active, b -> this.enabled = b); } -} +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/screen/GCMenuTypes.java b/src/main/java/dev/galacticraft/mod/screen/GCMenuTypes.java index b8e1365a8c..fda2958efd 100644 --- a/src/main/java/dev/galacticraft/mod/screen/GCMenuTypes.java +++ b/src/main/java/dev/galacticraft/mod/screen/GCMenuTypes.java @@ -68,7 +68,7 @@ public class GCMenuTypes { public static final MenuType PLAYER_INV_GC = new MenuType<>(GCPlayerInventoryMenu::new, FeatureFlags.VANILLA_SET); public static final MenuType PET_INV_GC = new ExtendedScreenHandlerType<>(GCPetInventoryMenu::new, ByteBufCodecs.INT); - public static final MenuType AIRLOCK_CONTROLLER_MENU = new MenuType<>(AirlockControllerMenu::new, FeatureFlags.VANILLA_SET); + public static final MenuType AIRLOCK_CONTROLLER_MENU = SynchronizedMenuType.create(AirlockControllerMenu::new); public static final MenuType ROCKET_WORKBENCH = new ExtendedScreenHandlerType<>(RocketWorkbenchMenu::new, RocketWorkbenchMenu.OpeningData.CODEC); public static final MenuType ROCKET = new ExtendedScreenHandlerType<>(RocketMenu::new, ByteBufCodecs.INT); public static final MenuType PARACHEST = new ExtendedScreenHandlerType<>(ParachestMenu::new, ParachestMenu.OpeningData.STREAM_CODEC); diff --git a/src/main/java/dev/galacticraft/mod/tag/GCBlockTags.java b/src/main/java/dev/galacticraft/mod/tag/GCBlockTags.java index 2f394a9dc6..54b8fa99d0 100644 --- a/src/main/java/dev/galacticraft/mod/tag/GCBlockTags.java +++ b/src/main/java/dev/galacticraft/mod/tag/GCBlockTags.java @@ -103,6 +103,8 @@ public class GCBlockTags { public static final TagKey GLASS_FLUID_PIPES = galacticraftTag("glass_fluid_pipes"); public static final TagKey STAINED_GLASS_FLUID_PIPES = galacticraftTag("glass_fluid_pipes/stained"); + public static final TagKey AIRLOCK_BLOCKS = galacticraftTag("airlock_blocks"); + public static final TagKey SLABS = galacticraftTag("slabs"); public static final TagKey STAIRS = galacticraftTag("stairs"); public static final TagKey WALLS = galacticraftTag("walls"); diff --git a/src/main/java/dev/galacticraft/mod/util/Translations.java b/src/main/java/dev/galacticraft/mod/util/Translations.java index afe552ce2f..c10cbffb38 100644 --- a/src/main/java/dev/galacticraft/mod/util/Translations.java +++ b/src/main/java/dev/galacticraft/mod/util/Translations.java @@ -384,7 +384,9 @@ interface Ui { String UPGRADE = "ui.galacticraft.upgrade"; String COLOR = "ui.galacticraft.color"; - String AIRLOCK_REDSTONE_SIGNAL = "ui.galacticraft.airlock.redstone_signal"; + String AIRLOCK_ENABLED = "ui.galacticraft.airlock.enabled"; + String AIRLOCK_DISABLED = "ui.galacticraft.airlock.disabled"; + String AIRLOCK_OPEN_WHEN_NEAR = "ui.galacticraft.airlock.open_when_near"; String AIRLOCK_OWNER = "ui.galacticraft.airlock.owner"; String ALPHA_WARNING_1 = "ui.galacticraft.alpha_warning.content1"; String ALPHA_WARNING_2 = "ui.galacticraft.alpha_warning.content2"; diff --git a/src/main/resources/assets/galacticraft/blockstates/air_lock_seal.json b/src/main/resources/assets/galacticraft/blockstates/air_lock_seal.json index 9b6690ba3c..a5bb35c9b4 100644 --- a/src/main/resources/assets/galacticraft/blockstates/air_lock_seal.json +++ b/src/main/resources/assets/galacticraft/blockstates/air_lock_seal.json @@ -1,10 +1,10 @@ { "variants": { + "facing=east": { "model": "galacticraft:block/air_lock_seal", "uvlock": true }, + "facing=west": { "model": "galacticraft:block/air_lock_seal", "y": 180, "uvlock": true }, "facing=north": { "model": "galacticraft:block/air_lock_seal", "y": 90, "uvlock": true }, - "facing=east": { "model": "galacticraft:block/air_lock_seal", "uvlock": true }, - "facing=south": { "model": "galacticraft:block/air_lock_seal", "y": 90, "uvlock": true }, - "facing=west": { "model": "galacticraft:block/air_lock_seal", "uvlock": true }, - "facing=up": { "model": "galacticraft:block/air_lock_seal", "x": 90, "uvlock": true }, - "facing=down": { "model": "galacticraft:block/air_lock_seal", "x": 90, "uvlock": true } + "facing=south": { "model": "galacticraft:block/air_lock_seal", "y": 270, "uvlock": true }, + "facing=up": { "model": "galacticraft:block/air_lock_seal_ud", "uvlock": true }, + "facing=down": { "model": "galacticraft:block/air_lock_seal_ud", "x": 180, "uvlock": true } } } \ No newline at end of file diff --git a/src/main/resources/assets/galacticraft/models/block/air_lock_seal.json b/src/main/resources/assets/galacticraft/models/block/air_lock_seal.json index ba01fd7aef..82e74333db 100644 --- a/src/main/resources/assets/galacticraft/models/block/air_lock_seal.json +++ b/src/main/resources/assets/galacticraft/models/block/air_lock_seal.json @@ -8,15 +8,15 @@ }, "elements": [ { - "from": [ 4, 0, 0 ], - "to": [ 12, 16, 16 ], + "from": [4, 0, 0], + "to": [12, 16, 16], "faces": { - "down": {"texture": "#top"}, - "up": {"texture": "#bottom"}, + "down": {"texture": "#top"}, + "up": {"texture": "#bottom"}, "north": {"texture": "#side"}, - "east": {"texture": "#side"}, + "east": {"texture": "#side"}, "south": {"texture": "#side"}, - "west": {"texture": "#side"} + "west": {"texture": "#side"} } } ] diff --git a/src/main/resources/assets/galacticraft/models/block/air_lock_seal_ud.json b/src/main/resources/assets/galacticraft/models/block/air_lock_seal_ud.json new file mode 100644 index 0000000000..8f926472bc --- /dev/null +++ b/src/main/resources/assets/galacticraft/models/block/air_lock_seal_ud.json @@ -0,0 +1,23 @@ +{ + "parent": "block/block", + "textures": { + "particle": "galacticraft:block/tin_decoration", + "top": "galacticraft:block/tin_decoration", + "side": "galacticraft:block/tin_decoration", + "bottom": "galacticraft:block/tin_decoration" + }, + "elements": [ + { + "from": [0, 4, 0], + "to": [16, 12, 16], + "faces": { + "down": {"texture": "#top"}, + "up": {"texture": "#bottom"}, + "north": {"texture": "#side"}, + "east": {"texture": "#side"}, + "south": {"texture": "#side"}, + "west": {"texture": "#side"} + } + } + ] +} \ No newline at end of file From 3b56cb3f569eea49751ef1d46bb907ed4e36680e Mon Sep 17 00:00:00 2001 From: Roelymole Date: Tue, 12 Aug 2025 14:43:23 +0100 Subject: [PATCH 02/11] fix: update model provider so that the faces in the side configuration tab are present --- .../blockstates/air_lock_controller.json | 2 +- .../generated/assets/galacticraft/lang/en_us.json | 1 - .../models/block/air_lock_controller.json | 7 ------- .../models/item/air_lock_controller.json | 3 --- .../models/machine/air_lock_controller.json | 12 ++++++++++++ .../galacticraft/mod/data/model/GCModelProvider.java | 11 +++++++++-- 6 files changed, 22 insertions(+), 14 deletions(-) delete mode 100644 src/main/generated/assets/galacticraft/models/block/air_lock_controller.json delete mode 100644 src/main/generated/assets/galacticraft/models/item/air_lock_controller.json create mode 100644 src/main/generated/assets/galacticraft/models/machine/air_lock_controller.json diff --git a/src/main/generated/assets/galacticraft/blockstates/air_lock_controller.json b/src/main/generated/assets/galacticraft/blockstates/air_lock_controller.json index 9a99056722..55e65f7420 100644 --- a/src/main/generated/assets/galacticraft/blockstates/air_lock_controller.json +++ b/src/main/generated/assets/galacticraft/blockstates/air_lock_controller.json @@ -1,7 +1,7 @@ { "variants": { "": { - "model": "galacticraft:block/air_lock_controller" + "model": "galacticraft:machine/air_lock_controller" } } } \ No newline at end of file diff --git a/src/main/generated/assets/galacticraft/lang/en_us.json b/src/main/generated/assets/galacticraft/lang/en_us.json index 0cc0ebc5f8..4b7d12d742 100644 --- a/src/main/generated/assets/galacticraft/lang/en_us.json +++ b/src/main/generated/assets/galacticraft/lang/en_us.json @@ -882,7 +882,6 @@ "tooltip.galacticraft.standard_wrench": "Most Galacticraft machines can be rotated by right-clicking with the Standard Wrench.", "tooltip.galacticraft.time_until_cool": "Time Until Cool: %s", "tooltip.galacticraft.waila_oxygen_tank": "O₂ Tank %s", - "ui.galacticraft.airlock.disable_with_redstone": "Disables from redstone.", "ui.galacticraft.airlock.disabled": "Airlock Disabled.", "ui.galacticraft.airlock.enabled": "Airlock Enabled.", "ui.galacticraft.airlock.open_when_near": "Airlock opens near player.", diff --git a/src/main/generated/assets/galacticraft/models/block/air_lock_controller.json b/src/main/generated/assets/galacticraft/models/block/air_lock_controller.json deleted file mode 100644 index 76729ec505..0000000000 --- a/src/main/generated/assets/galacticraft/models/block/air_lock_controller.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "parent": "minecraft:block/cube_column", - "textures": { - "end": "galacticraft:block/air_lock_frame", - "side": "galacticraft:block/air_lock_controller" - } -} \ No newline at end of file diff --git a/src/main/generated/assets/galacticraft/models/item/air_lock_controller.json b/src/main/generated/assets/galacticraft/models/item/air_lock_controller.json deleted file mode 100644 index b16277e11d..0000000000 --- a/src/main/generated/assets/galacticraft/models/item/air_lock_controller.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "galacticraft:block/air_lock_controller" -} \ No newline at end of file diff --git a/src/main/generated/assets/galacticraft/models/machine/air_lock_controller.json b/src/main/generated/assets/galacticraft/models/machine/air_lock_controller.json new file mode 100644 index 0000000000..65c5a5247d --- /dev/null +++ b/src/main/generated/assets/galacticraft/models/machine/air_lock_controller.json @@ -0,0 +1,12 @@ +{ + "data": { + "back": "galacticraft:block/air_lock_controller", + "bottom": "galacticraft:block/air_lock_frame", + "front": "galacticraft:block/air_lock_controller", + "left": "galacticraft:block/air_lock_controller", + "particle": "galacticraft:block/air_lock_controller", + "right": "galacticraft:block/air_lock_controller", + "top": "galacticraft:block/air_lock_frame" + }, + "machinelib:type": "machine" +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java b/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java index 43e764c1d9..414bfe05af 100644 --- a/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java @@ -472,8 +472,15 @@ private void createVaporSpout(BlockModelGenerators generator) { private void createAirLockController(BlockModelGenerators generator) { var block = GCBlocks.AIR_LOCK_CONTROLLER; - var textureMapping = TextureMapping.column(TextureMapping.getBlockTexture(block), TextureMapping.getBlockTexture(GCBlocks.AIR_LOCK_FRAME)); - generator.createTrivialBlock(block, textureMapping, ModelTemplates.CUBE_COLUMN); + ResourceLocation controller = TextureMapping.getBlockTexture(block); + ResourceLocation frame = TextureMapping.getBlockTexture(GCBlocks.AIR_LOCK_FRAME); + MachineModelGenerator.createTrivialMachine(generator, block, TextureProvider.builder(Constant.MOD_ID) + .sides(controller) + .top(frame) + .bottom(frame) + .particle(controller) + .build() + ); } private void createRocketWorkbench(BlockModelGenerators generator) { From 706ab49074b19fa3e094b539e06e3b8a33d52836 Mon Sep 17 00:00:00 2001 From: maxryan008 Date: Tue, 12 Aug 2025 23:52:49 +1000 Subject: [PATCH 03/11] Better screen implementation for airlock --- .../assets/galacticraft/lang/en_us.json | 8 +- .../java/dev/galacticraft/mod/Constant.java | 2 +- .../ingame/AirlockControllerScreen.java | 97 +++++++++++++++--- .../entity/AirlockControllerBlockEntity.java | 21 +++- .../mod/data/GCTranslationProvider.java | 7 +- .../mod/screen/AirlockControllerMenu.java | 6 +- .../galacticraft/mod/util/Translations.java | 1 + .../textures/gui/air_lock_controller.png | Bin 3527 -> 1012 bytes 8 files changed, 113 insertions(+), 29 deletions(-) diff --git a/src/main/generated/assets/galacticraft/lang/en_us.json b/src/main/generated/assets/galacticraft/lang/en_us.json index 0cc0ebc5f8..25928651e5 100644 --- a/src/main/generated/assets/galacticraft/lang/en_us.json +++ b/src/main/generated/assets/galacticraft/lang/en_us.json @@ -882,10 +882,10 @@ "tooltip.galacticraft.standard_wrench": "Most Galacticraft machines can be rotated by right-clicking with the Standard Wrench.", "tooltip.galacticraft.time_until_cool": "Time Until Cool: %s", "tooltip.galacticraft.waila_oxygen_tank": "O₂ Tank %s", - "ui.galacticraft.airlock.disable_with_redstone": "Disables from redstone.", - "ui.galacticraft.airlock.disabled": "Airlock Disabled.", - "ui.galacticraft.airlock.enabled": "Airlock Enabled.", - "ui.galacticraft.airlock.open_when_near": "Airlock opens near player.", + "ui.galacticraft.airlock.default_name": "Airlock Controller", + "ui.galacticraft.airlock.disabled": "Airlock Disabled", + "ui.galacticraft.airlock.enabled": "Airlock Enabled", + "ui.galacticraft.airlock.open_when_near": "Airlock opens near player", "ui.galacticraft.airlock.owner": "%s's Airlock Controller", "ui.galacticraft.alpha_warning.content1": "Galacticraft is currently in ALPHA.", "ui.galacticraft.alpha_warning.content2": "Please report all issues you find.", diff --git a/src/main/java/dev/galacticraft/mod/Constant.java b/src/main/java/dev/galacticraft/mod/Constant.java index d5b793b54b..3de1110d00 100644 --- a/src/main/java/dev/galacticraft/mod/Constant.java +++ b/src/main/java/dev/galacticraft/mod/Constant.java @@ -594,7 +594,7 @@ interface ScreenTexture { ResourceLocation ROCKET_WORKBENCH_SCREEN = id("textures/gui/rocket_workbench.png"); ResourceLocation ROCKET_SELECTION = id("textures/gui/rocket_part_selection.png"); - ResourceLocation AIRLOCK_CONTROLLER_SCREEN = id("textures/gui/airlock_controller_screen.png"); + ResourceLocation AIRLOCK_CONTROLLER_SCREEN = id("textures/gui/air_lock_controller.png"); ResourceLocation PLAYER_INVENTORY_SCREEN = id("textures/gui/player_inventory_screen.png"); ResourceLocation PET_INVENTORY_SCREEN = id("textures/gui/pet_inventory_screen.png"); diff --git a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java index 760af3d85c..d6214ee426 100644 --- a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java +++ b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java @@ -31,34 +31,83 @@ import dev.galacticraft.mod.util.Translations; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.EditBox; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; public class AirlockControllerScreen extends MachineScreen { + private final EditBox textField; + public AirlockControllerScreen(AirlockControllerMenu menu, Inventory inv, Component title) { super(menu, title, Constant.ScreenTexture.AIRLOCK_CONTROLLER_SCREEN); + + this.textField = new EditBox( + Minecraft.getInstance().font, + this.leftPos + 132, this.topPos + 65, + 26, 20, + Component.literal("") + ); + this.textField.setValue(String.valueOf(this.menu.proximityOpen)); + + this.textField.setFilter(s -> { + if (s.isEmpty()) return true; + try { + int v = Integer.parseInt(s); + return v >= 0 && v <= 5; + } catch (NumberFormatException ignore) { + return false; + } + }); + + this.textField.setResponder(s -> { + if (s.isEmpty()) return; + byte value; + try { + value = Byte.parseByte(s); + } catch (NumberFormatException ignore) { + return; + } + if (value < 0) value = 0; + if (value > 5) value = 5; + + String clamped = String.valueOf(value); + if (!s.equals(clamped)) { + this.textField.setValue(clamped); + return; + } + + if (value != this.menu.proximityOpen) { + this.menu.proximityOpen = value; + ClientPlayNetworking.send(new AirlockSetProximityPayload(value)); + } + }); } @Override protected void init() { super.init(); - this.titleLabelX += 20; - // If MachineLib has its own redstone mode button bar, add it here if needed. + this.imageHeight = 171; + this.titleLabelX = 4; + + this.textField.setX(this.leftPos + 132); + this.textField.setY(this.topPos + 65); + this.addRenderableWidget(this.textField); } @Override protected void renderMachineBackground(GuiGraphics g, int mouseX, int mouseY, float delta) { - // Status boolean enabled = this.menu.enabled; - var label = enabled ? Component.translatable(Translations.Ui.AIRLOCK_ENABLED) + Component label = enabled + ? Component.translatable(Translations.Ui.AIRLOCK_ENABLED) : Component.translatable(Translations.Ui.AIRLOCK_DISABLED); int color = enabled ? ChatFormatting.GREEN.getColor() : ChatFormatting.RED.getColor(); - g.drawString(this.font, label, this.leftPos + 60, this.topPos + 18, color, false); + g.drawCenteredString(this.font, label.getString(), this.leftPos + 90, this.topPos + 22, color); + + int upX = this.leftPos + 158, upY = this.topPos + 65; + int downX = this.leftPos + 158, downY = this.topPos + 75; - // Proximity (up/down) - int upX = this.leftPos + 158, upY = this.topPos + 59; - int downX = this.leftPos + 158, downY = this.topPos + 69; boolean hoverUp = DrawableUtil.isWithin(mouseX, mouseY, upX, upY, Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT); boolean hoverDown = DrawableUtil.isWithin(mouseX, mouseY, downX, downY, Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT); @@ -72,31 +121,45 @@ protected void renderMachineBackground(GuiGraphics g, int mouseX, int mouseY, fl hoverDown ? Constant.TextureCoordinate.ARROW_DOWN_HOVER_Y : Constant.TextureCoordinate.ARROW_DOWN_Y, Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT); - g.drawString(this.font, - Component.translatable(Translations.Ui.AIRLOCK_OPEN_WHEN_NEAR, this.menu.proximityOpen), - this.leftPos + 60, this.topPos + 64, ChatFormatting.DARK_GRAY.getColor(), false); + g.drawString(this.font, Component.literal("Airlock Proximity:"), this.leftPos + 46, this.topPos + 71, ChatFormatting.DARK_GRAY.getColor(), false); + } + + @Override + protected void renderForeground(GuiGraphics graphics, int mouseX, int mouseY, float delta) { + super.renderForeground(graphics, mouseX, mouseY, delta); + + this.textField.setX(this.leftPos + 132); + this.textField.setY(this.topPos + 65); + + String shouldBe = String.valueOf(this.menu.proximityOpen); + if (!this.textField.getValue().equals(shouldBe) && !this.textField.isFocused()) { + this.textField.setValue(shouldBe); + } } + @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { if (button == 0) { - // Up - int upX = this.leftPos + 158, upY = this.topPos + 59; + int upX = this.leftPos + 158, upY = this.topPos + 65; + int downX = this.leftPos + 158, downY = this.topPos + 75; + if (DrawableUtil.isWithin(mouseX, mouseY, upX, upY, Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT)) { if (this.menu.proximityOpen < 5) { - byte next = (byte)(this.menu.proximityOpen + 1); + byte next = (byte) (this.menu.proximityOpen + 1); this.menu.proximityOpen = next; + this.textField.setValue(String.valueOf(next)); ClientPlayNetworking.send(new AirlockSetProximityPayload(next)); this.playButtonSound(); return true; } } - // Down - int downX = this.leftPos + 158, downY = this.topPos + 69; + if (DrawableUtil.isWithin(mouseX, mouseY, downX, downY, Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT)) { if (this.menu.proximityOpen > 0) { - byte next = (byte)(this.menu.proximityOpen - 1); + byte next = (byte) (this.menu.proximityOpen - 1); this.menu.proximityOpen = next; + this.textField.setValue(String.valueOf(next)); ClientPlayNetworking.send(new AirlockSetProximityPayload(next)); this.playButtonSound(); return true; diff --git a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java index 5afe8492b2..99091dbea4 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java +++ b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java @@ -22,6 +22,7 @@ package dev.galacticraft.mod.content.block.entity; +import com.mojang.authlib.GameProfile; import dev.galacticraft.machinelib.api.block.entity.MachineBlockEntity; import dev.galacticraft.machinelib.api.machine.MachineStatus; import dev.galacticraft.machinelib.api.machine.configuration.RedstoneMode; @@ -36,6 +37,7 @@ import dev.galacticraft.mod.machine.GCMachineStatuses; import dev.galacticraft.mod.screen.AirlockControllerMenu; import dev.galacticraft.mod.util.Translations; +import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; @@ -51,6 +53,7 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.SkullBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -58,6 +61,8 @@ import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.UUID; import static dev.galacticraft.mod.content.block.special.AirlockSealBlock.FACING; @@ -278,7 +283,21 @@ public void unseal(AirlockFrameScanner.Result f) { @Override public @NotNull Component getDisplayName() { - return Component.translatable(Translations.Ui.AIRLOCK_OWNER, ""); // fill in owner if you track it + UUID owner = this.getSecurity().getOwner(); + String displayName; + + if (owner == null) { + return Component.translatable(Translations.Ui.AIRLOCK_DEFAULT_NAME); + } else { + Optional profile = SkullBlockEntity.fetchGameProfile(owner).getNow(null); + if (profile != null && profile.isPresent()) { + displayName = profile.get().getName(); + } else { + return Component.translatable(Translations.Ui.AIRLOCK_DEFAULT_NAME); + } + } + + return Component.translatable(Translations.Ui.AIRLOCK_OWNER, displayName); } @Override diff --git a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java index 1d389ffbf8..8319522cc1 100644 --- a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java @@ -1126,9 +1126,10 @@ protected void generateUiTranslations() { this.add(Ui.UPGRADE, "Upgrade"); this.add(Ui.COLOR, "Color"); - this.add(Ui.AIRLOCK_ENABLED, "Airlock Enabled."); - this.add(Ui.AIRLOCK_DISABLED, "Airlock Disabled."); - this.add(Ui.AIRLOCK_OPEN_WHEN_NEAR, "Airlock opens near player."); + this.add(Ui.AIRLOCK_DEFAULT_NAME, "Airlock Controller"); + this.add(Ui.AIRLOCK_ENABLED, "Airlock Enabled"); + this.add(Ui.AIRLOCK_DISABLED, "Airlock Disabled"); + this.add(Ui.AIRLOCK_OPEN_WHEN_NEAR, "Airlock opens near player"); this.add(Ui.AIRLOCK_OWNER, "%s's Airlock Controller"); this.add(Ui.ALPHA_WARNING_1, "Galacticraft is currently in ALPHA."); this.add(Ui.ALPHA_WARNING_2, "Please report all issues you find."); diff --git a/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java b/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java index 650313ad06..a79f0a6759 100644 --- a/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java +++ b/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java @@ -33,8 +33,8 @@ import org.jetbrains.annotations.NotNull; public class AirlockControllerMenu extends MachineMenu { - public byte proximityOpen; // 0..5 - public boolean enabled; + public byte proximityOpen = 0; // 0..5 + public boolean enabled = false; public AirlockControllerMenu(int syncId, Player player, AirlockControllerBlockEntity be) { super(GCMenuTypes.AIRLOCK_CONTROLLER_MENU, syncId, player, be); @@ -43,7 +43,7 @@ public AirlockControllerMenu(int syncId, Player player, AirlockControllerBlockEn } public AirlockControllerMenu(int syncId, Inventory inv, BlockPos pos) { - super(GCMenuTypes.AIRLOCK_CONTROLLER_MENU, syncId, inv, pos, 8, 84); + super(GCMenuTypes.AIRLOCK_CONTROLLER_MENU, syncId, inv, pos, 8, 89); } @Override diff --git a/src/main/java/dev/galacticraft/mod/util/Translations.java b/src/main/java/dev/galacticraft/mod/util/Translations.java index c10cbffb38..e153a581a0 100644 --- a/src/main/java/dev/galacticraft/mod/util/Translations.java +++ b/src/main/java/dev/galacticraft/mod/util/Translations.java @@ -384,6 +384,7 @@ interface Ui { String UPGRADE = "ui.galacticraft.upgrade"; String COLOR = "ui.galacticraft.color"; + String AIRLOCK_DEFAULT_NAME = "ui.galacticraft.airlock.default_name"; String AIRLOCK_ENABLED = "ui.galacticraft.airlock.enabled"; String AIRLOCK_DISABLED = "ui.galacticraft.airlock.disabled"; String AIRLOCK_OPEN_WHEN_NEAR = "ui.galacticraft.airlock.open_when_near"; diff --git a/src/main/resources/assets/galacticraft/textures/gui/air_lock_controller.png b/src/main/resources/assets/galacticraft/textures/gui/air_lock_controller.png index cc430d638f5e8097b85bf0b76008e2f3b86dec7a..31078fda22a5ab8c220425c292c70e9257b4db6f 100644 GIT binary patch literal 1012 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G!U;i$lZxy-8q?;3=GWcJY5_^ zD(1Ysb1?UIgowjMf924dxhn*ji$vpA2(0AwPAwE;R+f}rtTIg^aOd+kOf3B^32`5a zKQ8-t*XDtp&-Q(d`;Y$Sw>-c3`TXAozLzDG3Pkx2A3i_VQm#L_B6)9DW(~)ImnFOI z-aVjy<84`TkKwo9ezKr6oAb#%A2UBT^*TITlX&iUufy)Uc|C^5>h{Y&zP~?l-w8K` z;xPN;ZVR6NU}H#RA{v=7&D{LUQs+2a`wfnibFZG?;MgK_pFzQgXmkc_K>O1e~4mu7CW@uV z0RZ1fkXC#M0I(=|>+8c!^z}vIf&O0JzMcSJOeo3@K_EVJCyaH4gqMC$rm=c7-|+^R zh{$w?x#sfSmZoKcsy2$1(Li7Z`Qpa#e#|&r!q%V>%!O!Rlwgv+#0x{n`x{O@sl#<1 z?CelhaQ>Uk2YAXx*O&tjkWs0Wlfz&NN_)>qj?*e6LDDTz8vq6*d>bPJnAsY(yEH^F zfro}a#21SN(zwup$l-RKGyxxpP>ea_SxJRGDe}R3E&wTL*5s3i@FZ$Tnx-kXjy=xh z&yn}ymGtVC)`JWuX}YGL*QUrfmFLd9)M`x}(5!5>o^ji{@T=8K%&0%HK9LoQFO-KY za5Ne|R?d=JR$rb*ut8}7E)!p#0J|h{HOQqT5sp>yAq4K8jF}9U)s5a{lxBg5j#CDl&dU9}50you!s!a4xreo2&G|jw^@qsh)>p}0(6Z9@>(2vHMF7m$y`NdIgbD3d~x4AnRSiy9zVk!m? zR>dwDb510Th%$JvIxa!jID!$*smzj$a>U86XPIu1b zKZ;J6?`1NJec8Z)qZjBqM?LR>{-R9)O((->JnN!RfaDw?5`SfJm;Cpknih*r+;=br z4B;<(%qxDbpZ=FoSz74BRGiV{G?ogGH>^EfdsRy1@rbCuB`+5}%!sed*Rr?@&c)io z&?0OTTWu(082mikQr*(M%6`&Fm9I8&%4qg^7ZI{fhvIGrJ3a8b)>f`m=3jo1SbvH4 z-1+j9#OYiU9DoYa~(LcK-3=a;1JvxdraO3gEDry^H^$k;=}NcGdp(KJ1}X;ODM zH<-j8O1zc2CRv&lkWR*sGmIswA+<>l3>5GG)tC{Q9Ga(L7-^azMoM=y9+wdFW+Z`0 zf+R(fWS-fS_?3+EcQiX}fzTtGBgjz-9qg^~ThmyDM!hn_GJQAWWxRy5mGy*4V2ekc zNBk=D(9aC0Rj7=D43Er<$?%u_FEg$FvI?5u^0xMlAt6W{q`xL^R#bX4s5q$jsrcs6 zAF&B8zb*t=Ezc({-XkW6{^ilT6utHYQvz2!jhFXTcW=CAK$O*}`$X<|K zkU4{cu+J;ORcY7;m!-0|BUV4GX0bRd%rsT0tCC6XhYW*EosCx2biGDp1#Y$USsen4 z((^XVd$CbfSm9U~X&B}ZU*=Z-)<)*0X3fqsMbnV7N}?R8YtP;WW@BMf>eKZD^CLFD z)uGCv+o9l#esJt%{j1Jhj$KAkw)(I>i@z+&VJpQ_&Y7ioRrs6D7D3LYx8xk}*4S1~ zJ!>s&%|X|ppX}2LH9d_R^vJJiOZ%%pctdf#bD49>d~lTAf>lXPk3|d- zxOe;B8|WQn-b)gEm-)Q-TX~=IjVDK@?4(>yDHgcPYs9y$vIRZ8< z$VLByF5gE{>?pMRDh1KtF z{UdfnM?p}u_CM{eQP7m$==tc56JZ?DvG>+EiT$|iIE12ej>u-mNp=^NQWkBHBAcXn z!=2$S%90vVhsNZe;O}V@X_V5-rEiJF%0mt74c{7$0zQ+Q$m2{aiNTQ1;upm6=}wl@ zRSi`ys_2)015X3lm|4M+D)dsIfhyn)3JM z&Sjq!FMCB?7Y^xbgw~A6cPGfKm<`Sj=bEF8amIy9?Ey7-r3|gi9~s{= zPcqphW3Dq>c$*Gj2VWGg4v{m$y1d_e&v?g?ev{%!<#&CjHteCp2TSUnx_mV%?R1Fo zF+>mC=rv3u+q=X*^GHfg{-;4JN=>Ol$4ChoG3rqlvDJ3h@~-{eh#kbP+FD9&ki*R$ zVF!`6Ze-l=N3rKQ1+Jm*f1W*#>k-z?};58;u%;49C&u-u5n!pqWJG@3r4 z?|xOARNHEOu=#DarZq<1jmJqrO{Pvd+U2b*{&xyVJ!>KI3#7;W`D7F7%zVUressx+ zt%EJcBxfxX)%c^t<@5>e*RW}oDAnKVDcg=cKYjl8t@ST&xgbJIct|LY7NMMw z@0YAxy~umfA%=Kd+FCT>JP8V3MU9}P&`;4Y^!egF?U|4FX1|`6kEbS>zbvlSRETKb zJGW%kn`eOx8XGM#)lm=9UOEo7FdI8zh4A?O!?4fhxQ7ot%AjCbqs*g_O0!Pd!2{pk z57i{s6n&Pj58k`A*LLRd%bJ2&(nsMYN!+%Ba&9KR=kZb^1AVbS>{M z2CS2d($~|+uWVeg4zb?qew`EMzjQjtxXuV;#|qnOSw&8go43a9m1mb%5*!J?MHqFu zbw>GP_Du(swlA_2rco)0G|*bf~a$Z=r)6lF4en-MB5MvmU*E zy1x6Xw{p^=qs0!GEKu&}eUv%$eZ8yg!c#l^bEP=NtuWo}K? z&(476o{Ao-!W>|19|Qnw{eJ?KCC4T}6?suGJCwD*7b?Ux&=Y{Wdi$Xi&0z@9zZEYj zLX$h7qSTsXPdgJY6BAj0iAv}Jeh~e?Cips)3H)zPwYY#Zi1uHdhMHdj(Ewm7I#9zq zf$m@3hnoKx=R!chI@Lb^AO62X0cw95BS1&3R91m1gP#A-uGDw@JEof@25ZLyA^?Df z+r&T*5$e1-splu42d3+7!JeGuC|om37Hs3*VdO4^DP>W2(MKojOmY zzA?f4)IM9N|E}@ovrSZtP6Ft1q$x=7+H2m$P@5SMOI@2s1u1Np;HhuMF$|N6EmP`3rGR!L?^ zFxTSu`sUB5o!uBfdm*;wchphosB?!hu;~kYnE_rkgYF<{iK7h9i_Ys_5x}6;{)-pJ b=MO~q%~j>4-x2!$e1-|k+@S8-?b!bS*tjPb From 8d3c59c6e335c3c93c3ed244d5ee76b7f14219e6 Mon Sep 17 00:00:00 2001 From: maxryan008 Date: Wed, 13 Aug 2025 00:17:05 +1000 Subject: [PATCH 04/11] Even better logic --- .../assets/galacticraft/lang/en_us.json | 1 + .../ingame/AirlockControllerScreen.java | 20 +- .../mod/content/AirlockState.java | 7 + .../entity/AirlockControllerBlockEntity.java | 282 +++++++++++------- .../mod/data/GCTranslationProvider.java | 1 + .../mod/machine/GCMachineStatuses.java | 4 + .../mod/screen/AirlockControllerMenu.java | 9 +- .../galacticraft/mod/util/Translations.java | 1 + 8 files changed, 209 insertions(+), 116 deletions(-) create mode 100644 src/main/java/dev/galacticraft/mod/content/AirlockState.java diff --git a/src/main/generated/assets/galacticraft/lang/en_us.json b/src/main/generated/assets/galacticraft/lang/en_us.json index 25928651e5..4754a04fd5 100644 --- a/src/main/generated/assets/galacticraft/lang/en_us.json +++ b/src/main/generated/assets/galacticraft/lang/en_us.json @@ -887,6 +887,7 @@ "ui.galacticraft.airlock.enabled": "Airlock Enabled", "ui.galacticraft.airlock.open_when_near": "Airlock opens near player", "ui.galacticraft.airlock.owner": "%s's Airlock Controller", + "ui.galacticraft.airlock.partial": "Airlock Partially Enabled", "ui.galacticraft.alpha_warning.content1": "Galacticraft is currently in ALPHA.", "ui.galacticraft.alpha_warning.content2": "Please report all issues you find.", "ui.galacticraft.alpha_warning.content3": "Press [ESC] or click to continue.", diff --git a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java index d6214ee426..bc22402b8d 100644 --- a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java +++ b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java @@ -22,6 +22,7 @@ package dev.galacticraft.mod.client.gui.screen.ingame; +import dev.galacticraft.mod.content.AirlockState; import dev.galacticraft.mod.network.c2s.AirlockSetProximityPayload; import dev.galacticraft.machinelib.client.api.screen.MachineScreen; import dev.galacticraft.mod.Constant; @@ -98,11 +99,20 @@ protected void init() { @Override protected void renderMachineBackground(GuiGraphics g, int mouseX, int mouseY, float delta) { - boolean enabled = this.menu.enabled; - Component label = enabled - ? Component.translatable(Translations.Ui.AIRLOCK_ENABLED) - : Component.translatable(Translations.Ui.AIRLOCK_DISABLED); - int color = enabled ? ChatFormatting.GREEN.getColor() : ChatFormatting.RED.getColor(); + AirlockState enabled = this.menu.state; + Component label; + int color; + if (enabled.equals(AirlockState.ALL)) { + label = Component.translatable(Translations.Ui.AIRLOCK_ENABLED); + color = ChatFormatting.GREEN.getColor(); + } else if (enabled.equals(AirlockState.PARTIAL)) { + label = Component.translatable(Translations.Ui.AIRLOCK_PARTIAL); + color = ChatFormatting.YELLOW.getColor(); + } else { + label = Component.translatable(Translations.Ui.AIRLOCK_DISABLED); + color = ChatFormatting.RED.getColor(); + } + g.drawCenteredString(this.font, label.getString(), this.leftPos + 90, this.topPos + 22, color); int upX = this.leftPos + 158, upY = this.topPos + 65; diff --git a/src/main/java/dev/galacticraft/mod/content/AirlockState.java b/src/main/java/dev/galacticraft/mod/content/AirlockState.java new file mode 100644 index 0000000000..88e05dfdcf --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/content/AirlockState.java @@ -0,0 +1,7 @@ +package dev.galacticraft.mod.content; + +public enum AirlockState { + NONE, // no frames sealed (all open) + PARTIAL, // at least one sealed, but not all + ALL; // all frames sealed +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java index 99091dbea4..415a7c0182 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java +++ b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java @@ -26,10 +26,12 @@ import dev.galacticraft.machinelib.api.block.entity.MachineBlockEntity; import dev.galacticraft.machinelib.api.machine.MachineStatus; import dev.galacticraft.machinelib.api.machine.configuration.RedstoneMode; +import dev.galacticraft.machinelib.api.menu.MachineMenu; import dev.galacticraft.machinelib.api.storage.MachineEnergyStorage; import dev.galacticraft.machinelib.api.storage.MachineItemStorage; import dev.galacticraft.machinelib.api.storage.StorageSpec; import dev.galacticraft.mod.Galacticraft; +import dev.galacticraft.mod.content.AirlockState; import dev.galacticraft.mod.content.GCBlockEntityTypes; import dev.galacticraft.mod.content.GCBlocks; import dev.galacticraft.mod.content.GCSounds; @@ -43,6 +45,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.players.GameProfileCache; import net.minecraft.sounds.SoundSource; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.MenuProvider; @@ -58,37 +61,39 @@ import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import static dev.galacticraft.mod.content.block.special.AirlockSealBlock.FACING; public class AirlockControllerBlockEntity extends MachineBlockEntity { - // Proximity setting (0..5). 0 = off. + // --- UI-config (persisted) --- private byte proximityOpen = 0; - // DUMMY storage so MachineLib doesn't crash on zero-slot specs private static final StorageSpec SPEC = StorageSpec.of( - MachineEnergyStorage.spec( - 0, 0 - ) + MachineEnergyStorage.spec(0, 0) ); - // runtime - public boolean active; - public boolean lastActive; - private List lastFrames = java.util.Collections.emptyList(); - public int ticks = 0; + // --- Runtime --- + private List lastFrames = Collections.emptyList(); + private Map lastFrameMap = Collections.emptyMap(); + private final Set sealedFrames = new HashSet<>(); + + private AirlockState state = AirlockState.NONE; + private int ticks = 0; public AirlockControllerBlockEntity(BlockPos pos, BlockState state) { super(GCBlockEntityTypes.AIRLOCK_CONTROLLER, pos, state, SPEC); } + // --- Persisted config --- + public byte getProximityOpen() { return this.proximityOpen; } - public void setProximityOpen(byte v) { this.proximityOpen = (byte)Math.max(0, Math.min(5, v)); setChanged(); } + public void setProximityOpen(byte v) { + this.proximityOpen = (byte) Math.max(0, Math.min(5, v)); + setChanged(); + } @Override protected void saveAdditional(CompoundTag tag, HolderLookup.Provider lookup) { @@ -102,70 +107,105 @@ public void loadAdditional(CompoundTag tag, HolderLookup.Provider lookup) { if (tag.contains("ProximityOpen")) this.proximityOpen = tag.getByte("ProximityOpen"); } - private void serverTick() { - if (this.level == null || this.level.isClientSide()) return; - this.ticks++; + // --- Core logic --- - if (this.ticks % 5 != 0) return; + private void serverTick() { + if (!(this.level instanceof ServerLevel server)) return; + if ((++this.ticks % 5) != 0) return; // tick every 5 - // Re-scan frames - List frames = AirlockFrameScanner.scanAll(this.level, this.worldPosition); + List frames = AirlockFrameScanner.scanAll(server, this.worldPosition); + Map frameMap = indexFrames(frames); boolean framesChanged = !sameFrames(frames, this.lastFrames); - // RedstoneMode: true = machine should be "active" given powered state - boolean powered = this.level.getBestNeighborSignal(this.worldPosition) > 0; + boolean powered = server.getBestNeighborSignal(this.worldPosition) > 0; RedstoneMode mode = this.getRedstoneMode(); boolean redstoneAllows = mode.isActive(powered); - // Proximity: open (disable) only for *authorized* nearby players - boolean near = false; - if (!frames.isEmpty() && this.proximityOpen > 0) { - double r = this.proximityOpen; - AABB big = null; - for (var f : frames) { - AABB expanded = getExpanded(f, r); - big = (big == null) ? expanded : big.minmax(expanded); - } - if (big != null) { - // ONLY count players who are allowed by MachineLib security - var players = this.level.getEntitiesOfClass(Player.class, big); - for (Player p : players) { - if (this.getSecurity().hasAccess(p)) { - near = true; - break; + Set nextSealed = new HashSet<>(); + if (redstoneAllows && !frames.isEmpty()) { + final double r = this.proximityOpen; + for (AirlockFrameScanner.Result f : frames) { + boolean anyAuthorizedNear = false; + if (r > 0) { + AABB expanded = expandedInterior(f, r); + for (Player p : server.getEntitiesOfClass(Player.class, expanded)) { + if (this.getSecurity().hasAccess(p)) { + anyAuthorizedNear = true; + break; + } } } + if (!anyAuthorizedNear) { + nextSealed.add(frameId(f)); + } } } - boolean newActive = redstoneAllows && !near; - boolean activeChanged = (newActive != this.active); + boolean anyChange = false; - if (activeChanged || framesChanged) { - // Unseal old frames - for (var f : this.lastFrames) unseal(f); + for (long id : new HashSet<>(this.sealedFrames)) { + if (!nextSealed.contains(id)) { + AirlockFrameScanner.Result f = this.lastFrameMap.getOrDefault(id, frameMap.get(id)); + if (f != null) { + unseal(f); + this.sealedFrames.remove(id); + anyChange = true; + } + } + } - // Seal new if active - if (newActive) { - for (var f : frames) seal(f); + for (long id : nextSealed) { + if (!this.sealedFrames.contains(id)) { + AirlockFrameScanner.Result f = frameMap.get(id); + if (f != null) { + seal(f); + this.sealedFrames.add(id); + anyChange = true; + } } + } + + AirlockState newState; + if (frames.isEmpty() || this.sealedFrames.isEmpty()) newState = AirlockState.NONE; + else if (this.sealedFrames.size() == frames.size()) newState = AirlockState.ALL; + else newState = AirlockState.PARTIAL; - this.active = newActive; + boolean stateChanged = (newState != this.state); + this.state = newState; + + if (anyChange || framesChanged || stateChanged) { this.lastFrames = frames; + this.lastFrameMap = frameMap; - BlockState s = this.level.getBlockState(this.worldPosition); - this.level.sendBlockUpdated(this.worldPosition, s, s, Block.UPDATE_CLIENTS); + BlockState s = server.getBlockState(this.worldPosition); + server.sendBlockUpdated(this.worldPosition, s, s, Block.UPDATE_CLIENTS); + setChanged(); } } - private static @NotNull AABB getExpanded(AirlockFrameScanner.Result f, double r) { + // --- Helpers --- + + private static Map indexFrames(List list) { + Map out = new HashMap<>(list.size()); + for (AirlockFrameScanner.Result r : list) out.put(frameId(r), r); + return out; + } + + private static long frameId(AirlockFrameScanner.Result f) { + int h = 1; + h = 31 * h + f.plane.ordinal(); + h = 31 * h + f.minX; h = 31 * h + f.minY; h = 31 * h + f.minZ; + h = 31 * h + f.maxX; h = 31 * h + f.maxY; h = 31 * h + f.maxZ; + return (h & 0xffffffffL); + } + + private static AABB expandedInterior(AirlockFrameScanner.Result f, double r) { AABB interior = switch (f.plane) { case XY -> new AABB(f.minX + 1, f.minY + 1, f.minZ, f.maxX, f.maxY, f.maxZ); case XZ -> new AABB(f.minX + 1, f.minY, f.minZ + 1, f.maxX, f.maxY, f.maxZ); case YZ -> new AABB(f.minX, f.minY + 1, f.minZ + 1, f.maxX, f.maxY, f.maxZ); }; - interior.inflate(0.0001); // avoid zero-thickness issues on the plane - return interior.inflate(r); + return interior.inflate(Math.max(r, 0) + 1.0e-4); } private static boolean sameFrames(List a, List b) { @@ -181,152 +221,180 @@ private static boolean sameFrames(List a, List { - int z = f.minZ; + final int z = f.minZ; for (int x = f.minX + 1; x <= f.maxX - 1; x++) for (int y = f.minY + 1; y <= f.maxY - 1; y++) - if (this.level.getBlockState(new BlockPos(x, y, z)).isAir()) anyAir = true; + if (server.getBlockState(new BlockPos(x, y, z)).isAir()) anyAir = true; } case XZ -> { - int y = f.minY; + final int y = f.minY; for (int x = f.minX + 1; x <= f.maxX - 1; x++) for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) - if (this.level.getBlockState(new BlockPos(x, y, z)).isAir()) anyAir = true; + if (server.getBlockState(new BlockPos(x, y, z)).isAir()) anyAir = true; } case YZ -> { - int x = f.minX; + final int x = f.minX; for (int y = f.minY + 1; y <= f.maxY - 1; y++) for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) - if (this.level.getBlockState(new BlockPos(x, y, z)).isAir()) anyAir = true; + if (server.getBlockState(new BlockPos(x, y, z)).isAir()) anyAir = true; } } if (anyAir) { - BlockPos center = new BlockPos((f.minX + f.maxX)/2, (f.minY + f.maxY)/2, (f.minZ + f.maxZ)/2); - this.level.playSound(null, center, GCSounds.PLAYER_CLOSEAIRLOCK, SoundSource.BLOCKS, 1.0F, 1.0F); + BlockPos center = new BlockPos((f.minX + f.maxX) / 2, (f.minY + f.maxY) / 2, (f.minZ + f.maxZ) / 2); + server.playSound(null, center, GCSounds.PLAYER_CLOSEAIRLOCK, SoundSource.BLOCKS, 1.0F, 1.0F); } switch (f.plane) { case XY -> { - int z = f.minZ; + final int z = f.minZ; for (int x = f.minX + 1; x <= f.maxX - 1; x++) for (int y = f.minY + 1; y <= f.maxY - 1; y++) { BlockPos p = new BlockPos(x, y, z); - if (this.level.getBlockState(p).isAir()) - this.level.setBlock(p, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, f.sealFacing), Block.UPDATE_ALL); + if (server.getBlockState(p).isAir()) + server.setBlock(p, GCBlocks.AIR_LOCK_SEAL.defaultBlockState() + .setValue(dev.galacticraft.mod.content.block.special.AirlockSealBlock.FACING, f.sealFacing), + Block.UPDATE_ALL); } } case XZ -> { - int y = f.minY; + final int y = f.minY; for (int x = f.minX + 1; x <= f.maxX - 1; x++) for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) { BlockPos p = new BlockPos(x, y, z); - if (this.level.getBlockState(p).isAir()) - this.level.setBlock(p, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, f.sealFacing), Block.UPDATE_ALL); + if (server.getBlockState(p).isAir()) + server.setBlock(p, GCBlocks.AIR_LOCK_SEAL.defaultBlockState() + .setValue(dev.galacticraft.mod.content.block.special.AirlockSealBlock.FACING, f.sealFacing), + Block.UPDATE_ALL); } } case YZ -> { - int x = f.minX; + final int x = f.minX; for (int y = f.minY + 1; y <= f.maxY - 1; y++) for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) { BlockPos p = new BlockPos(x, y, z); - if (this.level.getBlockState(p).isAir()) - this.level.setBlock(p, GCBlocks.AIR_LOCK_SEAL.defaultBlockState().setValue(FACING, f.sealFacing), Block.UPDATE_ALL); + if (server.getBlockState(p).isAir()) + server.setBlock(p, GCBlocks.AIR_LOCK_SEAL.defaultBlockState() + .setValue(dev.galacticraft.mod.content.block.special.AirlockSealBlock.FACING, f.sealFacing), + Block.UPDATE_ALL); } } } } - public void unseal(AirlockFrameScanner.Result f) { + private void unseal(AirlockFrameScanner.Result f) { + if (!(this.level instanceof ServerLevel server)) return; + boolean hadSeal = false; switch (f.plane) { case XY -> { - int z = f.minZ; + final int z = f.minZ; for (int x = f.minX + 1; x <= f.maxX - 1; x++) for (int y = f.minY + 1; y <= f.maxY - 1; y++) { BlockPos p = new BlockPos(x, y, z); - if (this.level.getBlockState(p).is(GCBlocks.AIR_LOCK_SEAL)) { + if (server.getBlockState(p).is(GCBlocks.AIR_LOCK_SEAL)) { hadSeal = true; - this.level.setBlock(p, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); + server.setBlock(p, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); } } } case XZ -> { - int y = f.minY; + final int y = f.minY; for (int x = f.minX + 1; x <= f.maxX - 1; x++) for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) { BlockPos p = new BlockPos(x, y, z); - if (this.level.getBlockState(p).is(GCBlocks.AIR_LOCK_SEAL)) { + if (server.getBlockState(p).is(GCBlocks.AIR_LOCK_SEAL)) { hadSeal = true; - this.level.setBlock(p, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); + server.setBlock(p, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); } } } case YZ -> { - int x = f.minX; + final int x = f.minX; for (int y = f.minY + 1; y <= f.maxY - 1; y++) for (int z = f.minZ + 1; z <= f.maxZ - 1; z++) { BlockPos p = new BlockPos(x, y, z); - if (this.level.getBlockState(p).is(GCBlocks.AIR_LOCK_SEAL)) { + if (server.getBlockState(p).is(GCBlocks.AIR_LOCK_SEAL)) { hadSeal = true; - this.level.setBlock(p, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); + server.setBlock(p, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); } } } } if (hadSeal) { - BlockPos center = new BlockPos((f.minX + f.maxX)/2, (f.minY + f.maxY)/2, (f.minZ + f.maxZ)/2); - this.level.playSound(null, center, GCSounds.PLAYER_OPENAIRLOCK, SoundSource.BLOCKS, 1.0F, 1.0F); + BlockPos center = new BlockPos((f.minX + f.maxX) / 2, (f.minY + f.maxY) / 2, (f.minZ + f.maxZ) / 2); + server.playSound(null, center, GCSounds.PLAYER_OPENAIRLOCK, SoundSource.BLOCKS, 1.0F, 1.0F); } } + // --- UI / Status --- + + public AirlockState getAirlockState() { return this.state; } + public List getLastFrames() { return this.lastFrames; } + @Override public @NotNull Component getDisplayName() { - UUID owner = this.getSecurity().getOwner(); - String displayName; - - if (owner == null) { + // If no owner, default name + var security = this.getSecurity(); + if (security.getOwner() == null) { return Component.translatable(Translations.Ui.AIRLOCK_DEFAULT_NAME); - } else { - Optional profile = SkullBlockEntity.fetchGameProfile(owner).getNow(null); - if (profile != null && profile.isPresent()) { - displayName = profile.get().getName(); - } else { - return Component.translatable(Translations.Ui.AIRLOCK_DEFAULT_NAME); + } + + // Try server cache + String ownerName = null; + if (this.level != null && this.level.getServer() != null) { + GameProfileCache cache = this.level.getServer().getProfileCache(); + if (cache != null) { + Optional prof = cache.get(security.getOwner()); + ownerName = prof.map(GameProfile::getName).orElse(null); } } - return Component.translatable(Translations.Ui.AIRLOCK_OWNER, displayName); - } + // As a last resort, attempt skull fetch + if (ownerName == null) { + Optional prof = SkullBlockEntity.fetchGameProfile(security.getOwner()).getNow(Optional.empty()); + ownerName = prof.map(GameProfile::getName).orElse(null); + } - @Override - protected @NotNull MachineStatus tick(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { - serverTick(); - return GCMachineStatuses.SEALED; + if (ownerName == null || ownerName.isBlank()) { + return Component.translatable(Translations.Ui.AIRLOCK_DEFAULT_NAME); + } + return Component.translatable(Translations.Ui.AIRLOCK_OWNER, ownerName); } @Override - protected void tickDisabled(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { + protected @NotNull MachineStatus tick(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { serverTick(); - - super.tickDisabled(level, pos, state, profiler); + return switch (this.state) { + case ALL -> GCMachineStatuses.AIRLOCK_ENABLED; + case PARTIAL -> GCMachineStatuses.AIRLOCK_PARTIAL; + case NONE -> GCMachineStatuses.AIRLOCK_DISABLED; + }; } @Override - public AirlockControllerMenu createMenu(int syncId, Inventory inventory, Player player) { + public @Nullable MachineMenu createMenu(int syncId, Inventory inventory, Player player) { return new AirlockControllerMenu(syncId, player, this); } - public List getLastFrames() { - return this.lastFrames; + @Override + protected void tickDisabled(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { + serverTick(); + super.tickDisabled(level, pos, state, profiler); } - /** Unseal on BE removal (block broken / replaced). */ @Override public void setRemoved() { - if (!this.level.isClientSide() && this.lastFrames != null) { - for (var f : this.lastFrames) unseal(f); + if (this.level instanceof ServerLevel && this.lastFrameMap != null) { + for (long id : this.sealedFrames) { + AirlockFrameScanner.Result f = this.lastFrameMap.get(id); + if (f != null) unseal(f); + } + this.sealedFrames.clear(); } super.setRemoved(); } diff --git a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java index 8319522cc1..62175d6ab5 100644 --- a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java @@ -1128,6 +1128,7 @@ protected void generateUiTranslations() { this.add(Ui.AIRLOCK_DEFAULT_NAME, "Airlock Controller"); this.add(Ui.AIRLOCK_ENABLED, "Airlock Enabled"); + this.add(Ui.AIRLOCK_PARTIAL, "Airlock Partially Enabled"); this.add(Ui.AIRLOCK_DISABLED, "Airlock Disabled"); this.add(Ui.AIRLOCK_OPEN_WHEN_NEAR, "Airlock opens near player"); this.add(Ui.AIRLOCK_OWNER, "%s's Airlock Controller"); diff --git a/src/main/java/dev/galacticraft/mod/machine/GCMachineStatuses.java b/src/main/java/dev/galacticraft/mod/machine/GCMachineStatuses.java index 830de413bc..00bf92e9ef 100644 --- a/src/main/java/dev/galacticraft/mod/machine/GCMachineStatuses.java +++ b/src/main/java/dev/galacticraft/mod/machine/GCMachineStatuses.java @@ -56,6 +56,10 @@ public final class GCMachineStatuses { public static final MachineStatus ALREADY_SEALED = MachineStatus.create(Translations.MachineStatus.ALREADY_SEALED, ChatFormatting.RED, MachineStatus.Type.OTHER); public static final MachineStatus AREA_TOO_LARGE = MachineStatus.create(Translations.MachineStatus.AREA_TOO_LARGE, ChatFormatting.RED, MachineStatus.Type.PARTIALLY_WORKING); public static final MachineStatus SEALED = MachineStatus.create(Translations.MachineStatus.SEALED, ChatFormatting.GREEN, MachineStatus.Type.WORKING); + // Airlock + public static final MachineStatus AIRLOCK_ENABLED = MachineStatus.create(Translations.Ui.AIRLOCK_ENABLED, ChatFormatting.GREEN, MachineStatus.Type.WORKING); + public static final MachineStatus AIRLOCK_PARTIAL = MachineStatus.create(Translations.Ui.AIRLOCK_PARTIAL, ChatFormatting.YELLOW, MachineStatus.Type.WORKING); + public static final MachineStatus AIRLOCK_DISABLED = MachineStatus.create(Translations.Ui.AIRLOCK_DISABLED, ChatFormatting.RED, MachineStatus.Type.WORKING); // Oxygen - Bubble Distributor public static final MachineStatus DISTRIBUTING = MachineStatus.create(Translations.MachineStatus.DISTRIBUTING, ChatFormatting.GREEN, MachineStatus.Type.WORKING); diff --git a/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java b/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java index a79f0a6759..6cc21d7510 100644 --- a/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java +++ b/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java @@ -24,6 +24,7 @@ import dev.galacticraft.machinelib.api.menu.MachineMenu; import dev.galacticraft.machinelib.api.menu.MenuData; +import dev.galacticraft.mod.content.AirlockState; import dev.galacticraft.mod.content.block.entity.AirlockControllerBlockEntity; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.player.Inventory; @@ -33,13 +34,13 @@ import org.jetbrains.annotations.NotNull; public class AirlockControllerMenu extends MachineMenu { - public byte proximityOpen = 0; // 0..5 - public boolean enabled = false; + public byte proximityOpen; + public AirlockState state; public AirlockControllerMenu(int syncId, Player player, AirlockControllerBlockEntity be) { super(GCMenuTypes.AIRLOCK_CONTROLLER_MENU, syncId, player, be); this.proximityOpen = be.getProximityOpen(); - this.enabled = be.active; + this.state = be.getAirlockState(); } public AirlockControllerMenu(int syncId, Inventory inv, BlockPos pos) { @@ -50,6 +51,6 @@ public AirlockControllerMenu(int syncId, Inventory inv, BlockPos pos) { public void registerData(@NotNull MenuData data) { super.registerData(data); data.registerByte(this.be::getProximityOpen, b -> this.proximityOpen = (byte) b); - data.registerBoolean(() -> this.be.active, b -> this.enabled = b); + data.registerInt(() -> this.be.getAirlockState().ordinal(), b -> this.state = AirlockState.values()[b]); } } \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/util/Translations.java b/src/main/java/dev/galacticraft/mod/util/Translations.java index e153a581a0..85e44db8d1 100644 --- a/src/main/java/dev/galacticraft/mod/util/Translations.java +++ b/src/main/java/dev/galacticraft/mod/util/Translations.java @@ -386,6 +386,7 @@ interface Ui { String AIRLOCK_DEFAULT_NAME = "ui.galacticraft.airlock.default_name"; String AIRLOCK_ENABLED = "ui.galacticraft.airlock.enabled"; + String AIRLOCK_PARTIAL = "ui.galacticraft.airlock.partial"; String AIRLOCK_DISABLED = "ui.galacticraft.airlock.disabled"; String AIRLOCK_OPEN_WHEN_NEAR = "ui.galacticraft.airlock.open_when_near"; String AIRLOCK_OWNER = "ui.galacticraft.airlock.owner"; From 2c8dc3d0089d5e11ba15b0a0e9394b0b914bf852 Mon Sep 17 00:00:00 2001 From: maxryan008 Date: Wed, 13 Aug 2025 00:36:37 +1000 Subject: [PATCH 05/11] Apply spotless quickly --- .../mod/content/AirlockState.java | 22 +++++++++++++++++ .../entity/AirlockControllerBlockEntity.java | 9 ------- .../machine/airlock/AirlockFrameScanner.java | 24 +++++++++++++++++-- .../c2s/AirlockSetProximityPayload.java | 22 +++++++++++++++++ .../mod/screen/AirlockControllerMenu.java | 2 -- 5 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/main/java/dev/galacticraft/mod/content/AirlockState.java b/src/main/java/dev/galacticraft/mod/content/AirlockState.java index 88e05dfdcf..d56e8c671e 100644 --- a/src/main/java/dev/galacticraft/mod/content/AirlockState.java +++ b/src/main/java/dev/galacticraft/mod/content/AirlockState.java @@ -1,3 +1,25 @@ +/* + * Copyright (c) 2019-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package dev.galacticraft.mod.content; public enum AirlockState { diff --git a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java index 415a7c0182..e55ad4fff8 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java +++ b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java @@ -28,9 +28,7 @@ import dev.galacticraft.machinelib.api.machine.configuration.RedstoneMode; import dev.galacticraft.machinelib.api.menu.MachineMenu; import dev.galacticraft.machinelib.api.storage.MachineEnergyStorage; -import dev.galacticraft.machinelib.api.storage.MachineItemStorage; import dev.galacticraft.machinelib.api.storage.StorageSpec; -import dev.galacticraft.mod.Galacticraft; import dev.galacticraft.mod.content.AirlockState; import dev.galacticraft.mod.content.GCBlockEntityTypes; import dev.galacticraft.mod.content.GCBlocks; @@ -39,7 +37,6 @@ import dev.galacticraft.mod.machine.GCMachineStatuses; import dev.galacticraft.mod.screen.AirlockControllerMenu; import dev.galacticraft.mod.util.Translations; -import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; @@ -48,24 +45,18 @@ import net.minecraft.server.players.GameProfileCache; import net.minecraft.sounds.SoundSource; import net.minecraft.util.profiling.ProfilerFiller; -import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; -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.entity.BlockEntityType; import net.minecraft.world.level.block.entity.SkullBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import static dev.galacticraft.mod.content.block.special.AirlockSealBlock.FACING; public class AirlockControllerBlockEntity extends MachineBlockEntity { // --- UI-config (persisted) --- diff --git a/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java b/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java index 9ee72e343c..c2a9550788 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java +++ b/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java @@ -1,12 +1,32 @@ +/* + * Copyright (c) 2019-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package dev.galacticraft.mod.content.block.machine.airlock; -import dev.galacticraft.mod.content.GCBlocks; import dev.galacticraft.mod.content.block.entity.AirlockControllerBlockEntity; import dev.galacticraft.mod.tag.GCBlockTags; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Direction.Axis; -import net.minecraft.core.HolderSet; import net.minecraft.world.level.Level; import java.util.*; diff --git a/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java b/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java index 2270c684ec..fd7c5568ea 100644 --- a/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java +++ b/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java @@ -1,3 +1,25 @@ +/* + * Copyright (c) 2019-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package dev.galacticraft.mod.network.c2s; import dev.galacticraft.impl.network.c2s.C2SPayload; diff --git a/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java b/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java index 6cc21d7510..216687acbb 100644 --- a/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java +++ b/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java @@ -29,8 +29,6 @@ import net.minecraft.core.BlockPos; 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 org.jetbrains.annotations.NotNull; public class AirlockControllerMenu extends MachineMenu { From f65a6b8a8b7f3e4bbf58dc746045f5e9b4ba806c Mon Sep 17 00:00:00 2001 From: maxryan008 Date: Wed, 13 Aug 2025 10:29:16 +1000 Subject: [PATCH 06/11] Revised all suggestions to fix code --- .../assets/galacticraft/lang/en_us.json | 2 +- .../ingame/AirlockControllerScreen.java | 26 +++- .../entity/AirlockControllerBlockEntity.java | 132 ++++++++++++------ .../machine/airlock/AirlockFrameScanner.java | 17 +-- .../mod/data/GCTranslationProvider.java | 2 +- .../galacticraft/mod/util/Translations.java | 2 +- 6 files changed, 120 insertions(+), 61 deletions(-) diff --git a/src/main/generated/assets/galacticraft/lang/en_us.json b/src/main/generated/assets/galacticraft/lang/en_us.json index 4754a04fd5..21a2b2f48c 100644 --- a/src/main/generated/assets/galacticraft/lang/en_us.json +++ b/src/main/generated/assets/galacticraft/lang/en_us.json @@ -886,8 +886,8 @@ "ui.galacticraft.airlock.disabled": "Airlock Disabled", "ui.galacticraft.airlock.enabled": "Airlock Enabled", "ui.galacticraft.airlock.open_when_near": "Airlock opens near player", - "ui.galacticraft.airlock.owner": "%s's Airlock Controller", "ui.galacticraft.airlock.partial": "Airlock Partially Enabled", + "ui.galacticraft.airlock_proximity_label": "Airlock Proximity:", "ui.galacticraft.alpha_warning.content1": "Galacticraft is currently in ALPHA.", "ui.galacticraft.alpha_warning.content2": "Please report all issues you find.", "ui.galacticraft.alpha_warning.content3": "Press [ESC] or click to continue.", diff --git a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java index bc22402b8d..1eb3411972 100644 --- a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java +++ b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java @@ -33,10 +33,12 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.EditBox; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; +import org.jetbrains.annotations.NotNull; public class AirlockControllerScreen extends MachineScreen { private final EditBox textField; @@ -48,7 +50,7 @@ public AirlockControllerScreen(AirlockControllerMenu menu, Inventory inv, Compon Minecraft.getInstance().font, this.leftPos + 132, this.topPos + 65, 26, 20, - Component.literal("") + Component.empty() ); this.textField.setValue(String.valueOf(this.menu.proximityOpen)); @@ -90,7 +92,7 @@ public AirlockControllerScreen(AirlockControllerMenu menu, Inventory inv, Compon protected void init() { super.init(); this.imageHeight = 171; - this.titleLabelX = 4; + this.titleLabelX = 90; this.textField.setX(this.leftPos + 132); this.textField.setY(this.topPos + 65); @@ -104,16 +106,16 @@ protected void renderMachineBackground(GuiGraphics g, int mouseX, int mouseY, fl int color; if (enabled.equals(AirlockState.ALL)) { label = Component.translatable(Translations.Ui.AIRLOCK_ENABLED); - color = ChatFormatting.GREEN.getColor(); + color = ChatFormatting.DARK_GREEN.getColor(); } else if (enabled.equals(AirlockState.PARTIAL)) { label = Component.translatable(Translations.Ui.AIRLOCK_PARTIAL); - color = ChatFormatting.YELLOW.getColor(); + color = ChatFormatting.DARK_PURPLE.getColor(); } else { label = Component.translatable(Translations.Ui.AIRLOCK_DISABLED); color = ChatFormatting.RED.getColor(); } - g.drawCenteredString(this.font, label.getString(), this.leftPos + 90, this.topPos + 22, color); + drawCenteredString(g, this.font, label, this.leftPos + 90, this.topPos + 22, color, false); int upX = this.leftPos + 158, upY = this.topPos + 65; int downX = this.leftPos + 158, downY = this.topPos + 75; @@ -131,7 +133,7 @@ protected void renderMachineBackground(GuiGraphics g, int mouseX, int mouseY, fl hoverDown ? Constant.TextureCoordinate.ARROW_DOWN_HOVER_Y : Constant.TextureCoordinate.ARROW_DOWN_Y, Constant.TextureCoordinate.ARROW_VERTICAL_WIDTH, Constant.TextureCoordinate.ARROW_VERTICAL_HEIGHT); - g.drawString(this.font, Component.literal("Airlock Proximity:"), this.leftPos + 46, this.topPos + 71, ChatFormatting.DARK_GRAY.getColor(), false); + drawStringAlignedRight(g, this.font, Component.translatable(Translations.Ui.AIRLOCK_PROXIMITY_LABEL), this.leftPos + 130, this.topPos + 71, ChatFormatting.DARK_GRAY.getColor(), false); } @Override @@ -147,6 +149,10 @@ protected void renderForeground(GuiGraphics graphics, int mouseX, int mouseY, fl } } + @Override + protected void drawTitle(@NotNull GuiGraphics graphics) { + drawCenteredString(graphics, this.font, this.title, this.titleLabelX, this.titleLabelY, 0xFF404040, false); + } @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { @@ -178,4 +184,12 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { } return super.mouseClicked(mouseX, mouseY, button); } + + public void drawCenteredString(GuiGraphics g, Font font, Component text, int centerX, int y, int color, boolean shadow) { + g.drawString(font, text, centerX - font.width(text) / 2, y, color, shadow); + } + + public void drawStringAlignedRight(GuiGraphics g, Font font, Component text, int x, int y, int color, boolean shadow) { + g.drawString(font, text, x - this.font.width(text), y, color, shadow); + } } \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java index e55ad4fff8..3557ae1e5e 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java +++ b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java @@ -47,8 +47,10 @@ import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; +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.entity.SkullBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; @@ -59,6 +61,10 @@ public class AirlockControllerBlockEntity extends MachineBlockEntity { + // --- NBT keys + private static final String NBT_PROX = "ProximityOpen"; + private static final String NBT_SEALED = "SealedFrames"; + // --- UI-config (persisted) --- private byte proximityOpen = 0; @@ -89,19 +95,67 @@ public void setProximityOpen(byte v) { @Override protected void saveAdditional(CompoundTag tag, HolderLookup.Provider lookup) { super.saveAdditional(tag, lookup); - tag.putByte("ProximityOpen", this.proximityOpen); + tag.putByte(NBT_PROX, this.proximityOpen); + + // persist sealed frame IDs (longs) + long[] arr = this.sealedFrames.stream().mapToLong(Long::longValue).toArray(); + tag.putLongArray(NBT_SEALED, arr); } @Override public void loadAdditional(CompoundTag tag, HolderLookup.Provider lookup) { super.loadAdditional(tag, lookup); - if (tag.contains("ProximityOpen")) this.proximityOpen = tag.getByte("ProximityOpen"); + if (tag.contains(NBT_PROX)) { + this.proximityOpen = tag.getByte(NBT_PROX); + } + // read sealed IDs; don't try to reseal yet (level may not be ready) + if (tag.contains(NBT_SEALED)) { + this.sealedFrames.clear(); + for (long id : tag.getLongArray(NBT_SEALED)) this.sealedFrames.add(id); + } + onLoad(); } // --- Core logic --- - private void serverTick() { + /** Re-apply seals after the chunk loads. */ + public void onLoad() { if (!(this.level instanceof ServerLevel server)) return; + + // Re-scan the frames for current geometry + List frames = AirlockFrameScanner.scanAll(server, this.worldPosition); + Map frameMap = indexFrames(frames); + + // Place seals for frames that were persisted as sealed + boolean changed = false; + for (long id : this.sealedFrames) { + AirlockFrameScanner.Result f = frameMap.get(id); + if (f != null) { + // Ensure they're sealed (idempotent: only places over air) + seal(f); + changed = true; + } + } + + // restore runtime caches/state + this.lastFrames = frames; + this.lastFrameMap = frameMap; + + // recompute state enum + if (this.sealedFrames.isEmpty() || frames.isEmpty()) this.state = AirlockState.NONE; + else if (this.sealedFrames.size() == frames.size()) this.state = AirlockState.ALL; + else this.state = AirlockState.PARTIAL; + + if (changed) { + BlockState s = server.getBlockState(this.worldPosition); + server.sendBlockUpdated(this.worldPosition, s, s, Block.UPDATE_CLIENTS); + setChanged(); + } + } + + private void serverTick() { + ServerLevel server = (ServerLevel) this.level; + assert server != null; if ((++this.ticks % 5) != 0) return; // tick every 5 List frames = AirlockFrameScanner.scanAll(server, this.worldPosition); @@ -329,37 +383,17 @@ private void unseal(AirlockFrameScanner.Result f) { @Override public @NotNull Component getDisplayName() { - // If no owner, default name - var security = this.getSecurity(); - if (security.getOwner() == null) { - return Component.translatable(Translations.Ui.AIRLOCK_DEFAULT_NAME); - } - - // Try server cache - String ownerName = null; - if (this.level != null && this.level.getServer() != null) { - GameProfileCache cache = this.level.getServer().getProfileCache(); - if (cache != null) { - Optional prof = cache.get(security.getOwner()); - ownerName = prof.map(GameProfile::getName).orElse(null); - } - } - - // As a last resort, attempt skull fetch - if (ownerName == null) { - Optional prof = SkullBlockEntity.fetchGameProfile(security.getOwner()).getNow(Optional.empty()); - ownerName = prof.map(GameProfile::getName).orElse(null); - } + return Component.translatable(Translations.Ui.AIRLOCK_DEFAULT_NAME); + } - if (ownerName == null || ownerName.isBlank()) { - return Component.translatable(Translations.Ui.AIRLOCK_DEFAULT_NAME); - } - return Component.translatable(Translations.Ui.AIRLOCK_OWNER, ownerName); + @Override + protected void tickConstant(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { + serverTick(); + super.tickConstant(level, pos, state, profiler); } @Override protected @NotNull MachineStatus tick(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { - serverTick(); return switch (this.state) { case ALL -> GCMachineStatuses.AIRLOCK_ENABLED; case PARTIAL -> GCMachineStatuses.AIRLOCK_PARTIAL; @@ -372,21 +406,37 @@ private void unseal(AirlockFrameScanner.Result f) { return new AirlockControllerMenu(syncId, player, this); } - @Override - protected void tickDisabled(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { - serverTick(); - super.tickDisabled(level, pos, state, profiler); - } - @Override public void setRemoved() { - if (this.level instanceof ServerLevel && this.lastFrameMap != null) { - for (long id : this.sealedFrames) { - AirlockFrameScanner.Result f = this.lastFrameMap.get(id); - if (f != null) unseal(f); + try { + if (this.level instanceof ServerLevel server) { + boolean shouldUnseal = false; + + boolean chunkLoaded; + chunkLoaded = server.isLoaded(this.worldPosition); + + if (chunkLoaded) { + BlockEntity current = server.getBlockEntity(this.worldPosition); + if (current == null || current != this) { + shouldUnseal = true; + } else { + var stateAtPos = server.getBlockState(this.worldPosition); + if (stateAtPos.isAir()) { + shouldUnseal = true; + } + } + } + + if (shouldUnseal) { + for (long id : this.sealedFrames) { + var f = this.lastFrameMap.get(id); + if (f != null) unseal(f); + } + this.sealedFrames.clear(); + } } - this.sealedFrames.clear(); + } finally { + super.setRemoved(); } - super.setRemoved(); } } \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java b/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java index c2a9550788..25c3998f66 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java +++ b/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java @@ -68,8 +68,9 @@ public static final class Result { } private static boolean isFrame(Level level, BlockPos pos) { - return level.getBlockState(pos).getBlock().defaultBlockState().is(GCBlockTags.AIRLOCK_BLOCKS); + return level.getBlockState(pos).is(GCBlockTags.AIRLOCK_BLOCKS); } + private static boolean isController(Level level, BlockPos pos) { return level.getBlockEntity(pos) instanceof AirlockControllerBlockEntity; } @@ -98,7 +99,7 @@ public static Result scan(Level level, BlockPos controller) { private record Axes(Axis u, Axis v, Axis wConst) {} private static List scanPlane(Level level, BlockPos controller, Plane plane) { - final int fixed = coord(controller, plane.normal); + final int fixed = controller.get(plane.normal); final Axes axes = axesFor(plane); // u,v are the in-plane axes // 1) Gather all frame blocks connected to controller within this plane @@ -324,7 +325,7 @@ private static Set floodInPlane(Level level, BlockPos start, Plane pla BlockPos p = q.removeFirst(); for (Direction d : inPlaneDirections(plane)) { BlockPos n = p.relative(d); - if (coord(n, plane.normal) != fixed) continue; + if (n.get(plane.normal) != fixed) continue; if (!visited.contains(n) && isFrame(level, n)) { visited.add(n); q.add(n); @@ -342,16 +343,10 @@ private static Direction[] inPlaneDirections(Plane plane) { }; } - private static int coord(BlockPos p, Axis a) { - return switch (a) { - case X -> p.getX(); - case Y -> p.getY(); - case Z -> p.getZ(); - }; - } private static int proj(BlockPos p, Axis axis) { - return coord(p, axis); + return p.get(axis); } + private static BlockPos unproj(int u, int v, int wConst, Axes axes) { // inverse mapping from (u,v) back to (x,y,z) int x=0,y=0,z=0; diff --git a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java index 62175d6ab5..dbfc666437 100644 --- a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java @@ -1126,12 +1126,12 @@ protected void generateUiTranslations() { this.add(Ui.UPGRADE, "Upgrade"); this.add(Ui.COLOR, "Color"); + this.add(Ui.AIRLOCK_PROXIMITY_LABEL, "Airlock Proximity:"); this.add(Ui.AIRLOCK_DEFAULT_NAME, "Airlock Controller"); this.add(Ui.AIRLOCK_ENABLED, "Airlock Enabled"); this.add(Ui.AIRLOCK_PARTIAL, "Airlock Partially Enabled"); this.add(Ui.AIRLOCK_DISABLED, "Airlock Disabled"); this.add(Ui.AIRLOCK_OPEN_WHEN_NEAR, "Airlock opens near player"); - this.add(Ui.AIRLOCK_OWNER, "%s's Airlock Controller"); this.add(Ui.ALPHA_WARNING_1, "Galacticraft is currently in ALPHA."); this.add(Ui.ALPHA_WARNING_2, "Please report all issues you find."); this.add(Ui.ALPHA_WARNING_3, "Press [ESC] or click to continue."); diff --git a/src/main/java/dev/galacticraft/mod/util/Translations.java b/src/main/java/dev/galacticraft/mod/util/Translations.java index 85e44db8d1..0bed484fcb 100644 --- a/src/main/java/dev/galacticraft/mod/util/Translations.java +++ b/src/main/java/dev/galacticraft/mod/util/Translations.java @@ -384,12 +384,12 @@ interface Ui { String UPGRADE = "ui.galacticraft.upgrade"; String COLOR = "ui.galacticraft.color"; + String AIRLOCK_PROXIMITY_LABEL = "ui.galacticraft.airlock_proximity_label"; String AIRLOCK_DEFAULT_NAME = "ui.galacticraft.airlock.default_name"; String AIRLOCK_ENABLED = "ui.galacticraft.airlock.enabled"; String AIRLOCK_PARTIAL = "ui.galacticraft.airlock.partial"; String AIRLOCK_DISABLED = "ui.galacticraft.airlock.disabled"; String AIRLOCK_OPEN_WHEN_NEAR = "ui.galacticraft.airlock.open_when_near"; - String AIRLOCK_OWNER = "ui.galacticraft.airlock.owner"; String ALPHA_WARNING_1 = "ui.galacticraft.alpha_warning.content1"; String ALPHA_WARNING_2 = "ui.galacticraft.alpha_warning.content2"; String ALPHA_WARNING_3 = "ui.galacticraft.alpha_warning.content3"; From 463f04720167aa0b8f8e3ebd7d622722dab5990e Mon Sep 17 00:00:00 2001 From: maxryan008 Date: Wed, 13 Aug 2025 10:34:38 +1000 Subject: [PATCH 07/11] Ran spotlessApply --- .../mod/client/gui/screen/ingame/AirlockControllerScreen.java | 2 +- .../content/block/entity/AirlockControllerBlockEntity.java | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java index 1eb3411972..9699c8ff6a 100644 --- a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java +++ b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java @@ -188,7 +188,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { public void drawCenteredString(GuiGraphics g, Font font, Component text, int centerX, int y, int color, boolean shadow) { g.drawString(font, text, centerX - font.width(text) / 2, y, color, shadow); } - + public void drawStringAlignedRight(GuiGraphics g, Font font, Component text, int x, int y, int color, boolean shadow) { g.drawString(font, text, x - this.font.width(text), y, color, shadow); } diff --git a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java index 3557ae1e5e..a657fec6ec 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java +++ b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java @@ -22,7 +22,6 @@ package dev.galacticraft.mod.content.block.entity; -import com.mojang.authlib.GameProfile; import dev.galacticraft.machinelib.api.block.entity.MachineBlockEntity; import dev.galacticraft.machinelib.api.machine.MachineStatus; import dev.galacticraft.machinelib.api.machine.configuration.RedstoneMode; @@ -42,16 +41,13 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.players.GameProfileCache; import net.minecraft.sounds.SoundSource; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; -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.entity.SkullBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import org.jetbrains.annotations.NotNull; From a7a3799b0c67c73edcd656a8bf7d1532278c1f1e Mon Sep 17 00:00:00 2001 From: maxryan008 Date: Fri, 15 Aug 2025 14:59:29 +1000 Subject: [PATCH 08/11] Fixed airlock player proximity opening to be separate from player access. --- .../ingame/AirlockControllerScreen.java | 154 ++++++++++++++++++ .../mod/content/ProximityAccess.java | 29 ++++ .../entity/AirlockControllerBlockEntity.java | 64 ++++---- .../galacticraft/mod/network/GCPackets.java | 1 + .../mod/network/GCServerPacketReceivers.java | 1 + .../c2s/AirlockSetProximityAccessPayload.java | 70 ++++++++ .../c2s/AirlockSetProximityPayload.java | 1 - .../mod/screen/AirlockControllerMenu.java | 4 + 8 files changed, 294 insertions(+), 30 deletions(-) create mode 100644 src/main/java/dev/galacticraft/mod/content/ProximityAccess.java create mode 100644 src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityAccessPayload.java diff --git a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java index 9699c8ff6a..c9d9317437 100644 --- a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java +++ b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/AirlockControllerScreen.java @@ -23,6 +23,8 @@ package dev.galacticraft.mod.client.gui.screen.ingame; import dev.galacticraft.mod.content.AirlockState; +import dev.galacticraft.mod.content.ProximityAccess; +import dev.galacticraft.mod.network.c2s.AirlockSetProximityAccessPayload; import dev.galacticraft.mod.network.c2s.AirlockSetProximityPayload; import dev.galacticraft.machinelib.client.api.screen.MachineScreen; import dev.galacticraft.mod.Constant; @@ -35,14 +37,101 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractButton; import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Inventory; import org.jetbrains.annotations.NotNull; public class AirlockControllerScreen extends MachineScreen { private final EditBox textField; + private IconButton publicBtn; + private IconButton teamBtn; + private IconButton privateBtn; + + private static final ResourceLocation MACHINELIB_PANELS = + ResourceLocation.fromNamespaceAndPath("machinelib", "textures/gui/machine_panels.png"); + + private static final int TEX_W = 256, TEX_H = 256; + + private static final int BTN_U = 0; + private static final int BTN_V_NORMAL = 196; + private static final int BTN_V_HOVER = 216; + private static final int BTN_V_SELECTED = 236; + private static final int BTN_W = 20, BTN_H = 20; + + private static final int PUB_U = 208, PUB_V = 49, PUB_W = 15, PUB_H = 15; + private static final int TEAM_U = 210, TEAM_V = 71, TEAM_W = 12, TEAM_H = 14; + private static final int PRIV_U = 231, PRIV_V = 49, PRIV_W = 10, PRIV_H = 14; + + private static final int ACCESS_BTN_SIZE = 20; + private static final int ACCESS_BTN_GAP = 6; + + private enum AccessMode { PUBLIC, TEAM, PRIVATE } + private AccessMode selected = AccessMode.PUBLIC; + private ProximityAccess cachedAccess = null; + + private ProximityAccess currentAccess() { + return this.menu.proximityAccess != null ? this.menu.proximityAccess : ProximityAccess.PUBLIC; + } + + private static class IconButton extends AbstractButton { + @FunctionalInterface interface PressHandler { void onPress(IconButton b); } + + private final ResourceLocation texture; + private final int iconU, iconV, iconW, iconH; + private final PressHandler handler; + + private boolean selected; + + IconButton(int x, int y, int size, + ResourceLocation texture, + int iconU, int iconV, int iconW, int iconH, + PressHandler handler, + Component tooltipText) { + super(x, y, size, size, Component.empty()); + this.texture = texture; + this.iconU = iconU; this.iconV = iconV; this.iconW = iconW; this.iconH = iconH; + this.handler = handler; + if (!tooltipText.getString().isEmpty()) this.setTooltip(Tooltip.create(tooltipText)); + } + + void setSelected(boolean sel) { this.selected = sel; } + + @Override public void onPress() { handler.onPress(this); } + + @Override + protected void renderWidget(GuiGraphics g, int mouseX, int mouseY, float delta) { + final int v = this.selected ? BTN_V_SELECTED : (this.isHovered() ? BTN_V_HOVER : BTN_V_NORMAL); + g.blit(texture, getX(), getY(), BTN_U, v, BTN_W, BTN_H, TEX_W, TEX_H); + + int ix = getX() + (this.width - iconW) / 2 + 1; + int iy = getY() + (this.height - iconH) / 2; + g.blit(texture, ix, iy, iconU, iconV, iconW, iconH, TEX_W, TEX_H); + } + + @Override protected void updateWidgetNarration(NarrationElementOutput out) {} + } + + @Override + protected void containerTick() { + super.containerTick(); + + ProximityAccess now = currentAccess(); + if (now != this.cachedAccess) { + if (publicBtn != null && teamBtn != null && privateBtn != null) { + publicBtn.setSelected(now == ProximityAccess.PUBLIC); + teamBtn.setSelected(now == ProximityAccess.TEAM); + privateBtn.setSelected(now == ProximityAccess.PRIVATE); + } + this.cachedAccess = now; + } + } + public AirlockControllerScreen(AirlockControllerMenu menu, Inventory inv, Component title) { super(menu, title, Constant.ScreenTexture.AIRLOCK_CONTROLLER_SCREEN); @@ -97,6 +186,71 @@ protected void init() { this.textField.setX(this.leftPos + 132); this.textField.setY(this.topPos + 65); this.addRenderableWidget(this.textField); + + ProximityAccess initial = currentAccess(); + switch (initial) { + case PUBLIC -> selected = AccessMode.PUBLIC; + case TEAM -> selected = AccessMode.TEAM; + case PRIVATE -> selected = AccessMode.PRIVATE; + } + + this.publicBtn = new IconButton(0, 0, ACCESS_BTN_SIZE, MACHINELIB_PANELS, PUB_U, PUB_V, PUB_W, PUB_H, b -> { + selected = AccessMode.PUBLIC; + publicBtn.setSelected(true); teamBtn.setSelected(false); privateBtn.setSelected(false); + this.menu.proximityAccess = ProximityAccess.PUBLIC; + ClientPlayNetworking.send(new AirlockSetProximityAccessPayload(ProximityAccess.PUBLIC)); + }, Component.literal("Public")); + + this.teamBtn = new IconButton(0, 0, ACCESS_BTN_SIZE, MACHINELIB_PANELS, TEAM_U, TEAM_V, TEAM_W, TEAM_H, b -> { + selected = AccessMode.TEAM; + publicBtn.setSelected(false); teamBtn.setSelected(true); privateBtn.setSelected(false); + this.menu.proximityAccess = ProximityAccess.TEAM; + ClientPlayNetworking.send(new AirlockSetProximityAccessPayload(ProximityAccess.TEAM)); + }, Component.literal("Team")); + + this.privateBtn = new IconButton(0, 0, ACCESS_BTN_SIZE, MACHINELIB_PANELS, PRIV_U, PRIV_V, PRIV_W, PRIV_H, b -> { + selected = AccessMode.PRIVATE; + publicBtn.setSelected(false); teamBtn.setSelected(false); privateBtn.setSelected(true); + this.menu.proximityAccess = ProximityAccess.PRIVATE; + ClientPlayNetworking.send(new AirlockSetProximityAccessPayload(ProximityAccess.PRIVATE)); + }, Component.literal("Private")); + + this.addRenderableWidget(this.publicBtn); + this.addRenderableWidget(this.teamBtn); + this.addRenderableWidget(this.privateBtn); + + publicBtn.setSelected(initial == ProximityAccess.PUBLIC); + teamBtn.setSelected(initial == ProximityAccess.TEAM); + privateBtn.setSelected(initial == ProximityAccess.PRIVATE); + + this.cachedAccess = initial; + + layoutAccessButtons(); + } + + @Override + protected void repositionElements() { + super.repositionElements(); + layoutAccessButtons(); + } + + private void layoutAccessButtons() { + if (publicBtn == null || teamBtn == null || privateBtn == null) return; + + int centerX = this.leftPos + (this.imageWidth / 2); + int centerY = this.topPos + (this.imageHeight / 4); + + int total = ACCESS_BTN_SIZE * 3 + ACCESS_BTN_GAP * 2; + int startX = centerX - total / 2; + int y = centerY - ACCESS_BTN_SIZE / 2; + + this.publicBtn.setPosition(startX, y); + this.teamBtn.setPosition(startX + ACCESS_BTN_SIZE + ACCESS_BTN_GAP, y); + this.privateBtn.setPosition(startX + (ACCESS_BTN_SIZE + ACCESS_BTN_GAP) * 2, y); + + this.publicBtn.active = this.publicBtn.visible = true; + this.teamBtn.active = this.teamBtn.visible = true; + this.privateBtn.active = this.privateBtn.visible = true; } @Override diff --git a/src/main/java/dev/galacticraft/mod/content/ProximityAccess.java b/src/main/java/dev/galacticraft/mod/content/ProximityAccess.java new file mode 100644 index 0000000000..ed610ad422 --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/content/ProximityAccess.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.galacticraft.mod.content; + +public enum ProximityAccess { + PUBLIC, + TEAM, + PRIVATE; +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java index a657fec6ec..a99e4d08ca 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java +++ b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java @@ -28,10 +28,7 @@ import dev.galacticraft.machinelib.api.menu.MachineMenu; import dev.galacticraft.machinelib.api.storage.MachineEnergyStorage; import dev.galacticraft.machinelib.api.storage.StorageSpec; -import dev.galacticraft.mod.content.AirlockState; -import dev.galacticraft.mod.content.GCBlockEntityTypes; -import dev.galacticraft.mod.content.GCBlocks; -import dev.galacticraft.mod.content.GCSounds; +import dev.galacticraft.mod.content.*; import dev.galacticraft.mod.content.block.machine.airlock.AirlockFrameScanner; import dev.galacticraft.mod.machine.GCMachineStatuses; import dev.galacticraft.mod.screen.AirlockControllerMenu; @@ -57,18 +54,28 @@ public class AirlockControllerBlockEntity extends MachineBlockEntity { - // --- NBT keys private static final String NBT_PROX = "ProximityOpen"; private static final String NBT_SEALED = "SealedFrames"; + private static final String NBT_PROX_ACCESS = "ProximityAccess"; - // --- UI-config (persisted) --- private byte proximityOpen = 0; + private ProximityAccess proximityAccess = ProximityAccess.PUBLIC; + + + + public ProximityAccess getProximityAccess() { return this.proximityAccess; } + public void setProximityAccess(ProximityAccess access) { + if (access == null) access = ProximityAccess.PUBLIC; + if (this.proximityAccess != access) { + this.proximityAccess = access; + setChanged(); + } + } private static final StorageSpec SPEC = StorageSpec.of( MachineEnergyStorage.spec(0, 0) ); - // --- Runtime --- private List lastFrames = Collections.emptyList(); private Map lastFrameMap = Collections.emptyMap(); private final Set sealedFrames = new HashSet<>(); @@ -80,8 +87,6 @@ public AirlockControllerBlockEntity(BlockPos pos, BlockState state) { super(GCBlockEntityTypes.AIRLOCK_CONTROLLER, pos, state, SPEC); } - // --- Persisted config --- - public byte getProximityOpen() { return this.proximityOpen; } public void setProximityOpen(byte v) { this.proximityOpen = (byte) Math.max(0, Math.min(5, v)); @@ -92,8 +97,8 @@ public void setProximityOpen(byte v) { protected void saveAdditional(CompoundTag tag, HolderLookup.Provider lookup) { super.saveAdditional(tag, lookup); tag.putByte(NBT_PROX, this.proximityOpen); + tag.putInt(NBT_PROX_ACCESS, this.proximityAccess.ordinal()); - // persist sealed frame IDs (longs) long[] arr = this.sealedFrames.stream().mapToLong(Long::longValue).toArray(); tag.putLongArray(NBT_SEALED, arr); } @@ -101,10 +106,12 @@ protected void saveAdditional(CompoundTag tag, HolderLookup.Provider lookup) { @Override public void loadAdditional(CompoundTag tag, HolderLookup.Provider lookup) { super.loadAdditional(tag, lookup); - if (tag.contains(NBT_PROX)) { - this.proximityOpen = tag.getByte(NBT_PROX); + if (tag.contains(NBT_PROX)) this.proximityOpen = tag.getByte(NBT_PROX); + if (tag.contains(NBT_PROX_ACCESS)) { + int ord = tag.getInt(NBT_PROX_ACCESS); + this.proximityAccess = (ord >= 0 && ord < ProximityAccess.values().length) + ? ProximityAccess.values()[ord] : ProximityAccess.PUBLIC; } - // read sealed IDs; don't try to reseal yet (level may not be ready) if (tag.contains(NBT_SEALED)) { this.sealedFrames.clear(); for (long id : tag.getLongArray(NBT_SEALED)) this.sealedFrames.add(id); @@ -112,32 +119,24 @@ public void loadAdditional(CompoundTag tag, HolderLookup.Provider lookup) { onLoad(); } - // --- Core logic --- - - /** Re-apply seals after the chunk loads. */ public void onLoad() { if (!(this.level instanceof ServerLevel server)) return; - // Re-scan the frames for current geometry List frames = AirlockFrameScanner.scanAll(server, this.worldPosition); Map frameMap = indexFrames(frames); - // Place seals for frames that were persisted as sealed boolean changed = false; for (long id : this.sealedFrames) { AirlockFrameScanner.Result f = frameMap.get(id); if (f != null) { - // Ensure they're sealed (idempotent: only places over air) seal(f); changed = true; } } - // restore runtime caches/state this.lastFrames = frames; this.lastFrameMap = frameMap; - // recompute state enum if (this.sealedFrames.isEmpty() || frames.isEmpty()) this.state = AirlockState.NONE; else if (this.sealedFrames.size() == frames.size()) this.state = AirlockState.ALL; else this.state = AirlockState.PARTIAL; @@ -152,7 +151,7 @@ public void onLoad() { private void serverTick() { ServerLevel server = (ServerLevel) this.level; assert server != null; - if ((++this.ticks % 5) != 0) return; // tick every 5 + if ((++this.ticks % 5) != 0) return; List frames = AirlockFrameScanner.scanAll(server, this.worldPosition); Map frameMap = indexFrames(frames); @@ -170,10 +169,21 @@ private void serverTick() { if (r > 0) { AABB expanded = expandedInterior(f, r); for (Player p : server.getEntitiesOfClass(Player.class, expanded)) { - if (this.getSecurity().hasAccess(p)) { - anyAuthorizedNear = true; - break; + switch (this.proximityAccess) { + case PUBLIC -> { + anyAuthorizedNear = true; + } + case TEAM -> { + if (this.getSecurity().hasAccess(p)) { + anyAuthorizedNear = true; + } + } + case PRIVATE -> { + boolean isOwner = this.getSecurity().isOwner(p); + if (isOwner) anyAuthorizedNear = true; + } } + if (anyAuthorizedNear) break; } } if (!anyAuthorizedNear) { @@ -224,8 +234,6 @@ private void serverTick() { } } - // --- Helpers --- - private static Map indexFrames(List list) { Map out = new HashMap<>(list.size()); for (AirlockFrameScanner.Result r : list) out.put(frameId(r), r); @@ -372,8 +380,6 @@ private void unseal(AirlockFrameScanner.Result f) { } } - // --- UI / Status --- - public AirlockState getAirlockState() { return this.state; } public List getLastFrames() { return this.lastFrames; } diff --git a/src/main/java/dev/galacticraft/mod/network/GCPackets.java b/src/main/java/dev/galacticraft/mod/network/GCPackets.java index f89bfc3557..cd4776e14f 100644 --- a/src/main/java/dev/galacticraft/mod/network/GCPackets.java +++ b/src/main/java/dev/galacticraft/mod/network/GCPackets.java @@ -46,5 +46,6 @@ public static void register() { PayloadTypeRegistry.playC2S().register(SatelliteCreationPayload.TYPE, SatelliteCreationPayload.STREAM_CODEC); PayloadTypeRegistry.playC2S().register(AirlockSetProximityPayload.TYPE, AirlockSetProximityPayload.STREAM_CODEC); + PayloadTypeRegistry.playC2S().register(AirlockSetProximityAccessPayload.TYPE, AirlockSetProximityAccessPayload.STREAM_CODEC); } } diff --git a/src/main/java/dev/galacticraft/mod/network/GCServerPacketReceivers.java b/src/main/java/dev/galacticraft/mod/network/GCServerPacketReceivers.java index 5b8a2c6800..0750d4b984 100644 --- a/src/main/java/dev/galacticraft/mod/network/GCServerPacketReceivers.java +++ b/src/main/java/dev/galacticraft/mod/network/GCServerPacketReceivers.java @@ -42,6 +42,7 @@ public static void register() { registerPacket(PlanetTeleportPayload.TYPE); registerPacket(SatelliteCreationPayload.TYPE); registerPacket(AirlockSetProximityPayload.TYPE); + registerPacket(AirlockSetProximityAccessPayload.TYPE); } public static

void registerPacket(CustomPacketPayload.Type

type) { diff --git a/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityAccessPayload.java b/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityAccessPayload.java new file mode 100644 index 0000000000..5ec6331f53 --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityAccessPayload.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.galacticraft.mod.network.c2s; + +import dev.galacticraft.impl.network.c2s.C2SPayload; +import dev.galacticraft.mod.Constant; +import dev.galacticraft.mod.content.ProximityAccess; +import dev.galacticraft.mod.content.block.entity.AirlockControllerBlockEntity; +import dev.galacticraft.mod.screen.AirlockControllerMenu; +import io.netty.buffer.ByteBuf; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +public record AirlockSetProximityAccessPayload(ProximityAccess access) implements C2SPayload { + public static final StreamCodec STREAM_CODEC = + ByteBufCodecs.VAR_INT.map( + i -> { + ProximityAccess[] vals = ProximityAccess.values(); + ProximityAccess a = (i >= 0 && i < vals.length) ? vals[i] : ProximityAccess.PUBLIC; + return new AirlockSetProximityAccessPayload(a); + }, + p -> p.access.ordinal() + ); + + public static final ResourceLocation ID = Constant.id("airlock_set_proximity_access"); + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(ID); + + @Override + public void handle(ServerPlayNetworking.@NotNull Context context) { + if (context.player().containerMenu instanceof AirlockControllerMenu menu) { + AirlockControllerBlockEntity be = menu.be; + if (be != null && be.getLevel() != null && be.getLevel().isLoaded(be.getBlockPos())) { + if (!(be instanceof dev.galacticraft.machinelib.api.block.entity.MachineBlockEntity mb) + || mb.getSecurity().hasAccess(context.player())) { + be.setProximityAccess(this.access); + be.setChanged(); + } + } + } + } + + @Override + public @NotNull Type type() { + return TYPE; + } +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java b/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java index fd7c5568ea..8e844cb97f 100644 --- a/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java +++ b/src/main/java/dev/galacticraft/mod/network/c2s/AirlockSetProximityPayload.java @@ -47,7 +47,6 @@ public void handle(ServerPlayNetworking.@NotNull Context context) { if (be != null && be.getLevel() != null && be.getLevel().isLoaded(be.getBlockPos())) { if (!(be instanceof dev.galacticraft.machinelib.api.block.entity.MachineBlockEntity mb) || mb.getSecurity().hasAccess(context.player())) { - // clamp 0..5 on the server just in case byte clamped = (byte) Math.max(0, Math.min(5, this.proximity)); be.setProximityOpen(clamped); be.setChanged(); diff --git a/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java b/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java index 216687acbb..81580c77a2 100644 --- a/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java +++ b/src/main/java/dev/galacticraft/mod/screen/AirlockControllerMenu.java @@ -25,6 +25,7 @@ import dev.galacticraft.machinelib.api.menu.MachineMenu; import dev.galacticraft.machinelib.api.menu.MenuData; import dev.galacticraft.mod.content.AirlockState; +import dev.galacticraft.mod.content.ProximityAccess; import dev.galacticraft.mod.content.block.entity.AirlockControllerBlockEntity; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.player.Inventory; @@ -34,11 +35,13 @@ public class AirlockControllerMenu extends MachineMenu { public byte proximityOpen; public AirlockState state; + public ProximityAccess proximityAccess; public AirlockControllerMenu(int syncId, Player player, AirlockControllerBlockEntity be) { super(GCMenuTypes.AIRLOCK_CONTROLLER_MENU, syncId, player, be); this.proximityOpen = be.getProximityOpen(); this.state = be.getAirlockState(); + this.proximityAccess = be.getProximityAccess(); } public AirlockControllerMenu(int syncId, Inventory inv, BlockPos pos) { @@ -50,5 +53,6 @@ public void registerData(@NotNull MenuData data) { super.registerData(data); data.registerByte(this.be::getProximityOpen, b -> this.proximityOpen = (byte) b); data.registerInt(() -> this.be.getAirlockState().ordinal(), b -> this.state = AirlockState.values()[b]); + data.registerInt(() -> this.be.getProximityAccess().ordinal(), i -> this.proximityAccess = ProximityAccess.values()[i]); } } \ No newline at end of file From 5813530572b7aa70edb4de50f489c8b23f32ff58 Mon Sep 17 00:00:00 2001 From: maxryan008 Date: Mon, 18 Aug 2025 10:35:54 +1000 Subject: [PATCH 09/11] Reinforced Airlocks with matching properties of obsidian. E.G., Blast resistance, hard to break etc. --- .../reinforced_air_lock_controller.json | 7 ++++ .../reinforced_air_lock_frame.json | 7 ++++ .../assets/galacticraft/lang/en_us.json | 2 ++ .../block/reinforced_air_lock_frame.json | 6 ++++ .../item/reinforced_air_lock_frame.json | 3 ++ .../reinforced_air_lock_controller.json | 12 +++++++ .../reinforced_air_lock_controller.json | 32 ++++++++++++++++++ .../reinforced_air_lock_frame.json | 32 ++++++++++++++++++ .../reinforced_air_lock_controller.json | 20 +++++++++++ .../blocks/reinforced_air_lock_frame.json | 20 +++++++++++ .../reinforced_air_lock_controller.json | 27 +++++++++++++++ .../recipe/reinforced_air_lock_frame.json | 24 +++++++++++++ .../tags/block/airlock_blocks.json | 4 ++- .../tags/block/mineable/pickaxe.json | 2 ++ .../java/dev/galacticraft/mod/Constant.java | 2 ++ .../mod/content/GCBlockEntityTypes.java | 3 +- .../galacticraft/mod/content/GCBlocks.java | 7 ++-- .../entity/AirlockControllerBlockEntity.java | 5 +-- .../mod/content/item/GCCreativeModeTabs.java | 2 ++ .../mod/data/GCBlockLootTableProvider.java | 4 ++- .../mod/data/GCTranslationProvider.java | 4 ++- .../mod/data/model/GCModelProvider.java | 31 +++++++++++------ .../mod/data/recipes/GCMachineRecipes.java | 22 ++++++++++++ .../mod/data/tag/GCBlockTagProvider.java | 8 ++++- .../block/reinforced_air_lock_controller.png | Bin 0 -> 3127 bytes .../block/reinforced_air_lock_frame.png | Bin 0 -> 439 bytes 26 files changed, 267 insertions(+), 19 deletions(-) create mode 100644 src/main/generated/assets/galacticraft/blockstates/reinforced_air_lock_controller.json create mode 100644 src/main/generated/assets/galacticraft/blockstates/reinforced_air_lock_frame.json create mode 100644 src/main/generated/assets/galacticraft/models/block/reinforced_air_lock_frame.json create mode 100644 src/main/generated/assets/galacticraft/models/item/reinforced_air_lock_frame.json create mode 100644 src/main/generated/assets/galacticraft/models/machine/reinforced_air_lock_controller.json create mode 100644 src/main/generated/data/galacticraft/advancement/recipes/decorations/reinforced_air_lock_controller.json create mode 100644 src/main/generated/data/galacticraft/advancement/recipes/decorations/reinforced_air_lock_frame.json create mode 100644 src/main/generated/data/galacticraft/loot_table/blocks/reinforced_air_lock_controller.json create mode 100644 src/main/generated/data/galacticraft/loot_table/blocks/reinforced_air_lock_frame.json create mode 100644 src/main/generated/data/galacticraft/recipe/reinforced_air_lock_controller.json create mode 100644 src/main/generated/data/galacticraft/recipe/reinforced_air_lock_frame.json create mode 100644 src/main/resources/assets/galacticraft/textures/block/reinforced_air_lock_controller.png create mode 100644 src/main/resources/assets/galacticraft/textures/block/reinforced_air_lock_frame.png diff --git a/src/main/generated/assets/galacticraft/blockstates/reinforced_air_lock_controller.json b/src/main/generated/assets/galacticraft/blockstates/reinforced_air_lock_controller.json new file mode 100644 index 0000000000..899882ef42 --- /dev/null +++ b/src/main/generated/assets/galacticraft/blockstates/reinforced_air_lock_controller.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "galacticraft:machine/reinforced_air_lock_controller" + } + } +} \ No newline at end of file diff --git a/src/main/generated/assets/galacticraft/blockstates/reinforced_air_lock_frame.json b/src/main/generated/assets/galacticraft/blockstates/reinforced_air_lock_frame.json new file mode 100644 index 0000000000..5195d6c79f --- /dev/null +++ b/src/main/generated/assets/galacticraft/blockstates/reinforced_air_lock_frame.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "galacticraft:block/reinforced_air_lock_frame" + } + } +} \ No newline at end of file diff --git a/src/main/generated/assets/galacticraft/lang/en_us.json b/src/main/generated/assets/galacticraft/lang/en_us.json index 21a2b2f48c..018b27c1b5 100644 --- a/src/main/generated/assets/galacticraft/lang/en_us.json +++ b/src/main/generated/assets/galacticraft/lang/en_us.json @@ -344,6 +344,8 @@ "block.galacticraft.red_glass_fluid_pipe": "Red Stained Glass Fluid Pipe", "block.galacticraft.refinery": "Refinery", "block.galacticraft.refinery.description": "Refinery will take an input of oil and energy, and output fuel used for rockets and vehicles.", + "block.galacticraft.reinforced_air_lock_controller": "Reinforced Airlock Controller", + "block.galacticraft.reinforced_air_lock_frame": "Reinforced Airlock Frame", "block.galacticraft.rich_olivine_basalt": "Rich Olivine Basalt", "block.galacticraft.rocket_launch_pad": "Rocket Launch Pad", "block.galacticraft.rocket_workbench": "Rocket Workbench", diff --git a/src/main/generated/assets/galacticraft/models/block/reinforced_air_lock_frame.json b/src/main/generated/assets/galacticraft/models/block/reinforced_air_lock_frame.json new file mode 100644 index 0000000000..9230b55403 --- /dev/null +++ b/src/main/generated/assets/galacticraft/models/block/reinforced_air_lock_frame.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "galacticraft:block/reinforced_air_lock_frame" + } +} \ No newline at end of file diff --git a/src/main/generated/assets/galacticraft/models/item/reinforced_air_lock_frame.json b/src/main/generated/assets/galacticraft/models/item/reinforced_air_lock_frame.json new file mode 100644 index 0000000000..68cdf2a7e3 --- /dev/null +++ b/src/main/generated/assets/galacticraft/models/item/reinforced_air_lock_frame.json @@ -0,0 +1,3 @@ +{ + "parent": "galacticraft:block/reinforced_air_lock_frame" +} \ No newline at end of file diff --git a/src/main/generated/assets/galacticraft/models/machine/reinforced_air_lock_controller.json b/src/main/generated/assets/galacticraft/models/machine/reinforced_air_lock_controller.json new file mode 100644 index 0000000000..efee5f6115 --- /dev/null +++ b/src/main/generated/assets/galacticraft/models/machine/reinforced_air_lock_controller.json @@ -0,0 +1,12 @@ +{ + "data": { + "back": "galacticraft:block/reinforced_air_lock_controller", + "bottom": "galacticraft:block/reinforced_air_lock_frame", + "front": "galacticraft:block/reinforced_air_lock_controller", + "left": "galacticraft:block/reinforced_air_lock_controller", + "particle": "galacticraft:block/reinforced_air_lock_controller", + "right": "galacticraft:block/reinforced_air_lock_controller", + "top": "galacticraft:block/reinforced_air_lock_frame" + }, + "machinelib:type": "machine" +} \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/advancement/recipes/decorations/reinforced_air_lock_controller.json b/src/main/generated/data/galacticraft/advancement/recipes/decorations/reinforced_air_lock_controller.json new file mode 100644 index 0000000000..871f5fa8f8 --- /dev/null +++ b/src/main/generated/data/galacticraft/advancement/recipes/decorations/reinforced_air_lock_controller.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_compressed_meteoric_iron": { + "conditions": { + "items": [ + { + "items": "galacticraft:compressed_meteoric_iron" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "galacticraft:reinforced_air_lock_controller" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_compressed_meteoric_iron" + ] + ], + "rewards": { + "recipes": [ + "galacticraft:reinforced_air_lock_controller" + ] + } +} \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/advancement/recipes/decorations/reinforced_air_lock_frame.json b/src/main/generated/data/galacticraft/advancement/recipes/decorations/reinforced_air_lock_frame.json new file mode 100644 index 0000000000..d1ea76ca21 --- /dev/null +++ b/src/main/generated/data/galacticraft/advancement/recipes/decorations/reinforced_air_lock_frame.json @@ -0,0 +1,32 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_oxygen_concentrator": { + "conditions": { + "items": [ + { + "items": "galacticraft:oxygen_concentrator" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "galacticraft:reinforced_air_lock_frame" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_oxygen_concentrator" + ] + ], + "rewards": { + "recipes": [ + "galacticraft:reinforced_air_lock_frame" + ] + } +} \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/loot_table/blocks/reinforced_air_lock_controller.json b/src/main/generated/data/galacticraft/loot_table/blocks/reinforced_air_lock_controller.json new file mode 100644 index 0000000000..47b28aec0a --- /dev/null +++ b/src/main/generated/data/galacticraft/loot_table/blocks/reinforced_air_lock_controller.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "galacticraft:reinforced_air_lock_controller" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/loot_table/blocks/reinforced_air_lock_frame.json b/src/main/generated/data/galacticraft/loot_table/blocks/reinforced_air_lock_frame.json new file mode 100644 index 0000000000..ed7523d930 --- /dev/null +++ b/src/main/generated/data/galacticraft/loot_table/blocks/reinforced_air_lock_frame.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "galacticraft:reinforced_air_lock_frame" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/recipe/reinforced_air_lock_controller.json b/src/main/generated/data/galacticraft/recipe/reinforced_air_lock_controller.json new file mode 100644 index 0000000000..2505799582 --- /dev/null +++ b/src/main/generated/data/galacticraft/recipe/reinforced_air_lock_controller.json @@ -0,0 +1,27 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "item": "galacticraft:compressed_meteoric_iron" + }, + "O": { + "item": "minecraft:obsidian" + }, + "S": { + "item": "galacticraft:compressed_steel" + }, + "W": { + "item": "galacticraft:basic_wafer" + } + }, + "pattern": [ + "OSO", + "MWM", + "OSO" + ], + "result": { + "count": 1, + "id": "galacticraft:reinforced_air_lock_controller" + } +} \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/recipe/reinforced_air_lock_frame.json b/src/main/generated/data/galacticraft/recipe/reinforced_air_lock_frame.json new file mode 100644 index 0000000000..86db1e19c3 --- /dev/null +++ b/src/main/generated/data/galacticraft/recipe/reinforced_air_lock_frame.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "C": { + "item": "galacticraft:oxygen_concentrator" + }, + "O": { + "item": "minecraft:obsidian" + }, + "S": { + "item": "galacticraft:compressed_steel" + } + }, + "pattern": [ + "OOO", + "SCS", + "OOO" + ], + "result": { + "count": 4, + "id": "galacticraft:reinforced_air_lock_frame" + } +} \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json b/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json index e131c7b5de..bfb722309e 100644 --- a/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json +++ b/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json @@ -1,6 +1,8 @@ { "values": [ "galacticraft:air_lock_frame", - "galacticraft:air_lock_controller" + "galacticraft:air_lock_controller", + "galacticraft:reinforced_air_lock_frame", + "galacticraft:reinforced_air_lock_controller" ] } \ No newline at end of file diff --git a/src/main/generated/data/minecraft/tags/block/mineable/pickaxe.json b/src/main/generated/data/minecraft/tags/block/mineable/pickaxe.json index e62c0564b8..a4c4c83a6a 100644 --- a/src/main/generated/data/minecraft/tags/block/mineable/pickaxe.json +++ b/src/main/generated/data/minecraft/tags/block/mineable/pickaxe.json @@ -96,7 +96,9 @@ "galacticraft:cryogenic_chamber_part", "galacticraft:solar_panel_part", "galacticraft:air_lock_frame", + "galacticraft:reinforced_air_lock_frame", "galacticraft:air_lock_controller", + "galacticraft:reinforced_air_lock_controller", "galacticraft:sealable_aluminum_wire", "galacticraft:heavy_sealable_aluminum_wire", "galacticraft:silicon_block", diff --git a/src/main/java/dev/galacticraft/mod/Constant.java b/src/main/java/dev/galacticraft/mod/Constant.java index 3de1110d00..a426d1fd68 100644 --- a/src/main/java/dev/galacticraft/mod/Constant.java +++ b/src/main/java/dev/galacticraft/mod/Constant.java @@ -326,7 +326,9 @@ interface Block { String MAGNETIC_CRAFTING_TABLE = "magnetic_crafting_table"; String ROCKET_WORKBENCH = "rocket_workbench"; String AIR_LOCK_FRAME = "air_lock_frame"; + String REINFORCED_AIR_LOCK_FRAME = "reinforced_air_lock_frame"; String AIR_LOCK_CONTROLLER = "air_lock_controller"; + String REINFORCED_AIR_LOCK_CONTROLLER = "reinforced_air_lock_controller"; String AIR_LOCK_SEAL = "air_lock_seal"; String CHROMATIC_APPLICATOR = "chromatic_applicator"; String DISPLAY_SCREEN = "display_screen"; diff --git a/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java b/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java index 885c46e8da..28470d6d22 100644 --- a/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java +++ b/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java @@ -77,7 +77,8 @@ public class GCBlockEntityTypes { public static final BlockEntityType CRYOGENIC_CHAMBER = register(Constant.Block.CRYOGENIC_CHAMBER, CryogenicChamberBlockEntity::new, GCBlocks.CRYOGENIC_CHAMBER); public static final BlockEntityType CRYOGENIC_CHAMBER_PART = register(Constant.Block.CRYOGENIC_CHAMBER_PART, CryogenicChamberPartBlockEntity::new, GCBlocks.CRYOGENIC_CHAMBER_PART); public static final BlockEntityType DUNGEON_BOSS_SPAWNER = register(Constant.Block.BOSS_SPAWNER, DungeonSpawnerBlockEntity::new, GCBlocks.BOSS_SPAWNER); - public static final BlockEntityType AIRLOCK_CONTROLLER = register(Constant.Block.AIR_LOCK_CONTROLLER, AirlockControllerBlockEntity::new, GCBlocks.AIR_LOCK_CONTROLLER); + public static final BlockEntityType AIRLOCK_CONTROLLER = register(Constant.Block.AIR_LOCK_CONTROLLER, (pos, state) -> new AirlockControllerBlockEntity(GCBlockEntityTypes.AIRLOCK_CONTROLLER, pos, state), GCBlocks.AIR_LOCK_CONTROLLER); + public static final BlockEntityType REINFORCED_AIRLOCK_CONTROLLER = register(Constant.Block.REINFORCED_AIR_LOCK_CONTROLLER, (pos, state) -> new AirlockControllerBlockEntity(GCBlockEntityTypes.REINFORCED_AIRLOCK_CONTROLLER, pos, state), GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER); // DECORATION public static final BlockEntityType CANNED_FOOD = register(Constant.Block.CANNED_FOOD, CannedFoodBlockEntity::new, GCBlocks.CANNED_FOOD); diff --git a/src/main/java/dev/galacticraft/mod/content/GCBlocks.java b/src/main/java/dev/galacticraft/mod/content/GCBlocks.java index 05c7100242..eecf575740 100644 --- a/src/main/java/dev/galacticraft/mod/content/GCBlocks.java +++ b/src/main/java/dev/galacticraft/mod/content/GCBlocks.java @@ -361,10 +361,13 @@ public class GCBlocks { public static final Block OXYGEN_STORAGE_MODULE = BLOCKS.registerWithItem(Constant.Block.OXYGEN_STORAGE_MODULE, new ResourceStorageBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GRAY).strength(3.0F, 5.0F).sound(SoundType.METAL).requiresCorrectToolForDrops(), Constant.id(Constant.Block.OXYGEN_STORAGE_MODULE))); public static final Block FOOD_CANNER = BLOCKS.registerWithItem(Constant.Block.FOOD_CANNER, new FoodCannerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).strength(3.0F, 5.0F).sound(SoundType.METAL).mapColor(MapColor.COLOR_GRAY).requiresCorrectToolForDrops())); - public static final Block AIR_LOCK_FRAME = BLOCKS.registerWithItem(Constant.Block.AIR_LOCK_FRAME, new Block(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK).mapColor(MapColor.COLOR_GRAY))); - public static final Block AIR_LOCK_CONTROLLER = BLOCKS.registerWithItem(Constant.Block.AIR_LOCK_CONTROLLER, new SimpleMachineBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GRAY).strength(3.0F, 5.0F).sound(SoundType.METAL).requiresCorrectToolForDrops(), Constant.id(Constant.Block.AIR_LOCK_CONTROLLER))); + public static final Block AIR_LOCK_FRAME = BLOCKS.registerWithItem(Constant.Block.AIR_LOCK_FRAME, new Block(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK))); + public static final Block AIR_LOCK_CONTROLLER = BLOCKS.registerWithItem(Constant.Block.AIR_LOCK_CONTROLLER, new SimpleMachineBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK), Constant.id(Constant.Block.AIR_LOCK_CONTROLLER))); public static final Block AIR_LOCK_SEAL = BLOCKS.register(Constant.Block.AIR_LOCK_SEAL, new AirlockSealBlock(BlockBehaviour.Properties.ofFullCopy(AIR_LOCK_FRAME).strength(-1.0f, 3600000.0f).noLootTable().isValidSpawn(GCBlocks::never))); + public static final Block REINFORCED_AIR_LOCK_FRAME = BLOCKS.registerWithItem(Constant.Block.REINFORCED_AIR_LOCK_FRAME, new Block(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK).strength(50.0F, 1200.0F))); + public static final Block REINFORCED_AIR_LOCK_CONTROLLER = BLOCKS.registerWithItem(Constant.Block.REINFORCED_AIR_LOCK_CONTROLLER, new SimpleMachineBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK).strength(50.0F, 1200.0F), Constant.id(Constant.Block.REINFORCED_AIR_LOCK_CONTROLLER))); + // TORCHES public static final Block GLOWSTONE_TORCH = BLOCKS.register(Constant.Block.GLOWSTONE_TORCH, new GlowstoneTorchBlock(BlockBehaviour.Properties.of().noCollission().instabreak().lightLevel(blockStatex -> 14).sound(SoundType.WOOD).pushReaction(PushReaction.DESTROY))); public static final Block UNLIT_TORCH = BLOCKS.register(Constant.Block.UNLIT_TORCH, new UnlitTorchBlock(BlockBehaviour.Properties.of().noCollission().instabreak().lightLevel(blockStatex -> 0).sound(SoundType.WOOD).pushReaction(PushReaction.DESTROY))); diff --git a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java index a99e4d08ca..87d0f02ffa 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java +++ b/src/main/java/dev/galacticraft/mod/content/block/entity/AirlockControllerBlockEntity.java @@ -45,6 +45,7 @@ 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.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import org.jetbrains.annotations.NotNull; @@ -83,8 +84,8 @@ public void setProximityAccess(ProximityAccess access) { private AirlockState state = AirlockState.NONE; private int ticks = 0; - public AirlockControllerBlockEntity(BlockPos pos, BlockState state) { - super(GCBlockEntityTypes.AIRLOCK_CONTROLLER, pos, state, SPEC); + public AirlockControllerBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { + super((BlockEntityType) type, pos, state, SPEC); } public byte getProximityOpen() { return this.proximityOpen; } diff --git a/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java b/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java index 3f964c28ab..9a8948659e 100644 --- a/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java +++ b/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java @@ -277,6 +277,8 @@ public class GCCreativeModeTabs { output.accept(FOOD_CANNER); output.accept(AIR_LOCK_FRAME); output.accept(AIR_LOCK_CONTROLLER); + output.accept(REINFORCED_AIR_LOCK_FRAME); + output.accept(REINFORCED_AIR_LOCK_CONTROLLER); }).build(); public static final CreativeModeTab ITEMS_GROUP = FabricItemGroup diff --git a/src/main/java/dev/galacticraft/mod/data/GCBlockLootTableProvider.java b/src/main/java/dev/galacticraft/mod/data/GCBlockLootTableProvider.java index b58d60c00b..243bca50e3 100644 --- a/src/main/java/dev/galacticraft/mod/data/GCBlockLootTableProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/GCBlockLootTableProvider.java @@ -282,8 +282,10 @@ public void generate() { this.add(GCBlocks.FUELING_PAD, this::createLaunchPadTable); this.add(GCBlocks.ROCKET_LAUNCH_PAD, this::createLaunchPadTable); - this.dropSelf(GCBlocks.AIR_LOCK_CONTROLLER); this.dropSelf(GCBlocks.AIR_LOCK_FRAME); + this.dropSelf(GCBlocks.AIR_LOCK_CONTROLLER); + this.dropSelf(GCBlocks.REINFORCED_AIR_LOCK_FRAME); + this.dropSelf(GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER); this.dropSelf(GCBlocks.CRYOGENIC_CHAMBER); this.dropSelf(GCBlocks.CIRCUIT_FABRICATOR); this.dropSelf(GCBlocks.COMPRESSOR); diff --git a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java index dbfc666437..eec1a8a5cb 100644 --- a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java @@ -374,8 +374,10 @@ protected void generateBlockTranslations() { this.block(GCBlocks.OXYGEN_STORAGE_MODULE, "Oxygen Storage Module"); this.block(GCBlocks.FUEL_LOADER, "Fuel Loader"); - this.block(GCBlocks.AIR_LOCK_CONTROLLER, "Airlock Controller"); this.block(GCBlocks.AIR_LOCK_FRAME, "Airlock Frame"); + this.block(GCBlocks.AIR_LOCK_CONTROLLER, "Airlock Controller"); + this.block(GCBlocks.REINFORCED_AIR_LOCK_FRAME, "Reinforced Airlock Frame"); + this.block(GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER, "Reinforced Airlock Controller"); this.block(GCBlocks.AIR_LOCK_SEAL, "Airlock Seal"); } diff --git a/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java b/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java index 414bfe05af..cd73ced8e0 100644 --- a/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java @@ -358,7 +358,8 @@ public void generateBlockStateModels(BlockModelGenerators generator) { generator.createNonTemplateModelBlock(GCBlocks.SULFURIC_ACID); generator.createTrivialCube(GCBlocks.AIR_LOCK_FRAME); - this.createAirLockController(generator); + generator.createTrivialCube(GCBlocks.REINFORCED_AIR_LOCK_FRAME); + this.createAirLockControllers(generator); this.createParachests(generator); } @@ -470,15 +471,25 @@ private void createVaporSpout(BlockModelGenerators generator) { generator.createTrivialBlock(GCBlocks.VAPOR_SPOUT, textureMapping, ModelTemplates.CUBE_TOP); } - private void createAirLockController(BlockModelGenerators generator) { - var block = GCBlocks.AIR_LOCK_CONTROLLER; - ResourceLocation controller = TextureMapping.getBlockTexture(block); - ResourceLocation frame = TextureMapping.getBlockTexture(GCBlocks.AIR_LOCK_FRAME); - MachineModelGenerator.createTrivialMachine(generator, block, TextureProvider.builder(Constant.MOD_ID) - .sides(controller) - .top(frame) - .bottom(frame) - .particle(controller) + private void createAirLockControllers(BlockModelGenerators generator) { + var basic = GCBlocks.AIR_LOCK_CONTROLLER; + var reinforced = GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER; + ResourceLocation basicController = TextureMapping.getBlockTexture(basic); + ResourceLocation reinforcedController = TextureMapping.getBlockTexture(reinforced); + ResourceLocation basicFrame = TextureMapping.getBlockTexture(GCBlocks.AIR_LOCK_FRAME); + ResourceLocation reinforcedFrame = TextureMapping.getBlockTexture(GCBlocks.REINFORCED_AIR_LOCK_FRAME); + MachineModelGenerator.createTrivialMachine(generator, basic, TextureProvider.builder(Constant.MOD_ID) + .sides(basicController) + .top(basicFrame) + .bottom(basicFrame) + .particle(basicController) + .build() + ); + MachineModelGenerator.createTrivialMachine(generator, reinforced, TextureProvider.builder(Constant.MOD_ID) + .sides(reinforcedController) + .top(reinforcedFrame) + .bottom(reinforcedFrame) + .particle(reinforcedController) .build() ); } diff --git a/src/main/java/dev/galacticraft/mod/data/recipes/GCMachineRecipes.java b/src/main/java/dev/galacticraft/mod/data/recipes/GCMachineRecipes.java index 9911de1b07..c6c5f66cd0 100644 --- a/src/main/java/dev/galacticraft/mod/data/recipes/GCMachineRecipes.java +++ b/src/main/java/dev/galacticraft/mod/data/recipes/GCMachineRecipes.java @@ -40,6 +40,7 @@ import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Items; import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Blocks; import org.jetbrains.annotations.NotNull; import java.util.concurrent.CompletableFuture; @@ -275,6 +276,16 @@ public void buildRecipes(RecipeOutput output) { .unlockedBy(getHasName(GCItems.OXYGEN_CONCENTRATOR), has(GCItems.OXYGEN_CONCENTRATOR)) .save(output); + ShapedRecipeBuilder.shaped(RecipeCategory.DECORATIONS, GCBlocks.REINFORCED_AIR_LOCK_FRAME, 4) + .define('O', Items.OBSIDIAN) + .define('S', GCItems.COMPRESSED_STEEL) + .define('C', GCItems.OXYGEN_CONCENTRATOR) + .pattern("OOO") + .pattern("SCS") + .pattern("OOO") + .unlockedBy(getHasName(GCItems.OXYGEN_CONCENTRATOR), has(GCItems.OXYGEN_CONCENTRATOR)) + .save(output); + ShapedRecipeBuilder.shaped(RecipeCategory.DECORATIONS, GCBlocks.AIR_LOCK_CONTROLLER) .define('S', GCItems.COMPRESSED_STEEL) .define('M', GCItems.COMPRESSED_METEORIC_IRON) @@ -285,6 +296,17 @@ public void buildRecipes(RecipeOutput output) { .unlockedBy(getHasName(GCItems.COMPRESSED_METEORIC_IRON), has(GCItems.COMPRESSED_METEORIC_IRON)) .save(output); + ShapedRecipeBuilder.shaped(RecipeCategory.DECORATIONS, GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER) + .define('S', GCItems.COMPRESSED_STEEL) + .define('M', GCItems.COMPRESSED_METEORIC_IRON) + .define('W', GCItems.BASIC_WAFER) + .define('O', Items.OBSIDIAN) + .pattern("OSO") + .pattern("MWM") + .pattern("OSO") + .unlockedBy(getHasName(GCItems.COMPRESSED_METEORIC_IRON), has(GCItems.COMPRESSED_METEORIC_IRON)) + .save(output); + ShapedRecipeBuilder.shaped(RecipeCategory.DECORATIONS, GCBlocks.CRYOGENIC_CHAMBER) .define('D', GCItems.COMPRESSED_DESH) .define('P', GCItems.TIER_2_HEAVY_DUTY_PLATE) diff --git a/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java b/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java index 01ab7a9ffa..d5c162c9e0 100644 --- a/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java @@ -398,7 +398,11 @@ protected void addTags(HolderLookup.Provider provider) { this.tag(GCBlockTags.STAINED_GLASS_FLUID_PIPES).add(pipe); } - this.tag(GCBlockTags.AIRLOCK_BLOCKS).add(GCBlocks.AIR_LOCK_FRAME, GCBlocks.AIR_LOCK_CONTROLLER); + this.tag(GCBlockTags.AIRLOCK_BLOCKS) + .add(GCBlocks.AIR_LOCK_FRAME) + .add(GCBlocks.AIR_LOCK_CONTROLLER) + .add(GCBlocks.REINFORCED_AIR_LOCK_FRAME) + .add(GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER); this.tag(ConventionalBlockTags.VILLAGER_JOB_SITES) .add(GCBlocks.LUNAR_CARTOGRAPHY_TABLE); @@ -583,7 +587,9 @@ protected void addTags(HolderLookup.Provider provider) { GCBlocks.CRYOGENIC_CHAMBER_PART, GCBlocks.SOLAR_PANEL_PART, GCBlocks.AIR_LOCK_FRAME, + GCBlocks.REINFORCED_AIR_LOCK_FRAME, GCBlocks.AIR_LOCK_CONTROLLER, + GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER, GCBlocks.SEALABLE_ALUMINUM_WIRE, GCBlocks.HEAVY_SEALABLE_ALUMINUM_WIRE, diff --git a/src/main/resources/assets/galacticraft/textures/block/reinforced_air_lock_controller.png b/src/main/resources/assets/galacticraft/textures/block/reinforced_air_lock_controller.png new file mode 100644 index 0000000000000000000000000000000000000000..69b3a0248053c526d9a968c3c4e8fff3783d326a GIT binary patch literal 3127 zcmV-749N3|P)uJ@VVD_U zC<6{NG_fI~0ue<-1QkJoA_k0xBC#Thg@9ne9*`iQ#9$OrQF$}6R&?d%y_c8YA7_1Q zpS|}zXYYO1x&V;8{kgn!SPFnNo`4_X z6{c}T{8k*B#$jdxfFg<9uYy1K45IaYvHg`_dOZM)Sy63ve6hvv1)yUy0P^?0*fb9UASvow z`@mQCp^4`uNg&9uGcn1|&Nk+9SjOUl{-OWr@Hh0;_l(8q{wNRKos+;6rV8ldy0Owz z(}jF`W(JeRp&R{qi2rfmU!TJ;gp(Kmm5I1s5m_f-n#TRsj}B0%?E` zvOzxB2#P=n*a3EfYETOrKoe*ICqM@{4K9Go;5xVgZi5G41dM~{UdP z6d+Yd3o?MrAqM0Kc|iV92owdyL5UC#5<>aVCa44|hpM4Es0sQWIt5*Tu0n&*J!lk~ zf_{hI!w5`*sjxDv4V%CW*ah~3!{C*0BD@;TgA3v9a1~q+AA{TB3-ERLHar49hi4Ih z5D^-ph8Q6X#0?2VqLBoIkE}zAkxHZUgRb+f=natP#6>iMMoK->`~sRLq)(kHo*Vn{;LcG6+e zdD1=7D>9j^O?D{Qg|tCDK{ym)H7&wDr6*;uGTJg8GHjVb znL{!cWyUB7MT6o-VNo_w8Yq`2<5Ub)hw4L3rj}5@qxMs0WMyP6Wy582WNT#4$d1qu znl{acmP#w5ouJ*Jy_Zv#bCKi7ZIf$}8dZdVy&)LYdbX%I9R8VMQ|8r>Q*nyQ)sn)#Z|n)kKvS`4iutvy=3T65Yu+7a4Yv^%sX zb>ww?bn(=Yu(!=O6^iuTp>)p_Y^{w=i^lS773}6Fm1Fpe-gF!>I zp{*g$u-szvGhed; zvo5pW&GpS$<~8QGEXWp~7V9lKEnZq0SaK{6Sl+dwSOr*ZvFf(^Xl-N7w{EeXveC4O zv)N}e%%C!Y7^RFWwrE>d+x51mZQt2h+X?JW*!^a2WS?Sx)P8cQ&Qi|OhNWW;>JChY zI)@QQx?`Nj^#uJBl~d&PK+RZLOLos~K(b5>qmrMN0})tOkySZ3_WICNY@+|jrX%s^&6b2i>5 zeqa0y%Z;^%^_=a@u3%4b9605ii3Ep)@`TAmhs0fpQ%O!ql}XcFH*PieWwLj2ZSq`7 zV9Mc?h17`D)-+sNT-qs~3@?S(ldh7UlRlVXkWrK|vf6I-?$tAVKYn8-l({mqQ$Q8{ zO!WzMg`0(=S&msXS#Pt$vrpzo=kRj+a`kh!z=6$;cwT88(J6|n-WB%w`m$h~4 zpmp)YIh_3ETV2tjiAU!0h1dxU-n=E9e!)6|Z;4?!H=SSy{V>ut&IOq{_dlbFb#!9eY1iCsp6Bajj|H zr?hX|zPbJE{X++w546-O*Ot`2Kgd0Jx6Z4syTu9enWavU5N9)I?I-1m1* z_?_rJ$vD~agVqoG+9++s?NEDe`%Fht$4F;X=in*dQ{7$mU2Q)a|9JSc+Uc4zvS-T9 z63!N$T{xF_ZuWe}`RNOZ7sk3{yB}PPym+f8xTpV;-=!;;JuhGEb?H5K#o@~7t9DmU zU1MD9xNd#Dz0azz?I)|B+WM{g+Xrk0I&awC=o(x)cy`EX=)z6+o0o6-+`4{y+3mqQ z%kSJBju{@g%f35#FZJHb`&swrA8dGtepviS>QUumrN{L@>;2q1Vm)$Z)P1z?N$8UY zW2~{~zhwUMVZ87u`Dx{Z>O|9|`Q+&->FRy-Sjp7DHsy69KwU-!MxeeuI@&cF4| zM9z%A6NqE3F+vAZljxcz5o-@4lJz zeShHO#1zq@&+g@D(e++>rU1s6+SD`+)>;5kN`w$lN&z4uhzQ<$a?XShxGF$n4DSmT_-F4nYdi`E*uSl6fbo|IBm zd{v~y7|~iYEO(6K$gtcUXAon3AC^0E&edK_k!eiv!fTP5QmSm@I9ApWLhYIKeSa{< zRJk!ePg$QN5nqxh$9vCyzdtJIx~>8WtM~o_BqBKH&Mg477~|2-o4dE%T$?lS<5OVy z^GCHKB52V}PnQO$;K!$MR=>G6ynFwV)$d=32&>f!cwVG&9IJ`~em#Fx!QVfB0^q~f zZ)`Rja?UhO!^7}!oI#nWh+OzC;JYvPID5-%Hmf$Y=$BPKji&fNWp?(qel*5d$tHc@ zAG)pssH{tGU_ R6mtLo002ovPDHLkV1oTQ5Lo~K literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/galacticraft/textures/block/reinforced_air_lock_frame.png b/src/main/resources/assets/galacticraft/textures/block/reinforced_air_lock_frame.png new file mode 100644 index 0000000000000000000000000000000000000000..aeeafa0a29f05030cb5e72cbe6fb8c1107c346df GIT binary patch literal 439 zcmV;o0Z9IdP)Px$aY;l$R49>MQlXOLFbtKqNq|&fU>kII#w8`4{{N?h?iRFQZBPRVtOE|bobP50 zGK%yhJw3_YZa4b-)Kpl84MSL9BQz0_$PP*=g%F6y%t|TnGcz;ST2)n5CqQG2F-9UP zrQn-$77b)O6-am*a=Nw~ft({IM5fKsZ{XEal=d-nz zbN1dl=lV~d)bxBlgE8n1(Q2(29lxM7VbDPr?4e@_VW8m@mT7DX4^T53;xIG(>$>iX zgN~!`C_MH6l~PKdU!Mm#iX#U!)ELP`v@FX!&)c?LE|=cBsZEgRe!q{BJKg_g4I!v1 zxJ|-hX4mU=T~}4brZN3U02H1>N(tL>&Ux>dd0Cdn Date: Wed, 20 Aug 2025 12:04:14 +1000 Subject: [PATCH 10/11] Prototype of Bubble Airlock --- build.gradle.kts | 6 + .../bubble_air_lock_controller.json | 7 + .../assets/galacticraft/lang/en_us.json | 1 + .../machine/bubble_air_lock_controller.json | 12 + .../blocks/bubble_air_lock_controller.json | 20 ++ .../tags/block/airlock_blocks.json | 3 +- .../galacticraft/tags/block/machines.json | 3 +- .../java/dev/galacticraft/mod/Constant.java | 2 + .../galacticraft/mod/GalacticraftClient.java | 1 + .../ingame/BubbleAirlockControllerScreen.java | 142 ++++++++ .../mod/content/GCBlockEntityTypes.java | 1 + .../galacticraft/mod/content/GCBlocks.java | 2 + .../BubbleAirlockControllerBlockEntity.java | 333 ++++++++++++++++++ .../machine/airlock/AirlockFrameScanner.java | 55 +-- .../mod/content/item/GCCreativeModeTabs.java | 1 + .../mod/data/GCBlockLootTableProvider.java | 1 + .../mod/data/GCTranslationProvider.java | 1 + .../mod/data/model/GCModelProvider.java | 9 + .../mod/data/tag/GCBlockTagProvider.java | 6 +- .../mod/lookup/GCApiLookupProviders.java | 3 +- .../screen/BubbleAirlockControllerMenu.java | 32 ++ .../galacticraft/mod/screen/GCMenuTypes.java | 2 + .../assets/galacticraft/atlases/blocks.json | 8 + .../galacticraft/textures/block/white.png | Bin 0 -> 84 bytes 24 files changed, 610 insertions(+), 41 deletions(-) create mode 100644 src/main/generated/assets/galacticraft/blockstates/bubble_air_lock_controller.json create mode 100644 src/main/generated/assets/galacticraft/models/machine/bubble_air_lock_controller.json create mode 100644 src/main/generated/data/galacticraft/loot_table/blocks/bubble_air_lock_controller.json create mode 100644 src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/BubbleAirlockControllerScreen.java create mode 100644 src/main/java/dev/galacticraft/mod/content/block/entity/BubbleAirlockControllerBlockEntity.java create mode 100644 src/main/java/dev/galacticraft/mod/screen/BubbleAirlockControllerMenu.java create mode 100644 src/main/resources/assets/galacticraft/atlases/blocks.json create mode 100644 src/main/resources/assets/galacticraft/textures/block/white.png diff --git a/build.gradle.kts b/build.gradle.kts index 4908404ff3..91229eca53 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -202,6 +202,11 @@ repositories { includeGroup("squeek.appleskin") } } + maven("https://maven.teamgalacticraft.org/") { + content { + includeGroup("dev.maximus") + } + } } configurations { @@ -234,6 +239,7 @@ dependencies { "core"("dev.galacticraft:dynamicdimensions-fabric:$dynamicdimensionsVersion") "core"("dev.galacticraft:MachineLib:$machineLibVersion") + "core"("dev.maximus:glasswork:1.3") "core"("lol.bai:badpackets:fabric-$badpacketsVersion") // Optional Dependencies diff --git a/src/main/generated/assets/galacticraft/blockstates/bubble_air_lock_controller.json b/src/main/generated/assets/galacticraft/blockstates/bubble_air_lock_controller.json new file mode 100644 index 0000000000..ba7b303a0a --- /dev/null +++ b/src/main/generated/assets/galacticraft/blockstates/bubble_air_lock_controller.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "galacticraft:machine/bubble_air_lock_controller" + } + } +} \ No newline at end of file diff --git a/src/main/generated/assets/galacticraft/lang/en_us.json b/src/main/generated/assets/galacticraft/lang/en_us.json index 018b27c1b5..ea856f2639 100644 --- a/src/main/generated/assets/galacticraft/lang/en_us.json +++ b/src/main/generated/assets/galacticraft/lang/en_us.json @@ -122,6 +122,7 @@ "block.galacticraft.bronze_decoration_wall": "Bronze Decoration Wall", "block.galacticraft.brown_candle_moon_cheese_wheel": "Moon Cheese Wheel with Brown Candle", "block.galacticraft.brown_glass_fluid_pipe": "Brown Stained Glass Fluid Pipe", + "block.galacticraft.bubble_air_lock_controller": "Bubble Airlock Controller", "block.galacticraft.candle_moon_cheese_wheel": "Moon Cheese Wheel with Candle", "block.galacticraft.canned_food": "Canned Food", "block.galacticraft.cavernous_vines": "Cavernous Vines", diff --git a/src/main/generated/assets/galacticraft/models/machine/bubble_air_lock_controller.json b/src/main/generated/assets/galacticraft/models/machine/bubble_air_lock_controller.json new file mode 100644 index 0000000000..65c5a5247d --- /dev/null +++ b/src/main/generated/assets/galacticraft/models/machine/bubble_air_lock_controller.json @@ -0,0 +1,12 @@ +{ + "data": { + "back": "galacticraft:block/air_lock_controller", + "bottom": "galacticraft:block/air_lock_frame", + "front": "galacticraft:block/air_lock_controller", + "left": "galacticraft:block/air_lock_controller", + "particle": "galacticraft:block/air_lock_controller", + "right": "galacticraft:block/air_lock_controller", + "top": "galacticraft:block/air_lock_frame" + }, + "machinelib:type": "machine" +} \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/loot_table/blocks/bubble_air_lock_controller.json b/src/main/generated/data/galacticraft/loot_table/blocks/bubble_air_lock_controller.json new file mode 100644 index 0000000000..c1766f91e4 --- /dev/null +++ b/src/main/generated/data/galacticraft/loot_table/blocks/bubble_air_lock_controller.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "galacticraft:bubble_air_lock_controller" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json b/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json index bfb722309e..e60aa16787 100644 --- a/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json +++ b/src/main/generated/data/galacticraft/tags/block/airlock_blocks.json @@ -3,6 +3,7 @@ "galacticraft:air_lock_frame", "galacticraft:air_lock_controller", "galacticraft:reinforced_air_lock_frame", - "galacticraft:reinforced_air_lock_controller" + "galacticraft:reinforced_air_lock_controller", + "galacticraft:bubble_air_lock_controller" ] } \ No newline at end of file diff --git a/src/main/generated/data/galacticraft/tags/block/machines.json b/src/main/generated/data/galacticraft/tags/block/machines.json index a34fe0d22c..803921bf34 100644 --- a/src/main/generated/data/galacticraft/tags/block/machines.json +++ b/src/main/generated/data/galacticraft/tags/block/machines.json @@ -18,6 +18,7 @@ "galacticraft:oxygen_compressor", "galacticraft:oxygen_storage_module", "galacticraft:food_canner", - "galacticraft:rocket_workbench" + "galacticraft:rocket_workbench", + "galacticraft:bubble_air_lock_controller" ] } \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/Constant.java b/src/main/java/dev/galacticraft/mod/Constant.java index a426d1fd68..b740eb1e3b 100644 --- a/src/main/java/dev/galacticraft/mod/Constant.java +++ b/src/main/java/dev/galacticraft/mod/Constant.java @@ -329,6 +329,7 @@ interface Block { String REINFORCED_AIR_LOCK_FRAME = "reinforced_air_lock_frame"; String AIR_LOCK_CONTROLLER = "air_lock_controller"; String REINFORCED_AIR_LOCK_CONTROLLER = "reinforced_air_lock_controller"; + String BUBBLE_AIR_LOCK_CONTROLLER = "bubble_air_lock_controller"; String AIR_LOCK_SEAL = "air_lock_seal"; String CHROMATIC_APPLICATOR = "chromatic_applicator"; String DISPLAY_SCREEN = "display_screen"; @@ -965,6 +966,7 @@ interface Menu { String OXYGEN_SEALER_MENU = "oxygen_sealer_menu"; String FUEL_LOADER_MENU = "fuel_loader_menu"; String AIR_LOCK_CONTROLLER_MENU = "air_lock_menu"; + String BUBBLE_AIR_LOCK_CONTROLLER_MENU = "bubble_air_lock_menu"; String ROCKET_WORKBENCH_MENU = "rocket_workbench_menu"; String ROCKET = "rocket"; String PARACHEST = "parachest"; diff --git a/src/main/java/dev/galacticraft/mod/GalacticraftClient.java b/src/main/java/dev/galacticraft/mod/GalacticraftClient.java index 675e2c4432..9201557f3c 100644 --- a/src/main/java/dev/galacticraft/mod/GalacticraftClient.java +++ b/src/main/java/dev/galacticraft/mod/GalacticraftClient.java @@ -119,6 +119,7 @@ public void onInitializeClient() { MenuScreens.register(GCMenuTypes.OXYGEN_SEALER, OxygenSealerScreen::new); MenuScreens.register(GCMenuTypes.FUEL_LOADER, FuelLoaderScreen::new); MenuScreens.register(GCMenuTypes.AIRLOCK_CONTROLLER_MENU, AirlockControllerScreen::new); + MenuScreens.register(GCMenuTypes.BUBBLE_AIR_LOCK_CONTROLLER_MENU, BubbleAirlockControllerScreen::new); MenuScreens.register(GCMenuTypes.ROCKET_WORKBENCH, RocketWorkbenchScreen::new); MenuScreens.register(GCMenuTypes.ROCKET, RocketInventoryScreen::new); MenuScreens.register(GCMenuTypes.PARACHEST, ParachestScreen::new); diff --git a/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/BubbleAirlockControllerScreen.java b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/BubbleAirlockControllerScreen.java new file mode 100644 index 0000000000..9323493cfd --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/client/gui/screen/ingame/BubbleAirlockControllerScreen.java @@ -0,0 +1,142 @@ +package dev.galacticraft.mod.client.gui.screen.ingame; + +import dev.galacticraft.machinelib.client.api.screen.MachineScreen; +import dev.galacticraft.mod.Constant; +import dev.galacticraft.mod.content.AirlockState; +import dev.galacticraft.mod.content.ProximityAccess; +import dev.galacticraft.mod.content.block.entity.BubbleAirlockControllerBlockEntity; +import dev.galacticraft.mod.network.c2s.AirlockSetProximityAccessPayload; +import dev.galacticraft.mod.screen.BubbleAirlockControllerMenu; +import dev.galacticraft.mod.util.Translations; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractButton; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.narration.NarrationElementOutput; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import org.jetbrains.annotations.NotNull; + +public class BubbleAirlockControllerScreen extends MachineScreen { + private IconButton publicBtn, teamBtn, privateBtn; + + private static final ResourceLocation MACHINELIB_PANELS = ResourceLocation.fromNamespaceAndPath("machinelib", "textures/gui/machine_panels.png"); + private static final int TEX_W = 256, TEX_H = 256; + + private static final int BTN_U = 0; + private static final int BTN_V_NORMAL = 196; + private static final int BTN_V_HOVER = 216; + private static final int BTN_V_SELECTED = 236; + private static final int BTN_W = 20, BTN_H = 20; + + private static final int PUB_U = 208, PUB_V = 49, PUB_W = 15, PUB_H = 15; + private static final int TEAM_U = 210, TEAM_V = 71, TEAM_W = 12, TEAM_H = 14; + private static final int PRIV_U = 231, PRIV_V = 49, PRIV_W = 10, PRIV_H = 14; + + private static final int ACCESS_BTN_SIZE = 20; + private static final int ACCESS_BTN_GAP = 6; + + private static class IconButton extends AbstractButton { + @FunctionalInterface interface PressHandler { void onPress(IconButton b); } + private final ResourceLocation texture; + private final int iconU, iconV, iconW, iconH; + private final PressHandler handler; + private boolean selected; + + IconButton(int x, int y, int size, ResourceLocation texture, int iconU, int iconV, int iconW, int iconH, PressHandler handler, Component tooltipText) { + super(x, y, size, size, Component.empty()); + this.texture = texture; this.iconU = iconU; this.iconV = iconV; this.iconW = iconW; this.iconH = iconH; + this.handler = handler; + if (!tooltipText.getString().isEmpty()) this.setTooltip(Tooltip.create(tooltipText)); + } + void setSelected(boolean sel) { this.selected = sel; } + @Override public void onPress() { handler.onPress(this); } + @Override protected void renderWidget(GuiGraphics g, int mouseX, int mouseY, float delta) { + final int v = this.selected ? BTN_V_SELECTED : (this.isHovered() ? BTN_V_HOVER : BTN_V_NORMAL); + g.blit(texture, getX(), getY(), BTN_U, v, BTN_W, BTN_H, TEX_W, TEX_H); + int ix = getX() + (this.width - iconW) / 2 + 1; + int iy = getY() + (this.height - iconH) / 2; + g.blit(texture, ix, iy, iconU, iconV, iconW, iconH, TEX_W, TEX_H); + } + @Override protected void updateWidgetNarration(NarrationElementOutput out) {} + } + + public BubbleAirlockControllerScreen(BubbleAirlockControllerMenu menu, Inventory inv, Component title) { + super(menu, title, Constant.ScreenTexture.AIRLOCK_CONTROLLER_SCREEN); + } + + @Override protected void init() { + super.init(); + this.imageHeight = 171; + this.titleLabelX = 90; + + ProximityAccess initial = this.menu.access != null ? this.menu.access : ProximityAccess.PUBLIC; + + this.publicBtn = new IconButton(0, 0, ACCESS_BTN_SIZE, MACHINELIB_PANELS, PUB_U, PUB_V, PUB_W, PUB_H, b -> { + this.menu.access = ProximityAccess.PUBLIC; + ClientPlayNetworking.send(new AirlockSetProximityAccessPayload(ProximityAccess.PUBLIC)); + updateSelection(ProximityAccess.PUBLIC); + }, Component.literal("Public")); + + this.teamBtn = new IconButton(0, 0, ACCESS_BTN_SIZE, MACHINELIB_PANELS, TEAM_U, TEAM_V, TEAM_W, TEAM_H, b -> { + this.menu.access = ProximityAccess.TEAM; + ClientPlayNetworking.send(new AirlockSetProximityAccessPayload(ProximityAccess.TEAM)); + updateSelection(ProximityAccess.TEAM); + }, Component.literal("Team")); + + this.privateBtn = new IconButton(0, 0, ACCESS_BTN_SIZE, MACHINELIB_PANELS, PRIV_U, PRIV_V, PRIV_W, PRIV_H, b -> { + this.menu.access = ProximityAccess.PRIVATE; + ClientPlayNetworking.send(new AirlockSetProximityAccessPayload(ProximityAccess.PRIVATE)); + updateSelection(ProximityAccess.PRIVATE); + }, Component.literal("Private")); + + this.addRenderableWidget(publicBtn); + this.addRenderableWidget(teamBtn); + this.addRenderableWidget(privateBtn); + updateSelection(initial); + layoutAccessButtons(); + } + + private void updateSelection(ProximityAccess a) { + publicBtn.setSelected(a == ProximityAccess.PUBLIC); + teamBtn.setSelected(a == ProximityAccess.TEAM); + privateBtn.setSelected(a == ProximityAccess.PRIVATE); + } + + @Override protected void repositionElements() { + super.repositionElements(); + layoutAccessButtons(); + } + + private void layoutAccessButtons() { + int centerX = this.leftPos + (this.imageWidth / 2); + int centerY = this.topPos + 40; + int total = ACCESS_BTN_SIZE * 3 + ACCESS_BTN_GAP * 2; + int startX = centerX - total / 2; + int y = centerY - ACCESS_BTN_SIZE / 2; + this.publicBtn.setPosition(startX, y); + this.teamBtn.setPosition(startX + ACCESS_BTN_SIZE + ACCESS_BTN_GAP, y); + this.privateBtn.setPosition(startX + (ACCESS_BTN_SIZE + ACCESS_BTN_GAP) * 2, y); + } + + @Override + protected void renderMachineBackground(GuiGraphics g, int mouseX, int mouseY, float delta) { + AirlockState enabled = this.menu.state; + Component label; int color; + if (enabled.equals(AirlockState.ALL)) { label = Component.translatable(Translations.Ui.AIRLOCK_ENABLED); color = ChatFormatting.DARK_GREEN.getColor(); } + else if (enabled.equals(AirlockState.PARTIAL)) { label = Component.translatable(Translations.Ui.AIRLOCK_PARTIAL); color = ChatFormatting.DARK_PURPLE.getColor(); } + else { label = Component.translatable(Translations.Ui.AIRLOCK_DISABLED); color = ChatFormatting.RED.getColor(); } + drawCenteredString(g, this.font, label, this.leftPos + 90, this.topPos + 18, color, false); + } + + private void drawCenteredString(GuiGraphics g, Font font, Component text, int centerX, int y, int color, boolean shadow) { + g.drawString(font, text, centerX - font.width(text) / 2, y, color, shadow); + } + + @Override protected void drawTitle(@NotNull GuiGraphics graphics) { + drawCenteredString(graphics, this.font, this.title, this.titleLabelX, this.titleLabelY, 0xFF404040, false); + } +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java b/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java index 28470d6d22..c18cf033e8 100644 --- a/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java +++ b/src/main/java/dev/galacticraft/mod/content/GCBlockEntityTypes.java @@ -79,6 +79,7 @@ public class GCBlockEntityTypes { public static final BlockEntityType DUNGEON_BOSS_SPAWNER = register(Constant.Block.BOSS_SPAWNER, DungeonSpawnerBlockEntity::new, GCBlocks.BOSS_SPAWNER); public static final BlockEntityType AIRLOCK_CONTROLLER = register(Constant.Block.AIR_LOCK_CONTROLLER, (pos, state) -> new AirlockControllerBlockEntity(GCBlockEntityTypes.AIRLOCK_CONTROLLER, pos, state), GCBlocks.AIR_LOCK_CONTROLLER); public static final BlockEntityType REINFORCED_AIRLOCK_CONTROLLER = register(Constant.Block.REINFORCED_AIR_LOCK_CONTROLLER, (pos, state) -> new AirlockControllerBlockEntity(GCBlockEntityTypes.REINFORCED_AIRLOCK_CONTROLLER, pos, state), GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER); + public static final BlockEntityType BUBBLE_AIRLOCK_CONTROLLER = register(Constant.Block.BUBBLE_AIR_LOCK_CONTROLLER, BubbleAirlockControllerBlockEntity::new, GCBlocks.BUBBLE_AIR_LOCK_CONTROLLER); // DECORATION public static final BlockEntityType CANNED_FOOD = register(Constant.Block.CANNED_FOOD, CannedFoodBlockEntity::new, GCBlocks.CANNED_FOOD); diff --git a/src/main/java/dev/galacticraft/mod/content/GCBlocks.java b/src/main/java/dev/galacticraft/mod/content/GCBlocks.java index eecf575740..5398de8de6 100644 --- a/src/main/java/dev/galacticraft/mod/content/GCBlocks.java +++ b/src/main/java/dev/galacticraft/mod/content/GCBlocks.java @@ -368,6 +368,8 @@ public class GCBlocks { public static final Block REINFORCED_AIR_LOCK_FRAME = BLOCKS.registerWithItem(Constant.Block.REINFORCED_AIR_LOCK_FRAME, new Block(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK).strength(50.0F, 1200.0F))); public static final Block REINFORCED_AIR_LOCK_CONTROLLER = BLOCKS.registerWithItem(Constant.Block.REINFORCED_AIR_LOCK_CONTROLLER, new SimpleMachineBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK).strength(50.0F, 1200.0F), Constant.id(Constant.Block.REINFORCED_AIR_LOCK_CONTROLLER))); + public static final Block BUBBLE_AIR_LOCK_CONTROLLER = BLOCKS.registerWithItem(Constant.Block.BUBBLE_AIR_LOCK_CONTROLLER, new SimpleMachineBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.IRON_BLOCK), Constant.id(Constant.Block.BUBBLE_AIR_LOCK_CONTROLLER))); + // TORCHES public static final Block GLOWSTONE_TORCH = BLOCKS.register(Constant.Block.GLOWSTONE_TORCH, new GlowstoneTorchBlock(BlockBehaviour.Properties.of().noCollission().instabreak().lightLevel(blockStatex -> 14).sound(SoundType.WOOD).pushReaction(PushReaction.DESTROY))); public static final Block UNLIT_TORCH = BLOCKS.register(Constant.Block.UNLIT_TORCH, new UnlitTorchBlock(BlockBehaviour.Properties.of().noCollission().instabreak().lightLevel(blockStatex -> 0).sound(SoundType.WOOD).pushReaction(PushReaction.DESTROY))); diff --git a/src/main/java/dev/galacticraft/mod/content/block/entity/BubbleAirlockControllerBlockEntity.java b/src/main/java/dev/galacticraft/mod/content/block/entity/BubbleAirlockControllerBlockEntity.java new file mode 100644 index 0000000000..40b8d945cb --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/content/block/entity/BubbleAirlockControllerBlockEntity.java @@ -0,0 +1,333 @@ +package dev.galacticraft.mod.content.block.entity; + +import dev.galacticraft.machinelib.api.block.entity.MachineBlockEntity; +import dev.galacticraft.machinelib.api.machine.MachineStatus; +import dev.galacticraft.machinelib.api.machine.configuration.RedstoneMode; +import dev.galacticraft.machinelib.api.menu.MachineMenu; +import dev.galacticraft.machinelib.api.storage.MachineEnergyStorage; +import dev.galacticraft.machinelib.api.storage.StorageSpec; +import dev.galacticraft.mod.Constant; +import dev.galacticraft.mod.Galacticraft; +import dev.galacticraft.mod.client.model.GCModelLoader; +import dev.galacticraft.mod.content.AirlockState; +import dev.galacticraft.mod.content.GCBlockEntityTypes; +import dev.galacticraft.mod.content.ProximityAccess; +import dev.galacticraft.mod.content.block.machine.airlock.AirlockFrameScanner; +import dev.galacticraft.mod.machine.GCMachineStatuses; +import dev.galacticraft.mod.screen.BubbleAirlockControllerMenu; +import dev.maximus.glasswork.api.GlassworkAPI; +import dev.maximus.glasswork.api.InjectedQuad; +import dev.maximus.glasswork.api.QuadVertex; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.SectionPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class BubbleAirlockControllerBlockEntity extends MachineBlockEntity { + private static final long PER_TICK = Galacticraft.CONFIG.oxygenCollectorEnergyConsumptionRate(); + + private static final int COLOR_ARGB = 0x50296BF2; + private static final int PACKED_LIGHT = 0x00F000F0; + + private static final StorageSpec SPEC = StorageSpec.of( + MachineEnergyStorage.spec( + Galacticraft.CONFIG.machineEnergyStorageSize(), + Galacticraft.CONFIG.oxygenCollectorEnergyConsumptionRate() * 2, + 0 + ) + ); + + private static final String NBT_ACCESS = "Access"; + private static final String NBT_ACTIVE = "Active"; + + private ProximityAccess access = ProximityAccess.PUBLIC; + private boolean active = false; + + private AirlockState displayState = AirlockState.NONE; + private List frames = Collections.emptyList(); + private Map frameIndex = Collections.emptyMap(); + + private final Set uploadedSections = new HashSet<>(); + + public BubbleAirlockControllerBlockEntity(BlockPos pos, BlockState state) { + super(GCBlockEntityTypes.BUBBLE_AIRLOCK_CONTROLLER, pos, state, SPEC); + } + + public ProximityAccess getAccess() { return access; } + public void setAccess(ProximityAccess a) { + if (a == null) a = ProximityAccess.PUBLIC; + if (a != this.access) { this.access = a; setChanged(); } + } + public AirlockState getDisplayState() { return displayState; } + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider lookup) { + super.saveAdditional(tag, lookup); + tag.putInt(NBT_ACCESS, access.ordinal()); + tag.putBoolean(NBT_ACTIVE, active); + } + + @Override + public void loadAdditional(CompoundTag tag, HolderLookup.Provider lookup) { + super.loadAdditional(tag, lookup); + if (tag.contains(NBT_ACCESS)) { + int i = tag.getInt(NBT_ACCESS); + access = (i >= 0 && i < ProximityAccess.values().length) ? ProximityAccess.values()[i] : ProximityAccess.PUBLIC; + } + if (tag.contains(NBT_ACTIVE)) { + active = tag.getBoolean(NBT_ACTIVE); + } + onLoad(); + } + + public void onLoad() { + if (!(this.level instanceof ServerLevel server)) return; + rescanFrames(server); + if (active) pushAllQuads(); + } + + @Override + public void setRemoved() { + try { + clearAllQuads(); + } finally { + super.setRemoved(); + } + } + + @Override + protected void tickConstant(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { + serverTick(level); + super.tickConstant(level, pos, state, profiler); + } + + @Override + protected @NotNull MachineStatus tick(@NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ProfilerFiller profiler) { + return switch (displayState) { + case ALL -> GCMachineStatuses.AIRLOCK_ENABLED; + case PARTIAL -> GCMachineStatuses.AIRLOCK_PARTIAL; + case NONE -> GCMachineStatuses.AIRLOCK_DISABLED; + }; + } + + private void serverTick(ServerLevel server) { + boolean framesChanged = rescanFrames(server); + + boolean powered = server.getBestNeighborSignal(this.worldPosition) > 0; + RedstoneMode rs = this.getRedstoneMode(); + boolean rsAllows = rs.isActive(powered); + + boolean hasEnergy = this.energyStorage().canExtract(PER_TICK); + + boolean shouldBeActive = rsAllows && !frames.isEmpty() && hasEnergy; + + if (shouldBeActive != this.active || framesChanged) { + this.active = shouldBeActive; + if (this.active) { + pushAllQuads(); + displayState = AirlockState.ALL; + } else { + clearAllQuads(); + displayState = AirlockState.NONE; + } + setChanged(); + server.sendBlockUpdated(this.worldPosition, server.getBlockState(this.worldPosition), server.getBlockState(this.worldPosition), Block.UPDATE_CLIENTS); + } + + if (this.active) { + this.energyStorage().extract(PER_TICK); + applyBarrier(server); + } + } + + private boolean rescanFrames(ServerLevel server) { + List next = AirlockFrameScanner.scanAll(server, this.worldPosition); + if (!sameFrames(frames, next)) { + frames = next; + frameIndex = indexFrames(next); + return true; + } + return false; + } + + private static boolean sameFrames(List a, List b) { + if (a == b) return true; + if (a == null || b == null || a.size() != b.size()) return false; + for (int i = 0; i < a.size(); i++) { + var x = a.get(i); var y = b.get(i); + if (x.plane != y.plane) return false; + if (x.minX != y.minX || x.minY != y.minY || x.minZ != y.minZ) return false; + if (x.maxX != y.maxX || x.maxY != y.maxY || x.maxZ != y.maxZ) return false; + } + return true; + } + + private static Map indexFrames(List list) { + Map out = new HashMap<>(list.size()); + for (AirlockFrameScanner.Result r : list) out.put(frameId(r), r); + return out; + } + + private static long frameId(AirlockFrameScanner.Result f) { + int h = 1; + h = 31 * h + f.plane.ordinal(); + h = 31 * h + f.minX; h = 31 * h + f.minY; h = 31 * h + f.minZ; + h = 31 * h + f.maxX; h = 31 * h + f.maxY; h = 31 * h + f.maxZ; + return (h & 0xffffffffL); + } + + private void pushAllQuads() { + if (level == null) return; + clearAllQuads(); + + for (var f : frames) { + var quads = buildPlaneQuads(f, COLOR_ARGB, PACKED_LIGHT); + if (!quads.isEmpty()) { + var q = quads.get(0); + SectionPos sec = SectionPos.of(new BlockPos((int) q.v1().x(), (int) q.v1().y(), (int) q.v1().z())); + if (level instanceof ServerLevel server) { + GlassworkAPI.serverPut(server, sec, quads); + } + uploadedSections.add(sec); + } + } + } + + private void clearAllQuads() { + if (level == null) return; + if (level instanceof ServerLevel server) { + for (SectionPos sec : uploadedSections) { + GlassworkAPI.serverRemoveAll(server, sec); + } + } + uploadedSections.clear(); + } + + private List buildPlaneQuads(AirlockFrameScanner.Result f, int argb, int light) { + List out = new ArrayList<>(2); + + switch (f.plane) { + case XY -> { + int z = f.minZ; + float x1 = f.minX + 1, x2 = f.maxX; + float y1 = f.minY + 1, y2 = f.maxY; + + float zPlus = z + 0.5f; + float zMinus = z + 0.5f; + + out.add(quad(x1, y1, zPlus, x2, y1, zPlus, x2, y2, zPlus, x1, y2, zPlus, + argb, light, 0, 0, +1)); + out.add(quad(x1, y2, zMinus, x2, y2, zMinus, x2, y1, zMinus, x1, y1, zMinus, + argb, light, 0, 0, -1)); + } + + case XZ -> { + int y = f.minY; + float x1 = f.minX + 1, x2 = f.maxX; + float z1 = f.minZ + 1, z2 = f.maxZ; + + float yPlus = y + 0.5f; + float yMinus = y + 0.5f; + + out.add(quad(x1, yPlus, z1, x2, yPlus, z1, x2, yPlus, z2, x1, yPlus, z2, + argb, light, 0, +1, 0)); + out.add(quad(x1, yMinus, z2, x2, yMinus, z2, x2, yMinus, z1, x1, yMinus, z1, + argb, light, 0, -1, 0)); + } + + case YZ -> { + int x = f.minX; + float y1 = f.minY + 1, y2 = f.maxY; + float z1 = f.minZ + 1, z2 = f.maxZ; + + float xPlus = x + 0.5f; + float xMinus = x + 0.5f; + + out.add(quad(xPlus, y1, z1, xPlus, y1, z2, xPlus, y2, z2, xPlus, y2, z1, + argb, light, +1, 0, 0)); + out.add(quad(xMinus, y2, z1, xMinus, y2, z2, xMinus, y1, z2, xMinus, y1, z1, + argb, light, -1, 0, 0)); + } + } + return out; + } + + private static final ResourceLocation WHITE_SPRITE_ID = Constant.id("block/white"); + + private static TextureAtlasSprite whiteSprite() { + var mc = Minecraft.getInstance(); + return mc.getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(WHITE_SPRITE_ID); + } + + private static InjectedQuad quad( + float x1,float y1,float z1, + float x2,float y2,float z2, + float x3,float y3,float z3, + float x4,float y4,float z4, + int argb, int light, + float nx,float ny,float nz + ) { + var s = whiteSprite(); + float u0 = s != null ? s.getU0() : 0f, v0 = s != null ? s.getV0() : 0f; + float u1 = s != null ? s.getU1() : 1f, v1 = s != null ? s.getV1() : 1f; + + var v1q = new QuadVertex(x1,y1,z1, u0,v0, argb, light, 0, nx,ny,nz); + var v2q = new QuadVertex(x2,y2,z2, u1,v0, argb, light, 0, nx,ny,nz); + var v3q = new QuadVertex(x3,y3,z3, u1,v1, argb, light, 0, nx,ny,nz); + var v4q = new QuadVertex(x4,y4,z4, u0,v1, argb, light, 0, nx,ny,nz); + return new InjectedQuad(v1q,v2q,v3q,v4q); + } + + private void applyBarrier(ServerLevel level) { + for (var f : frames) { + AABB aabb = interiorSlab(f).inflate(0.2); + for (Player p : level.getEntitiesOfClass(Player.class, aabb)) { + if (isAuthorized(p)) continue; + Direction d = f.sealFacing; + p.setDeltaMovement(p.getDeltaMovement().add( + d.getStepX() * 0.2, + d.getStepY() * 0.2, + d.getStepZ() * 0.2 + )); + p.hurtMarked = true; + } + } + } + + private boolean isAuthorized(Player p) { + return switch (access) { + case PUBLIC -> true; + case TEAM -> this.getSecurity().hasAccess(p); + case PRIVATE -> this.getSecurity().isOwner(p); + }; + } + + private static AABB interiorSlab(AirlockFrameScanner.Result f) { + return switch (f.plane) { + case XY -> new AABB(f.minX + 1, f.minY + 1, f.minZ, f.maxX, f.maxY, f.maxZ); + case XZ -> new AABB(f.minX + 1, f.minY, f.minZ + 1, f.maxX, f.maxY, f.maxZ); + case YZ -> new AABB(f.minX, f.minY + 1, f.minZ + 1, f.maxX, f.maxY, f.maxZ); + }; + } + + @Override + public @Nullable MachineMenu createMenu(int syncId, Inventory inventory, Player player) { + return new BubbleAirlockControllerMenu(syncId, player, this); + } +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java b/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java index 25c3998f66..d623f56046 100644 --- a/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java +++ b/src/main/java/dev/galacticraft/mod/content/block/machine/airlock/AirlockFrameScanner.java @@ -23,11 +23,13 @@ package dev.galacticraft.mod.content.block.machine.airlock; import dev.galacticraft.mod.content.block.entity.AirlockControllerBlockEntity; +import dev.galacticraft.mod.content.block.entity.BubbleAirlockControllerBlockEntity; import dev.galacticraft.mod.tag.GCBlockTags; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Direction.Axis; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; import java.util.*; @@ -44,9 +46,9 @@ public final class AirlockFrameScanner { .thenComparingInt(r -> r.maxX).thenComparingInt(r -> r.maxY).thenComparingInt(r -> r.maxZ); public enum Plane { - XY(Axis.Z), // z is constant - XZ(Axis.Y), // y is constant - YZ(Axis.X); // x is constant + XY(Axis.Z), + XZ(Axis.Y), + YZ(Axis.X); final Axis normal; Plane(Axis normal) { this.normal = normal; } } @@ -54,7 +56,7 @@ public enum Plane { public static final class Result { public final Plane plane; public final int minX, minY, minZ, maxX, maxY, maxZ; - public final Direction sealFacing; // FACING for AIR_LOCK_SEAL + public final Direction sealFacing; Result(Plane plane, int minX, int minY, int minZ, @@ -72,7 +74,8 @@ private static boolean isFrame(Level level, BlockPos pos) { } private static boolean isController(Level level, BlockPos pos) { - return level.getBlockEntity(pos) instanceof AirlockControllerBlockEntity; + BlockEntity be = level.getBlockEntity(pos); + return be instanceof AirlockControllerBlockEntity || be instanceof BubbleAirlockControllerBlockEntity; } /** Scan all planes; return up to two smallest rectangles per plane. */ @@ -82,7 +85,7 @@ public static List scanAll(Level level, BlockPos controller) { for (Plane plane : Plane.values()) { out.addAll(scanPlane(level, controller, plane)); } - out.sort(ORDER); // stable order across ticks + out.sort(ORDER); return out; } @@ -94,28 +97,22 @@ public static Result scan(Level level, BlockPos controller) { : all.get(0); } - // ---------- Plane scanning ---------- - private record Axes(Axis u, Axis v, Axis wConst) {} private static List scanPlane(Level level, BlockPos controller, Plane plane) { final int fixed = controller.get(plane.normal); - final Axes axes = axesFor(plane); // u,v are the in-plane axes + final Axes axes = axesFor(plane); - // 1) Gather all frame blocks connected to controller within this plane Set frames = floodInPlane(level, controller, plane, fixed); if (frames.isEmpty()) return List.of(); - // Require exactly one controller in this connected set int controllers = 0; for (BlockPos p : frames) if (isController(level, p)) controllers++; if (controllers != 1) return List.of(); - // 2) in-plane coords of controller int u0 = proj(controller, axes.u); int v0 = proj(controller, axes.v); - // Search bounds (tight box around connected set) int minU = Integer.MAX_VALUE, maxU = Integer.MIN_VALUE, minV = Integer.MAX_VALUE, maxV = Integer.MIN_VALUE; for (BlockPos p : frames) { int u = proj(p, axes.u), v = proj(p, axes.v); @@ -123,7 +120,6 @@ private static List scanPlane(Level level, BlockPos controller, Plane pl if (v < minV) minV = v; if (v > maxV) maxV = v; } - // 3) Find up to two rectangles with controller on an edge Result bestVMin = findBestRectWithFixedEdge(level, frames, plane, axes, fixed, u0, v0, EdgeKind.VMIN, minU, maxU, minV, maxV); Result bestVMax = findBestRectWithFixedEdge(level, frames, plane, axes, fixed, u0, v0, EdgeKind.VMAX, minU, maxU, minV, maxV); Result bestUMin = findBestRectWithFixedEdge(level, frames, plane, axes, fixed, u0, v0, EdgeKind.UMIN, minU, maxU, minV, maxV); @@ -135,11 +131,10 @@ private static List scanPlane(Level level, BlockPos controller, Plane pl if (planeOut.size() < 2) addIfNotNullDistinct(planeOut, bestUMin); if (planeOut.size() < 2) addIfNotNullDistinct(planeOut, bestUMax); - planeOut.sort(ORDER); // stable per plane + planeOut.sort(ORDER); return planeOut; } - // ---------- Plane scanning ---------- private enum EdgeKind { VMIN, VMAX, UMIN, UMAX } private static void addIfNotNullDistinct(List list, Result r) { @@ -159,11 +154,10 @@ private static Result findBestRectWithFixedEdge( int u0, int v0, EdgeKind kind, int minU, int maxU, int minV, int maxV ) { - // Fix one edge to pass through the controller (so controller sits on that edge). - final boolean edgeIsAlongU; // true if the fixed edge is a U-span (constant v), false if a V-span (constant u) + final boolean edgeIsAlongU; switch (kind) { - case VMIN, VMAX -> edgeIsAlongU = true; // edge is v == v0, spans in U - case UMIN, UMAX -> edgeIsAlongU = false; // edge is u == u0, spans in V + case VMIN, VMAX -> edgeIsAlongU = true; + case UMIN, UMAX -> edgeIsAlongU = false; default -> throw new IllegalStateException(); } @@ -171,11 +165,8 @@ private static Result findBestRectWithFixedEdge( int bestMinU = 0, bestMaxU = 0, bestMinV = 0, bestMaxV = 0; if (edgeIsAlongU) { - // Controller sits on v == v0 edge; search vOpp on the other side. if (kind == EdgeKind.VMIN) { - // expand toward +V (vOpp > v0) for (int vOpp = v0 + 1; vOpp <= maxV; vOpp++) { - // sweep U around the controller, ensuring the fixed edge is continuous for (int uMin = u0; uMin >= minU; uMin--) { if (!isFrame(level, unproj(uMin, v0, fixed, axes))) break; for (int uMax = u0; uMax <= maxU; uMax++) { @@ -197,7 +188,7 @@ && interiorHasNoFrames(frames, axes, fixed, uMin, vMinRect, uMax, vMaxRect)) { } } } - } else { // VMAX: expand toward -V (vOpp < v0) --- FIXED LOOPS --- + } else { for (int vOpp = v0 - 1; vOpp >= minV; vOpp--) { for (int uMin = u0; uMin >= minU; uMin--) { if (!isFrame(level, unproj(uMin, v0, fixed, axes))) break; @@ -222,9 +213,7 @@ && interiorHasNoFrames(frames, axes, fixed, uMin, vMinRect, uMax, vMaxRect)) { } } } else { - // Controller sits on u == u0 edge; search uOpp on the other side. if (kind == EdgeKind.UMIN) { - // expand toward +U (uOpp > u0) for (int uOpp = u0 + 1; uOpp <= maxU; uOpp++) { for (int vMin2 = v0; vMin2 >= minV; vMin2--) { if (!isFrame(level, unproj(u0, vMin2, fixed, axes))) break; @@ -247,7 +236,7 @@ && interiorHasNoFrames(frames, axes, fixed, uMinRect, vMin2, uMaxRect, vMax2)) { } } } - } else { // UMAX: expand toward -U (uOpp < u0) --- FIXED LOOPS --- + } else { for (int uOpp = u0 - 1; uOpp >= minU; uOpp--) { for (int vMin2 = v0; vMin2 >= minV; vMin2--) { if (!isFrame(level, unproj(u0, vMin2, fixed, axes))) break; @@ -275,7 +264,6 @@ && interiorHasNoFrames(frames, axes, fixed, uMinRect, vMin2, uMaxRect, vMax2)) { if (bestArea == Long.MAX_VALUE) return null; - // Map u/v bounds back to xyz bounds int minX, minY, minZ, maxX, maxY, maxZ; switch (plane) { case XY -> { @@ -296,18 +284,15 @@ && interiorHasNoFrames(frames, axes, fixed, uMinRect, vMin2, uMaxRect, vMax2)) { default -> throw new IllegalStateException(); } - // Seal facing = plane normal direction Direction facing = switch (plane) { - case XY -> Direction.NORTH; // ⟂ Z - case XZ -> Direction.UP; // ⟂ Y - case YZ -> Direction.EAST; // ⟂ X + case XY -> Direction.NORTH; + case XZ -> Direction.UP; + case YZ -> Direction.EAST; }; return new Result(plane, minX, minY, minZ, maxX, maxY, maxZ, facing); } - // ---------- Geometry helpers ---------- - private static Axes axesFor(Plane plane) { return switch (plane) { case XY -> new Axes(Axis.X, Axis.Y, Axis.Z); @@ -348,7 +333,6 @@ private static int proj(BlockPos p, Axis axis) { } private static BlockPos unproj(int u, int v, int wConst, Axes axes) { - // inverse mapping from (u,v) back to (x,y,z) int x=0,y=0,z=0; for (Axis a : new Axis[]{axes.u, axes.v, axes.wConst}) { int val = (a == axes.u) ? u : (a == axes.v) ? v : wConst; @@ -363,7 +347,6 @@ private static BlockPos unproj(int u, int v, int wConst, Axes axes) { private static boolean perimeterIsFrames(Set frames, Axes axes, int wConst, int uMin, int vMin, int uMax, int vMax) { - // edges: u in [uMin..uMax] at v=vMin and v=vMax; v in [vMin..vMax] at u=uMin and u=uMax for (int u = uMin; u <= uMax; u++) { if (!frames.contains(unproj(u, vMin, wConst, axes))) return false; if (!frames.contains(unproj(u, vMax, wConst, axes))) return false; diff --git a/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java b/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java index 9a8948659e..b9786c7036 100644 --- a/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java +++ b/src/main/java/dev/galacticraft/mod/content/item/GCCreativeModeTabs.java @@ -279,6 +279,7 @@ public class GCCreativeModeTabs { output.accept(AIR_LOCK_CONTROLLER); output.accept(REINFORCED_AIR_LOCK_FRAME); output.accept(REINFORCED_AIR_LOCK_CONTROLLER); + output.accept(BUBBLE_AIR_LOCK_CONTROLLER); }).build(); public static final CreativeModeTab ITEMS_GROUP = FabricItemGroup diff --git a/src/main/java/dev/galacticraft/mod/data/GCBlockLootTableProvider.java b/src/main/java/dev/galacticraft/mod/data/GCBlockLootTableProvider.java index 243bca50e3..24dd7a51bf 100644 --- a/src/main/java/dev/galacticraft/mod/data/GCBlockLootTableProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/GCBlockLootTableProvider.java @@ -286,6 +286,7 @@ public void generate() { this.dropSelf(GCBlocks.AIR_LOCK_CONTROLLER); this.dropSelf(GCBlocks.REINFORCED_AIR_LOCK_FRAME); this.dropSelf(GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER); + this.dropSelf(GCBlocks.BUBBLE_AIR_LOCK_CONTROLLER); this.dropSelf(GCBlocks.CRYOGENIC_CHAMBER); this.dropSelf(GCBlocks.CIRCUIT_FABRICATOR); this.dropSelf(GCBlocks.COMPRESSOR); diff --git a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java index eec1a8a5cb..838901f8a3 100644 --- a/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/GCTranslationProvider.java @@ -378,6 +378,7 @@ protected void generateBlockTranslations() { this.block(GCBlocks.AIR_LOCK_CONTROLLER, "Airlock Controller"); this.block(GCBlocks.REINFORCED_AIR_LOCK_FRAME, "Reinforced Airlock Frame"); this.block(GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER, "Reinforced Airlock Controller"); + this.block(GCBlocks.BUBBLE_AIR_LOCK_CONTROLLER, "Bubble Airlock Controller"); this.block(GCBlocks.AIR_LOCK_SEAL, "Airlock Seal"); } diff --git a/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java b/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java index cd73ced8e0..ba5b5fbe25 100644 --- a/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/model/GCModelProvider.java @@ -474,8 +474,10 @@ private void createVaporSpout(BlockModelGenerators generator) { private void createAirLockControllers(BlockModelGenerators generator) { var basic = GCBlocks.AIR_LOCK_CONTROLLER; var reinforced = GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER; + var bubble = GCBlocks.BUBBLE_AIR_LOCK_CONTROLLER; ResourceLocation basicController = TextureMapping.getBlockTexture(basic); ResourceLocation reinforcedController = TextureMapping.getBlockTexture(reinforced); + ResourceLocation bubbleController = TextureMapping.getBlockTexture(bubble); ResourceLocation basicFrame = TextureMapping.getBlockTexture(GCBlocks.AIR_LOCK_FRAME); ResourceLocation reinforcedFrame = TextureMapping.getBlockTexture(GCBlocks.REINFORCED_AIR_LOCK_FRAME); MachineModelGenerator.createTrivialMachine(generator, basic, TextureProvider.builder(Constant.MOD_ID) @@ -492,6 +494,13 @@ private void createAirLockControllers(BlockModelGenerators generator) { .particle(reinforcedController) .build() ); + MachineModelGenerator.createTrivialMachine(generator, bubble, TextureProvider.builder(Constant.MOD_ID) + .sides(bubbleController) + .top(basicFrame) + .bottom(basicFrame) + .particle(bubbleController) + .build() + ); } private void createRocketWorkbench(BlockModelGenerators generator) { diff --git a/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java b/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java index d5c162c9e0..cf93fe3a28 100644 --- a/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java +++ b/src/main/java/dev/galacticraft/mod/data/tag/GCBlockTagProvider.java @@ -387,7 +387,8 @@ protected void addTags(HolderLookup.Provider provider) { GCBlocks.OXYGEN_COMPRESSOR, GCBlocks.OXYGEN_STORAGE_MODULE, GCBlocks.FOOD_CANNER, - GCBlocks.ROCKET_WORKBENCH + GCBlocks.ROCKET_WORKBENCH, + GCBlocks.BUBBLE_AIR_LOCK_CONTROLLER ); this.tag(GCBlockTags.GLASS_FLUID_PIPES).add(GCBlocks.GLASS_FLUID_PIPE).addTag(GCBlockTags.STAINED_GLASS_FLUID_PIPES); @@ -402,7 +403,8 @@ protected void addTags(HolderLookup.Provider provider) { .add(GCBlocks.AIR_LOCK_FRAME) .add(GCBlocks.AIR_LOCK_CONTROLLER) .add(GCBlocks.REINFORCED_AIR_LOCK_FRAME) - .add(GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER); + .add(GCBlocks.REINFORCED_AIR_LOCK_CONTROLLER) + .add(GCBlocks.BUBBLE_AIR_LOCK_CONTROLLER); this.tag(ConventionalBlockTags.VILLAGER_JOB_SITES) .add(GCBlocks.LUNAR_CARTOGRAPHY_TABLE); diff --git a/src/main/java/dev/galacticraft/mod/lookup/GCApiLookupProviders.java b/src/main/java/dev/galacticraft/mod/lookup/GCApiLookupProviders.java index 03b3590ee2..f0624d29c5 100644 --- a/src/main/java/dev/galacticraft/mod/lookup/GCApiLookupProviders.java +++ b/src/main/java/dev/galacticraft/mod/lookup/GCApiLookupProviders.java @@ -55,7 +55,8 @@ public class GCApiLookupProviders { GCBlockEntityTypes.OXYGEN_BUBBLE_DISTRIBUTOR, GCBlockEntityTypes.ENERGY_STORAGE_MODULE, GCBlockEntityTypes.FOOD_CANNER, - GCBlockEntityTypes.OXYGEN_STORAGE_MODULE + GCBlockEntityTypes.OXYGEN_STORAGE_MODULE, + GCBlockEntityTypes.BUBBLE_AIRLOCK_CONTROLLER }; @SuppressWarnings("rawtypes") private static final BlockEntityType[] WIRE_TYPES = new BlockEntityType[]{ diff --git a/src/main/java/dev/galacticraft/mod/screen/BubbleAirlockControllerMenu.java b/src/main/java/dev/galacticraft/mod/screen/BubbleAirlockControllerMenu.java new file mode 100644 index 0000000000..7636e00f61 --- /dev/null +++ b/src/main/java/dev/galacticraft/mod/screen/BubbleAirlockControllerMenu.java @@ -0,0 +1,32 @@ +package dev.galacticraft.mod.screen; + +import dev.galacticraft.machinelib.api.menu.MachineMenu; +import dev.galacticraft.machinelib.api.menu.MenuData; +import dev.galacticraft.mod.content.AirlockState; +import dev.galacticraft.mod.content.ProximityAccess; +import dev.galacticraft.mod.content.block.entity.BubbleAirlockControllerBlockEntity; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import org.jetbrains.annotations.NotNull; + +public class BubbleAirlockControllerMenu extends MachineMenu { + public AirlockState state; + public ProximityAccess access; + + public BubbleAirlockControllerMenu(int syncId, Player player, BubbleAirlockControllerBlockEntity be) { + super(GCMenuTypes.BUBBLE_AIR_LOCK_CONTROLLER_MENU, syncId, player, be); + this.state = be.getDisplayState(); + this.access = be.getAccess(); + } + + public BubbleAirlockControllerMenu(int syncId, Inventory inv, net.minecraft.core.BlockPos pos) { + super(GCMenuTypes.BUBBLE_AIR_LOCK_CONTROLLER_MENU, syncId, inv, pos, 8, 89); + } + + @Override + public void registerData(@NotNull MenuData data) { + super.registerData(data); + data.registerInt(() -> this.be.getDisplayState().ordinal(), i -> this.state = AirlockState.values()[i]); + data.registerInt(() -> this.be.getAccess().ordinal(), i -> this.access = ProximityAccess.values()[i]); + } +} \ No newline at end of file diff --git a/src/main/java/dev/galacticraft/mod/screen/GCMenuTypes.java b/src/main/java/dev/galacticraft/mod/screen/GCMenuTypes.java index fda2958efd..a20eea7b55 100644 --- a/src/main/java/dev/galacticraft/mod/screen/GCMenuTypes.java +++ b/src/main/java/dev/galacticraft/mod/screen/GCMenuTypes.java @@ -69,6 +69,7 @@ public class GCMenuTypes { public static final MenuType PET_INV_GC = new ExtendedScreenHandlerType<>(GCPetInventoryMenu::new, ByteBufCodecs.INT); public static final MenuType AIRLOCK_CONTROLLER_MENU = SynchronizedMenuType.create(AirlockControllerMenu::new); + public static final MenuType BUBBLE_AIR_LOCK_CONTROLLER_MENU = SynchronizedMenuType.create(BubbleAirlockControllerMenu::new); public static final MenuType ROCKET_WORKBENCH = new ExtendedScreenHandlerType<>(RocketWorkbenchMenu::new, RocketWorkbenchMenu.OpeningData.CODEC); public static final MenuType ROCKET = new ExtendedScreenHandlerType<>(RocketMenu::new, ByteBufCodecs.INT); public static final MenuType PARACHEST = new ExtendedScreenHandlerType<>(ParachestMenu::new, ParachestMenu.OpeningData.STREAM_CODEC); @@ -102,6 +103,7 @@ public static void register() { Registry.register(BuiltInRegistries.MENU, Constant.id(Constant.Menu.FOOD_CANNER_MENU), FOOD_CANNER); Registry.register(BuiltInRegistries.MENU, Constant.id(Constant.Menu.AIR_LOCK_CONTROLLER_MENU), AIRLOCK_CONTROLLER_MENU); + Registry.register(BuiltInRegistries.MENU, Constant.id(Constant.Menu.BUBBLE_AIR_LOCK_CONTROLLER_MENU), BUBBLE_AIR_LOCK_CONTROLLER_MENU); Registry.register(BuiltInRegistries.MENU, Constant.id(Constant.Menu.ROCKET_WORKBENCH_MENU), ROCKET_WORKBENCH); Registry.register(BuiltInRegistries.MENU, Constant.id(Constant.Menu.ROCKET), ROCKET); Registry.register(BuiltInRegistries.MENU, Constant.id(Constant.Menu.PARACHEST), PARACHEST); diff --git a/src/main/resources/assets/galacticraft/atlases/blocks.json b/src/main/resources/assets/galacticraft/atlases/blocks.json new file mode 100644 index 0000000000..43ad4dab72 --- /dev/null +++ b/src/main/resources/assets/galacticraft/atlases/blocks.json @@ -0,0 +1,8 @@ +{ + "sources": [ + { + "type": "minecraft:single", + "resource": "galacticraft:block/white" + } + ] +} diff --git a/src/main/resources/assets/galacticraft/textures/block/white.png b/src/main/resources/assets/galacticraft/textures/block/white.png new file mode 100644 index 0000000000000000000000000000000000000000..38ca2a91c713cb8c298715ee35fbaee494146304 GIT binary patch literal 84 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|(w;7kAr*1SKm3&v9K;n^+)M?S eK{#VQBg6W&f91C;D6|5VF?hQAxvX Date: Thu, 4 Sep 2025 09:40:01 +1000 Subject: [PATCH 11/11] updated Glasswork version --- build.gradle.kts | 3 ++- gradle.properties | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 91229eca53..f4951aaf27 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -49,6 +49,7 @@ val wthitVersion = project.property("wthit.version").toString() val architecturyVersion = project.property("architectury.version").toString() val appleskinVersion = project.property("appleskin.version").toString() val objVersion = project.property("obj.version").toString() +val glassworkVersion = project.property("glasswork.version").toString() plugins { java @@ -239,7 +240,7 @@ dependencies { "core"("dev.galacticraft:dynamicdimensions-fabric:$dynamicdimensionsVersion") "core"("dev.galacticraft:MachineLib:$machineLibVersion") - "core"("dev.maximus:glasswork:1.3") + "core"("dev.maximus:glasswork:$glassworkVersion") "core"("lol.bai:badpackets:fabric-$badpacketsVersion") // Optional Dependencies diff --git a/gradle.properties b/gradle.properties index b03554615e..52fa940468 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,3 +24,4 @@ wthit.version=12.5.4 obj.version=0.4.0 appleskin.version=mc1.21-3.0.5 architectury.version=13.0.6 +glasswork.version=1.5.1 \ No newline at end of file