diff --git a/common/src/main/java/toughasnails/api/container/TANContainerTypes.java b/common/src/main/java/toughasnails/api/container/TANContainerTypes.java index 6539ac67..5ef20202 100644 --- a/common/src/main/java/toughasnails/api/container/TANContainerTypes.java +++ b/common/src/main/java/toughasnails/api/container/TANContainerTypes.java @@ -9,4 +9,5 @@ public class TANContainerTypes { public static MenuType WATER_PURIFIER; + public static MenuType THERMOREGULATOR; } diff --git a/common/src/main/java/toughasnails/api/temperature/ITemperature.java b/common/src/main/java/toughasnails/api/temperature/ITemperature.java index aa86d260..935fd98c 100644 --- a/common/src/main/java/toughasnails/api/temperature/ITemperature.java +++ b/common/src/main/java/toughasnails/api/temperature/ITemperature.java @@ -4,6 +4,10 @@ ******************************************************************************/ package toughasnails.api.temperature; +import net.minecraft.core.BlockPos; + +import java.util.Set; + public interface ITemperature { /** @@ -54,6 +58,18 @@ public interface ITemperature */ int getLastHyperthermiaTicks(); + /** + * Get the last positions of nearby thermoregulators. + * @return nearby thermoregulators. + */ + Set getLastNearbyThermoregulators(); + + /** + * Get the positions of nearby thermoregulators. + * @return nearby thermoregulators. + */ + Set getNearbyThermoregulators(); + /** * Set the temperature level. * @param level temperature level @@ -101,4 +117,16 @@ public interface ITemperature * @param ticks number of ticks. */ void setLastHyperthermiaTicks(int ticks); + + /** + * Set the last nearby thermoregulators. + * @param values nearby thermoregulators. + */ + void setLastNearbyThermoregulators(Set values); + + /** + * Set the nearby thermoregulators. + * @param values nearby thermoregulators. + */ + void setNearbyThermoregulators(Set values); } diff --git a/common/src/main/java/toughasnails/api/temperature/TemperatureHelper.java b/common/src/main/java/toughasnails/api/temperature/TemperatureHelper.java index ec466a04..437561c4 100644 --- a/common/src/main/java/toughasnails/api/temperature/TemperatureHelper.java +++ b/common/src/main/java/toughasnails/api/temperature/TemperatureHelper.java @@ -96,7 +96,7 @@ public static int getTicksHyperthermic(Player player) * @param state the state to check. * @return is heating. */ - public static boolean isHeating(BlockState state) + public static boolean isHeatingBlock(BlockState state) { return Impl.INSTANCE.isHeating(state); } @@ -106,7 +106,7 @@ public static boolean isHeating(BlockState state) * @param state the state to check. * @return is cooling. */ - public static boolean isCooling(BlockState state) + public static boolean isCoolingBlock(BlockState state) { return Impl.INSTANCE.isCooling(state); } diff --git a/common/src/main/java/toughasnails/block/ThermoregulatorBlock.java b/common/src/main/java/toughasnails/block/ThermoregulatorBlock.java index 5c565cf8..ea85fd05 100644 --- a/common/src/main/java/toughasnails/block/ThermoregulatorBlock.java +++ b/common/src/main/java/toughasnails/block/ThermoregulatorBlock.java @@ -7,6 +7,9 @@ import com.mojang.serialization.MapCodec; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.*; @@ -17,6 +20,7 @@ import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.phys.BlockHitResult; import toughasnails.api.blockentity.TANBlockEntityTypes; import toughasnails.block.entity.ThermoregulatorBlockEntity; import toughasnails.block.entity.WaterPurifierBlockEntity; @@ -43,6 +47,20 @@ protected MapCodec codec() return CODEC; } + @Override + public InteractionResult use(BlockState state, Level worldIn, BlockPos pos, Player player, InteractionHand handIn, BlockHitResult hit) + { + if (worldIn.isClientSide) + { + return InteractionResult.SUCCESS; + } + else + { + player.openMenu(state.getMenuProvider(worldIn, pos)); + return InteractionResult.CONSUME; + } + } + @Override public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { @@ -87,20 +105,9 @@ protected void createBlockStateDefinition(StateDefinition.Builder items = NonNullList.withSize(2, ItemStack.EMPTY); + + private int coolingTimeRemaining; + private int heatingTimeRemaining; + private int fillTimer = 0; + private Set filledBlocks = new HashSet<>(); + + protected final ContainerData dataAccess = new ContainerData() + { + @Override + public int get(int index) + { + switch(index) { + case 0: + return ThermoregulatorBlockEntity.this.coolingTimeRemaining; + case 1: + return ThermoregulatorBlockEntity.this.heatingTimeRemaining; + default: + return 0; + } + } + + @Override + public void set(int index, int value) + { + switch(index) { + case 0: + ThermoregulatorBlockEntity.this.coolingTimeRemaining = value; + break; + case 1: + ThermoregulatorBlockEntity.this.heatingTimeRemaining = value; + break; + } + + } + + @Override + public int getCount() + { + return 2; + } + }; + + public ThermoregulatorBlockEntity(BlockPos pos, BlockState state) { super(TANBlockEntityTypes.THERMOREGULATOR, pos, state); } + @Override + public void load(CompoundTag nbt) + { + super.load(nbt); + this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); + ContainerHelper.loadAllItems(nbt, this.items); + this.coolingTimeRemaining = nbt.getInt("CoolingTimeRemaining"); + this.heatingTimeRemaining = nbt.getInt("HeatingTimeRemaining"); + this.fillTimer = nbt.getInt("FillTimer"); + + ListTag list = nbt.getList("FilledBlocks", Tag.TAG_COMPOUND); + this.filledBlocks = list.stream().map(tag -> NbtUtils.readBlockPos((CompoundTag)tag)).collect(Collectors.toCollection(HashSet::new)); + } + + @Override + public void saveAdditional(CompoundTag nbt) + { + super.saveAdditional(nbt); + nbt.putInt("CoolingTimeRemaining", this.coolingTimeRemaining); + nbt.putInt("HeatingTimeRemaining", this.heatingTimeRemaining); + nbt.putInt("FillTimer", this.fillTimer); + + ListTag list = new ListTag(); + this.filledBlocks.stream().map(NbtUtils::writeBlockPos).forEach(list::add); + nbt.put("FilledBlocks", list); + + ContainerHelper.saveAllItems(nbt, this.items); + } + + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() + { + return ClientboundBlockEntityDataPacket.create(this); + } + + @Override + public CompoundTag getUpdateTag() + { + return this.saveWithoutMetadata(); + } + public static void serverTick(Level level, BlockPos pos, BlockState state, ThermoregulatorBlockEntity blockEntity) { + boolean previouslyCooling = blockEntity.isCooling(); + boolean previouslyHeating = blockEntity.isHeating(); + boolean changed = false; + + if (blockEntity.isCooling()) --blockEntity.coolingTimeRemaining; + if (blockEntity.isHeating()) --blockEntity.heatingTimeRemaining; + + if (!blockEntity.isCooling()) + { + ItemStack fuel = blockEntity.items.get(SLOT_COOLING); + + if (!fuel.isEmpty()) + { + Item fuelItem = fuel.getItem(); + blockEntity.coolingTimeRemaining = CONSUMABLE_DURATION; + changed = true; + fuel.shrink(1); + + if (fuel.isEmpty()) + { + Item remainingItem = fuelItem.getCraftingRemainingItem(); + blockEntity.items.set(SLOT_COOLING, remainingItem == null ? ItemStack.EMPTY : new ItemStack(remainingItem)); + } + } + } + + if (!blockEntity.isHeating()) + { + ItemStack fuel = blockEntity.items.get(SLOT_HEATING); + + if (!fuel.isEmpty()) + { + Item fuelItem = fuel.getItem(); + blockEntity.heatingTimeRemaining = CONSUMABLE_DURATION; + changed = true; + fuel.shrink(1); + + if (fuel.isEmpty()) + { + Item remainingItem = fuelItem.getCraftingRemainingItem(); + blockEntity.items.set(SLOT_HEATING, remainingItem == null ? ItemStack.EMPTY : new ItemStack(remainingItem)); + } + } + } + + if (previouslyCooling != blockEntity.isCooling()) + { + changed = true; + state = state.setValue(ThermoregulatorBlock.COOLING, blockEntity.isCooling()); + level.setBlock(pos, state, 3); + } + + if (previouslyHeating != blockEntity.isHeating()) + { + changed = true; + state = state.setValue(ThermoregulatorBlock.HEATING, blockEntity.isHeating()); + level.setBlock(pos, state, 3); + } + + if (!blockEntity.isHeating() && !blockEntity.isCooling()) + { + blockEntity.filledBlocks.clear(); + } + + // Fill every second + if ((blockEntity.isCooling() || blockEntity.isHeating()) && blockEntity.fillTimer % 20 == 0) + { + // Update nearby thermoregulators for players + for (ServerPlayer player : level.getEntitiesOfClass(ServerPlayer.class, new AABB(pos.getX(), pos.getY(), pos.getZ(), pos.getX(), pos.getY() - 4, pos.getZ()).inflate(INFORM_PLAYER_RADIUS, (double) INFORM_PLAYER_RADIUS / 2.0, INFORM_PLAYER_RADIUS))) { + ITemperature temperature = TemperatureHelper.getTemperatureData(player); + temperature.getNearbyThermoregulators().add(pos); + } + + // Perform fill + BlockPos fillStart = pos.relative(state.getValue(ThermoregulatorBlock.FACING)); + + blockEntity.filledBlocks.clear(); + AreaFill.fill(level, fillStart, new AreaFill.PositionChecker() { + @Override + public void onSolid(Level level, AreaFill.PosAndDepth pos) {} + + @Override + public void onPassable(Level level, AreaFill.PosAndDepth pos) + { + blockEntity.filledBlocks.add(pos.pos()); + } + + @Override + public boolean isPassable(Level level, BlockPos pos) + { + BlockState state = level.getBlockState(pos); + return state.isAir() || !isFullySolid(level, pos); + } + }, SPREAD_RADIUS); + + changed = true; + } + + ++blockEntity.fillTimer; + + // Mark as changed + if (changed) + { + setChanged(level, pos, state); + level.sendBlockUpdated(pos, state, state, 3); + } + } + + public Effect getEffectAtPos(BlockPos pos) + { + if (this.filledBlocks.contains(pos)) return this.getEffect(); + else return Effect.NONE; + } + + public ImmutableSet getFilledBlocks() + { + return ImmutableSet.copyOf(this.filledBlocks); + } + + public Effect getEffect() + { + boolean cooling = isCooling(); + boolean heating = isHeating(); + if (cooling && heating) return Effect.NEUTRALIZING; + else if (cooling) return Effect.COOLING; + else if (heating) return Effect.HEATING; + else return Effect.NONE; + } + + public boolean isCooling() + { + return this.coolingTimeRemaining > 0; + } + + public boolean isHeating() + { + return this.heatingTimeRemaining > 0; + } + + @Override + public boolean stillValid(Player player) + { + return Container.stillValidBlockEntity(this, player); + } + + @Override + protected AbstractContainerMenu createMenu(int id, Inventory player) + { + return new ThermoregulatorContainer(id, player, this, this.dataAccess); + } + + @Override + protected Component getDefaultName() + { + return Component.translatable("container.toughasnails.thermoregulator"); + } + + @Override + public int[] getSlotsForFace(Direction direction) + { + if (direction == Direction.UP) + return new int[]{SLOT_COOLING, SLOT_HEATING}; + + BlockState state = this.getBlockState(); + Direction facing = state.getValue(ThermoregulatorBlock.FACING); + + if (facing.getClockWise() == direction) return new int[]{SLOT_COOLING}; + else if (facing.getCounterClockWise() == direction) return new int[]{SLOT_HEATING}; + else return new int[]{}; + } + + @Override + public boolean canPlaceItemThroughFace(int index, ItemStack stack, @Nullable Direction direction) + { + return this.canPlaceItem(index, stack); + } + + @Override + public boolean canPlaceItem(int index, ItemStack stack) + { + if (index == SLOT_COOLING) return isCoolingFuel(stack); + else if (index == SLOT_HEATING) return isHeatingFuel(stack); + else return false; + } + + @Override + public boolean canTakeItemThroughFace(int index, ItemStack stack, Direction direction) { + return false; + } + + @Override + public int getContainerSize() + { + return this.items.size(); + } + + @Override + public boolean isEmpty() + { + for (ItemStack itemstack : this.items) + { + if (!itemstack.isEmpty()) { + return false; + } + } + + return true; + } + + @Override + public ItemStack getItem(int index) + { + return this.items.get(index); + } + + @Override + public ItemStack removeItem(int index, int count) + { + return ContainerHelper.removeItem(this.items, index, count); + } + + @Override + public ItemStack removeItemNoUpdate(int index) + { + return ContainerHelper.takeItem(this.items, index); + } + + @Override + public void setItem(int index, ItemStack stack) + { + ItemStack currentStack = this.items.get(index); + boolean sameItem = !stack.isEmpty() && ItemStack.isSameItemSameTags(stack, currentStack); + this.items.set(index, stack); + + if (stack.getCount() > this.getMaxStackSize()) + { + stack.setCount(this.getMaxStackSize()); + } + + if (!sameItem) + { + this.setChanged(); + } + } + + @Override + public void clearContent() + { + this.items.clear(); + } + + public static boolean isCoolingFuel(ItemStack stack) + { + return stack.is(ModTags.Items.THERMOREGULATOR_COOLING_FUEL); + } + + public static boolean isHeatingFuel(ItemStack stack) + { + return stack.is(ModTags.Items.THERMOREGULATOR_HEATING_FUEL); + } + + public enum Effect + { + COOLING, HEATING, NEUTRALIZING, NONE; } } diff --git a/common/src/main/java/toughasnails/client/gui/ThermoregulatorScreen.java b/common/src/main/java/toughasnails/client/gui/ThermoregulatorScreen.java new file mode 100644 index 00000000..b8ba2774 --- /dev/null +++ b/common/src/main/java/toughasnails/client/gui/ThermoregulatorScreen.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright 2021, the Glitchfiend Team. + * All rights reserved. + ******************************************************************************/ +package toughasnails.client.gui; + +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; +import toughasnails.api.TANAPI; +import toughasnails.container.ThermoregulatorContainer; +import toughasnails.container.WaterPurifierContainer; + +public class ThermoregulatorScreen extends AbstractContainerScreen +{ + private static final ResourceLocation TEXTURE = new ResourceLocation(TANAPI.MOD_ID, "textures/gui/container/thermoregulator.png"); + + public ThermoregulatorScreen(ThermoregulatorContainer screenContainer, Inventory inv, Component titleIn) + { + super(screenContainer, inv, titleIn); + } + + public void init() + { + super.init(); + this.leftPos = (this.width - this.imageWidth) / 2; + this.titleLabelX = (this.imageWidth - this.font.width(this.title)) / 2; + } + + @Override + public void render(GuiGraphics gui, int mouseX, int mouseY, float partialTicks) + { + this.renderBackground(gui, mouseX, mouseY, partialTicks); + super.render(gui, mouseX, mouseY, partialTicks); + this.renderTooltip(gui, mouseX, mouseY); + } + + @Override + protected void renderBg(GuiGraphics gui, float partialTicks, int mouseX, int mouseY) + { + int leftPos = this.leftPos; + int topPos = this.topPos; + gui.blit(TEXTURE, leftPos, topPos, 0, 0, this.imageWidth, this.imageHeight); + + if (this.menu.isCooling()) + { + int progress = this.menu.getCoolingFuelProgress(); + gui.blit(TEXTURE, leftPos + 44 + 1, topPos + 25 + 13 - progress, 176, 13 - progress, 14, progress + 1); + } + + if (this.menu.isHeating()) + { + int progress = this.menu.getHeatingFuelProgress(); + gui.blit(TEXTURE, leftPos + 116 + 1, topPos + 25 + 13 - progress, 176, 27 - progress, 14, progress + 1); + } + } +} diff --git a/common/src/main/java/toughasnails/client/handler/LevelRenderHandler.java b/common/src/main/java/toughasnails/client/handler/LevelRenderHandler.java index 5a4294b0..ff9303bf 100644 --- a/common/src/main/java/toughasnails/client/handler/LevelRenderHandler.java +++ b/common/src/main/java/toughasnails/client/handler/LevelRenderHandler.java @@ -18,7 +18,10 @@ import net.minecraft.world.phys.Vec3; import org.joml.Matrix3f; import org.joml.Matrix4f; +import toughasnails.api.temperature.ITemperature; import toughasnails.api.temperature.TemperatureHelper; +import toughasnails.block.entity.ThermoregulatorBlockEntity; +import toughasnails.core.ToughAsNails; import toughasnails.temperature.AreaFill; import java.util.Arrays; @@ -77,6 +80,7 @@ private static void populateConnectedBlocks(Player player) Set passablePositions = new HashSet<>(); Set heatingPositions = new HashSet<>(); Set coolingPositions = new HashSet<>(); + Set thermoregulatorPositions = new HashSet<>(); Set blockingPositions = new HashSet<>(); AreaFill.fill(level, playerPos, new AreaFill.PositionChecker() { @Override @@ -84,11 +88,11 @@ public void onSolid(Level level, AreaFill.PosAndDepth pos) { BlockState state = level.getBlockState(pos.pos()); - if (TemperatureHelper.isHeating(state)) + if (TemperatureHelper.isHeatingBlock(state)) { heatingPositions.add(pos.pos()); } - else if (TemperatureHelper.isCooling(state)) + else if (TemperatureHelper.isCoolingBlock(state)) { coolingPositions.add(pos.pos()); } @@ -102,10 +106,22 @@ public void onPassable(Level level, AreaFill.PosAndDepth pos) } }); - connectBlocks(passablePositions, 170, 170, 170, 255); - connectBlocks(heatingPositions, 255, 170, 0, 255); - connectBlocks(coolingPositions, 85, 255, 255, 255); - connectBlocks(blockingPositions, 255, 85, 85, 255); + // Add blocks from thermoregulators + for (BlockPos pos : TemperatureHelper.getTemperatureData(player).getNearbyThermoregulators()) + { + ThermoregulatorBlockEntity blockEntity = (ThermoregulatorBlockEntity)level.getBlockEntity(pos); + + if (blockEntity == null) + continue; + + thermoregulatorPositions.addAll(blockEntity.getFilledBlocks()); + } + + connectBlocks(passablePositions, 170, 170, 170, 255); // Grey + connectBlocks(heatingPositions, 255, 170, 0, 255); // Orange + connectBlocks(coolingPositions, 85, 255, 255, 255); // Blue + connectBlocks(thermoregulatorPositions, 255, 85, 255, 255); // Purple + connectBlocks(blockingPositions, 255, 85, 85, 255); // Red } private static void connectBlocks(Set positions, int r, int g, int b, int a) diff --git a/common/src/main/java/toughasnails/container/ThermoregulatorContainer.java b/common/src/main/java/toughasnails/container/ThermoregulatorContainer.java new file mode 100644 index 00000000..3c50c535 --- /dev/null +++ b/common/src/main/java/toughasnails/container/ThermoregulatorContainer.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright 2021, the Glitchfiend Team. + * All rights reserved. + ******************************************************************************/ +package toughasnails.container; + +import net.minecraft.world.Container; +import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ContainerData; +import net.minecraft.world.inventory.SimpleContainerData; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; +import toughasnails.api.container.TANContainerTypes; +import toughasnails.api.crafting.TANRecipeTypes; +import toughasnails.block.entity.ThermoregulatorBlockEntity; +import toughasnails.block.entity.WaterPurifierBlockEntity; +import toughasnails.crafting.WaterPurifierRecipe; + +public class ThermoregulatorContainer extends AbstractContainerMenu +{ + private final Container container; + private final ContainerData data; + private final Level level; + + public ThermoregulatorContainer(int id, Inventory playerInventory) + { + this(id, playerInventory, new SimpleContainer(2), new SimpleContainerData(2)); + } + + public ThermoregulatorContainer(int id, Inventory playerInventory, Container container, ContainerData data) + { + super(TANContainerTypes.THERMOREGULATOR, id); + this.container = container; + this.data = data; + this.level = playerInventory.player.level(); + + // Add input item slot + this.addSlot(new ThermoregulatorCoolingFuelSlot(container, ThermoregulatorBlockEntity.SLOT_COOLING, 44, 42)); + + // Add filter item slot + this.addSlot(new ThermoregulatorHeatingFuelSlot(container, ThermoregulatorBlockEntity.SLOT_HEATING, 116, 42)); + + // Add inventory slots + for (int row = 0; row < 3; ++row) + { + for (int col = 0; col < 9; ++col) + { + this.addSlot(new Slot(playerInventory, col + row * 9 + 9, 8 + col * 18, 84 + row * 18)); + } + } + + // Add hotbar slots + for (int slot = 0; slot < 9; ++slot) + { + this.addSlot(new Slot(playerInventory, slot, 8 + slot * 18, 142)); + } + + // Add data slots + this.addDataSlots(data); + } + + @Override + public boolean stillValid(Player player) + { + return this.container.stillValid(player); + } + + @Override + public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) + { + ItemStack prevItem = ItemStack.EMPTY; + Slot slot = this.slots.get(index); + if (slot.hasItem()) + { + ItemStack slotItem = slot.getItem(); + prevItem = slotItem.copy(); + + // Output + if (index != ThermoregulatorBlockEntity.SLOT_HEATING && index != ThermoregulatorBlockEntity.SLOT_COOLING) // Moving from inventory to the purifier + { + if (ThermoregulatorBlockEntity.isCoolingFuel(slotItem)) + { + if (!this.moveItemStackTo(slotItem, 0, 1, false)) { + return ItemStack.EMPTY; + } + } + else if (ThermoregulatorBlockEntity.isHeatingFuel(slotItem)) + { + if (!this.moveItemStackTo(slotItem, 1, 2, false)) + { + return ItemStack.EMPTY; + } + } + else if (index >= 2 && index < 30) + { + if (!this.moveItemStackTo(slotItem, 30, 38, false)) + { + return ItemStack.EMPTY; + } + } + else if (index >= 30 && index < 38 && !this.moveItemStackTo(slotItem, 2, 30, false)) { + return ItemStack.EMPTY; + } + } + else if (!this.moveItemStackTo(slotItem, 2, 38, false)) // Move from purifier to inventory + { + return ItemStack.EMPTY; + } + + if (slotItem.isEmpty()) + { + slot.set(ItemStack.EMPTY); + } + else + { + slot.setChanged(); + } + + // No change + if (slotItem.getCount() == prevItem.getCount()) + { + return ItemStack.EMPTY; + } + + slot.onTake(player, slotItem); + } + + return prevItem; + } + + public int getCoolingFuelProgress() + { + return this.data.get(0) * 13 / ThermoregulatorBlockEntity.CONSUMABLE_DURATION; + } + + public int getHeatingFuelProgress() + { + return this.data.get(1) * 13 / ThermoregulatorBlockEntity.CONSUMABLE_DURATION; + } + + public boolean isCooling() + { + return this.data.get(0) > 0; + } + + public boolean isHeating() + { + return this.data.get(1) > 0; + } +} diff --git a/common/src/main/java/toughasnails/container/ThermoregulatorCoolingFuelSlot.java b/common/src/main/java/toughasnails/container/ThermoregulatorCoolingFuelSlot.java new file mode 100644 index 00000000..00ca0bdb --- /dev/null +++ b/common/src/main/java/toughasnails/container/ThermoregulatorCoolingFuelSlot.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright 2021, the Glitchfiend Team. + * All rights reserved. + ******************************************************************************/ +package toughasnails.container; + +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import toughasnails.block.entity.ThermoregulatorBlockEntity; + +public class ThermoregulatorCoolingFuelSlot extends Slot +{ + + public ThermoregulatorCoolingFuelSlot(Container inventory, int index, int xPosition, int yPosition) + { + super(inventory, index, xPosition, yPosition); + } + + @Override + public boolean mayPlace(ItemStack stack) + { + return ThermoregulatorBlockEntity.isCoolingFuel(stack); + } +} diff --git a/common/src/main/java/toughasnails/container/ThermoregulatorHeatingFuelSlot.java b/common/src/main/java/toughasnails/container/ThermoregulatorHeatingFuelSlot.java new file mode 100644 index 00000000..8581fe56 --- /dev/null +++ b/common/src/main/java/toughasnails/container/ThermoregulatorHeatingFuelSlot.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright 2021, the Glitchfiend Team. + * All rights reserved. + ******************************************************************************/ +package toughasnails.container; + +import net.minecraft.world.Container; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import toughasnails.block.entity.ThermoregulatorBlockEntity; + +public class ThermoregulatorHeatingFuelSlot extends Slot +{ + public ThermoregulatorHeatingFuelSlot(Container inventory, int index, int xPosition, int yPosition) + { + super(inventory, index, xPosition, yPosition); + } + + @Override + public boolean mayPlace(ItemStack stack) + { + return ThermoregulatorBlockEntity.isHeatingFuel(stack); + } +} diff --git a/common/src/main/java/toughasnails/init/ModClient.java b/common/src/main/java/toughasnails/init/ModClient.java index 056ddeb6..90f5b1be 100644 --- a/common/src/main/java/toughasnails/init/ModClient.java +++ b/common/src/main/java/toughasnails/init/ModClient.java @@ -6,15 +6,19 @@ import glitchcore.event.client.RegisterColorsEvent; import glitchcore.util.RenderTypeHelper; +import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.item.ClampedItemPropertyFunction; import net.minecraft.client.renderer.item.ItemProperties; +import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import org.jetbrains.annotations.Nullable; import toughasnails.api.item.TANItems; import toughasnails.api.temperature.TemperatureHelper; @@ -22,9 +26,11 @@ import toughasnails.core.ToughAsNails; import toughasnails.item.DyeableWoolItem; import toughasnails.item.LeafArmorItem; +import toughasnails.temperature.TemperatureHelperImpl; import java.util.HashMap; import java.util.Map; +import java.util.Set; import static toughasnails.api.block.TANBlocks.RAIN_COLLECTOR; import static toughasnails.api.block.TANBlocks.WATER_PURIFIER; @@ -51,10 +57,20 @@ public float unclampedCall(ItemStack stack, @Nullable ClientLevel level, @Nullab return 0.5F; Delta delta = deltas.computeIfAbsent(holder.getId(), k -> new Delta()); - delta.update(level, TemperatureHelper.getTemperatureAtPos(level, holder.blockPosition())); + delta.update(level, getTemperatureForThermometer(level, holder)); return delta.getValue(); } + private static TemperatureLevel getTemperatureForThermometer(Level level, Entity holder) + { + TemperatureLevel temperatureLevel = TemperatureHelper.getTemperatureAtPos(level, holder.blockPosition()); + + // Use the player to acquire nearby thermoregulators, even if they aren't holding the thermometer + Player player = Minecraft.getInstance().player; + Set nearbyThermoregulators = TemperatureHelper.getTemperatureData(player).getNearbyThermoregulators(); + return TemperatureHelperImpl.modifyTemperatureByThermoregulators(level, nearbyThermoregulators, holder.blockPosition(), temperatureLevel); + } + private static class Delta { private long lastUpdateTick; diff --git a/common/src/main/java/toughasnails/init/ModContainerTypes.java b/common/src/main/java/toughasnails/init/ModContainerTypes.java index e510dba9..05a2c7df 100644 --- a/common/src/main/java/toughasnails/init/ModContainerTypes.java +++ b/common/src/main/java/toughasnails/init/ModContainerTypes.java @@ -12,7 +12,9 @@ import net.minecraft.world.inventory.MenuType; import toughasnails.api.TANAPI; import toughasnails.api.container.TANContainerTypes; +import toughasnails.client.gui.ThermoregulatorScreen; import toughasnails.client.gui.WaterPurifierScreen; +import toughasnails.container.ThermoregulatorContainer; import toughasnails.container.WaterPurifierContainer; import java.util.function.BiConsumer; @@ -22,10 +24,12 @@ public class ModContainerTypes public static void registerContainers(BiConsumer> func) { TANContainerTypes.WATER_PURIFIER = register(func, "water_purifier", WaterPurifierContainer::new); + TANContainerTypes.THERMOREGULATOR = register(func, "thermoregulator", ThermoregulatorContainer::new); if (Environment.isClient()) { MenuScreens.register((MenuType) TANContainerTypes.WATER_PURIFIER, WaterPurifierScreen::new); + MenuScreens.register((MenuType) TANContainerTypes.THERMOREGULATOR, ThermoregulatorScreen::new); } } diff --git a/common/src/main/java/toughasnails/mixin/MixinServerPlayer.java b/common/src/main/java/toughasnails/mixin/MixinServerPlayer.java index 8b5682bb..4b9f40d7 100644 --- a/common/src/main/java/toughasnails/mixin/MixinServerPlayer.java +++ b/common/src/main/java/toughasnails/mixin/MixinServerPlayer.java @@ -46,7 +46,7 @@ public void onDoTick(CallbackInfo ci) IThirst thirst = ThirstHelper.getThirst(player); // Update the temperature if it has changed - if (data.getLastLevel() != data.getLevel() || data.getLastHyperthermiaTicks() != data.getHyperthermiaTicks()) + if (data.getLastLevel() != data.getLevel() || data.getLastHyperthermiaTicks() != data.getHyperthermiaTicks() || !data.getLastNearbyThermoregulators().equals(data.getNearbyThermoregulators())) { syncTemperature(player); } diff --git a/common/src/main/java/toughasnails/network/UpdateTemperaturePacket.java b/common/src/main/java/toughasnails/network/UpdateTemperaturePacket.java index 6f84d1f4..9696f2c0 100644 --- a/common/src/main/java/toughasnails/network/UpdateTemperaturePacket.java +++ b/common/src/main/java/toughasnails/network/UpdateTemperaturePacket.java @@ -6,21 +6,27 @@ import glitchcore.network.CustomPacket; import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.entity.player.Player; import toughasnails.api.temperature.ITemperature; import toughasnails.api.temperature.TemperatureHelper; import toughasnails.api.temperature.TemperatureLevel; +import java.util.HashSet; +import java.util.Set; + public class UpdateTemperaturePacket implements CustomPacket { private TemperatureLevel temperatureLevel; private int hyperthermiaTicks; + private Set nearbyThermoregulators; - public UpdateTemperaturePacket(TemperatureLevel temperatureLevel, int hyperthermiaTicks) + public UpdateTemperaturePacket(TemperatureLevel temperatureLevel, int hyperthermiaTicks, Set nearbyThermoregulators) { this.temperatureLevel = temperatureLevel; this.hyperthermiaTicks = hyperthermiaTicks; + this.nearbyThermoregulators = nearbyThermoregulators; } public UpdateTemperaturePacket() {} @@ -30,12 +36,13 @@ public void encode(FriendlyByteBuf buf) { buf.writeEnum(this.temperatureLevel); buf.writeInt(this.hyperthermiaTicks); + buf.writeCollection(this.nearbyThermoregulators, FriendlyByteBuf::writeBlockPos); } @Override public UpdateTemperaturePacket decode(FriendlyByteBuf buf) { - return new UpdateTemperaturePacket(buf.readEnum(TemperatureLevel.class), buf.readInt()); + return new UpdateTemperaturePacket(buf.readEnum(TemperatureLevel.class), buf.readInt(), buf.readCollection(HashSet::new, FriendlyByteBuf::readBlockPos)); } @Override @@ -49,6 +56,7 @@ public void handle(UpdateTemperaturePacket packet, Context context) temperature.setLevel(packet.temperatureLevel); temperature.setHyperthermiaTicks(packet.hyperthermiaTicks); + temperature.setNearbyThermoregulators(packet.nearbyThermoregulators); }); } } diff --git a/common/src/main/java/toughasnails/temperature/AreaFill.java b/common/src/main/java/toughasnails/temperature/AreaFill.java index 631026da..c2b6e9b7 100644 --- a/common/src/main/java/toughasnails/temperature/AreaFill.java +++ b/common/src/main/java/toughasnails/temperature/AreaFill.java @@ -10,7 +10,6 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import toughasnails.api.temperature.TemperatureHelper; -import toughasnails.core.ToughAsNails; import toughasnails.init.ModConfig; import java.util.Arrays; @@ -21,12 +20,15 @@ public class AreaFill { public static void fill(Level level, BlockPos pos, PositionChecker checker) + { + fill(level, pos, checker, ModConfig.temperature.nearHeatCoolProximity); + } + + public static void fill(Level level, BlockPos pos, PositionChecker checker, final int maxDepth) { Set checked = Sets.newHashSet(); Queue queue = new LinkedList(); - final int maxDepth = ModConfig.temperature.nearHeatCoolProximity; - queue.add(new PosAndDepth(pos, 1)); while (!queue.isEmpty()) { @@ -115,7 +117,7 @@ default void onPassable(Level level, PosAndDepth pos) { default boolean isPassable(Level level, BlockPos pos) { BlockState state = level.getBlockState(pos); - return state.isAir() || (!isFullySolid(level, pos) && !TemperatureHelper.isHeating(state) && !TemperatureHelper.isCooling(state)); + return state.isAir() || (!isFullySolid(level, pos) && !TemperatureHelper.isHeatingBlock(state) && !TemperatureHelper.isCoolingBlock(state)); } default boolean isFullySolid(Level level, BlockPos pos) diff --git a/common/src/main/java/toughasnails/temperature/BuiltInTemperatureModifier.java b/common/src/main/java/toughasnails/temperature/BuiltInTemperatureModifier.java index 898b8e3c..5536a65c 100644 --- a/common/src/main/java/toughasnails/temperature/BuiltInTemperatureModifier.java +++ b/common/src/main/java/toughasnails/temperature/BuiltInTemperatureModifier.java @@ -22,8 +22,9 @@ public enum BuiltInTemperatureModifier int newChangeDelay = currentChangeDelay; for (IPlayerTemperatureModifier modifier : playerModifiers) { - newTarget = modifier.modify(player, currentTarget); + newTarget = modifier.modify(player, newTarget); } + if (newTarget != currentTarget) newChangeDelay = Math.min(currentChangeDelay, ModConfig.temperature.playerTemperatureChangeDelay); return new Tuple<>(newTarget, newChangeDelay); })), diff --git a/common/src/main/java/toughasnails/temperature/TemperatureData.java b/common/src/main/java/toughasnails/temperature/TemperatureData.java index 2c4bbb9f..2e1a840b 100644 --- a/common/src/main/java/toughasnails/temperature/TemperatureData.java +++ b/common/src/main/java/toughasnails/temperature/TemperatureData.java @@ -4,11 +4,15 @@ ******************************************************************************/ package toughasnails.temperature; +import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import toughasnails.api.temperature.ITemperature; import toughasnails.api.temperature.TemperatureLevel; import toughasnails.init.ModConfig; +import java.util.HashSet; +import java.util.Set; + public class TemperatureData implements ITemperature { public static final TemperatureLevel DEFAULT_LEVEL = TemperatureLevel.NEUTRAL; @@ -20,8 +24,10 @@ public class TemperatureData implements ITemperature private int ticksDry; private int extremityDelayTicks; + private Set nearbyThermoregulators = new HashSet<>(); private TemperatureLevel lastTemperature = DEFAULT_LEVEL; private int lastHyperthermiaTicks; + private Set lastNearbyThermoregulators = new HashSet<>(); public void addAdditionalSaveData(CompoundTag nbt) { @@ -118,6 +124,18 @@ public int getLastHyperthermiaTicks() return this.lastHyperthermiaTicks; } + @Override + public Set getLastNearbyThermoregulators() + { + return this.lastNearbyThermoregulators; + } + + @Override + public Set getNearbyThermoregulators() + { + return this.nearbyThermoregulators; + } + @Override public void setLevel(TemperatureLevel level) { @@ -164,4 +182,16 @@ public void setLastHyperthermiaTicks(int ticks) { this.lastHyperthermiaTicks = ticks; } + + @Override + public void setLastNearbyThermoregulators(Set values) + { + this.lastNearbyThermoregulators = values; + } + + @Override + public void setNearbyThermoregulators(Set values) + { + this.nearbyThermoregulators = values; + } } diff --git a/common/src/main/java/toughasnails/temperature/TemperatureHandler.java b/common/src/main/java/toughasnails/temperature/TemperatureHandler.java index d17c5e08..7f5457a6 100644 --- a/common/src/main/java/toughasnails/temperature/TemperatureHandler.java +++ b/common/src/main/java/toughasnails/temperature/TemperatureHandler.java @@ -6,6 +6,7 @@ import glitchcore.event.entity.LivingEntityUseItemEvent; import glitchcore.event.player.PlayerEvent; +import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.Mth; import net.minecraft.util.Tuple; @@ -16,6 +17,8 @@ import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; import toughasnails.api.damagesource.TANDamageTypes; import toughasnails.api.potion.TANEffects; import toughasnails.api.temperature.ITemperature; @@ -27,6 +30,7 @@ import toughasnails.init.ModTags; import toughasnails.network.UpdateTemperaturePacket; +import java.util.HashSet; import java.util.UUID; public class TemperatureHandler @@ -38,8 +42,12 @@ public static void onPlayerTick(Player player) if (!ModConfig.temperature.enableTemperature || player.level().isClientSide()) return; + Level level = player.level(); ITemperature data = TemperatureHelper.getTemperatureData(player); + // Remove thermoregulators more than 32 blocks away + data.getNearbyThermoregulators().removeIf(pos -> level.getBlockEntity(pos) == null || player.distanceToSqr((double) pos.getX() + 0.5, (double) pos.getY() + 0.5, (double) pos.getZ() + 0.5) > 32 * 32); + // Decrement the positional change delay ticks data.setChangeDelayTicks(Math.max(0, data.getChangeDelayTicks() - 1)); // Increment dry ticks @@ -130,6 +138,7 @@ public static void onChangeDimension(PlayerEvent.ChangeDimension event) ITemperature temperature = TemperatureHelper.getTemperatureData(event.getPlayer()); temperature.setLastLevel(TemperatureData.DEFAULT_LEVEL); temperature.setLastHyperthermiaTicks(0); + temperature.setLastNearbyThermoregulators(new HashSet<>()); } public static void onItemUseFinish(LivingEntityUseItemEvent.Finish event) @@ -150,9 +159,10 @@ public static void onItemUseFinish(LivingEntityUseItemEvent.Finish event) public static void syncTemperature(ServerPlayer player) { ITemperature temperature = TemperatureHelper.getTemperatureData(player); - ModPackets.HANDLER.sendToPlayer(new UpdateTemperaturePacket(temperature.getLevel(), temperature.getHyperthermiaTicks()), player); + ModPackets.HANDLER.sendToPlayer(new UpdateTemperaturePacket(temperature.getLevel(), temperature.getHyperthermiaTicks(), temperature.getNearbyThermoregulators()), player); temperature.setLastLevel(temperature.getLevel()); temperature.setLastHyperthermiaTicks(temperature.getHyperthermiaTicks()); + temperature.setNearbyThermoregulators(temperature.getNearbyThermoregulators()); } private static void removeHeatExhaustion(Player player) diff --git a/common/src/main/java/toughasnails/temperature/TemperatureHelperImpl.java b/common/src/main/java/toughasnails/temperature/TemperatureHelperImpl.java index 9e6ce135..63b60345 100644 --- a/common/src/main/java/toughasnails/temperature/TemperatureHelperImpl.java +++ b/common/src/main/java/toughasnails/temperature/TemperatureHelperImpl.java @@ -24,6 +24,8 @@ import toughasnails.api.potion.TANEffects; import toughasnails.api.temperature.*; import toughasnails.api.temperature.IProximityBlockModifier.Type; +import toughasnails.block.entity.ThermoregulatorBlockEntity; +import toughasnails.core.ToughAsNails; import toughasnails.init.ModConfig; import toughasnails.init.ModTags; @@ -36,7 +38,7 @@ public class TemperatureHelperImpl implements TemperatureHelper.Impl.ITemperatur { protected static List positionalModifiers = Lists.newArrayList(TemperatureHelperImpl::altitudeModifier, TemperatureHelperImpl::rainModifier); protected static List proximityModifiers = new ArrayList<>(); - protected static List playerModifiers = Lists.newArrayList(TemperatureHelperImpl::immersionModifier); + protected static List playerModifiers = Lists.newArrayList(TemperatureHelperImpl::thermoregulatorModifier, TemperatureHelperImpl::immersionModifier); @Override public TemperatureLevel getTemperatureAtPos(Level level, BlockPos pos) @@ -222,11 +224,11 @@ private static void addHeatingOrCooling(Set heating, Set coo { BlockState state = level.getBlockState(pos); - if (TemperatureHelper.isHeating(state)) + if (TemperatureHelper.isHeatingBlock(state)) { heating.add(pos); } - else if (TemperatureHelper.isCooling(state)) + else if (TemperatureHelper.isCoolingBlock(state)) { cooling.add(pos); } @@ -248,6 +250,11 @@ else if(sourceType == Type.COOLING) } } + private static TemperatureLevel thermoregulatorModifier(Player player, TemperatureLevel current) + { + return modifyTemperatureByThermoregulators(player.level(), TemperatureHelper.getTemperatureData(player).getNearbyThermoregulators(), player.blockPosition(), current); + } + private static TemperatureLevel immersionModifier(Player player, TemperatureLevel current) { Level level = player.level(); @@ -347,4 +354,31 @@ private static boolean coldEnoughToSnow(Level level, Holder biome, BlockP { return biome.value().coldEnoughToSnow(pos); } + + public static TemperatureLevel modifyTemperatureByThermoregulators(Level level, Set thermoregulators, BlockPos checkPos, TemperatureLevel current) + { + int coolingCount = 0; + int heatingCount = 0; + int neutralCount = 0; + + for (BlockPos pos : thermoregulators) + { + ThermoregulatorBlockEntity blockEntity = (ThermoregulatorBlockEntity)level.getBlockEntity(pos); + + if (blockEntity == null) + continue; + + switch (blockEntity.getEffectAtPos(checkPos)) + { + case COOLING -> ++coolingCount; + case HEATING -> ++heatingCount; + case NEUTRALIZING -> ++neutralCount; + } + } + + if (coolingCount == 0 && heatingCount == 0 && neutralCount > 0) return TemperatureLevel.NEUTRAL; + else if (coolingCount > heatingCount) return current.decrement(2); + else if (heatingCount > coolingCount) return current.increment(2); + else return current; + } } diff --git a/common/src/main/resources/assets/toughasnails/textures/gui/container/thermoregulator.png b/common/src/main/resources/assets/toughasnails/textures/gui/container/thermoregulator.png index 102cceb7..822401a7 100644 Binary files a/common/src/main/resources/assets/toughasnails/textures/gui/container/thermoregulator.png and b/common/src/main/resources/assets/toughasnails/textures/gui/container/thermoregulator.png differ diff --git a/forge/src/main/java/toughasnails/forge/mixin/MixinThermoregulatorBlockEntity.java b/forge/src/main/java/toughasnails/forge/mixin/MixinThermoregulatorBlockEntity.java new file mode 100644 index 00000000..a5693465 --- /dev/null +++ b/forge/src/main/java/toughasnails/forge/mixin/MixinThermoregulatorBlockEntity.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2023, the Glitchfiend Team. + * All rights reserved. + ******************************************************************************/ +package toughasnails.forge.mixin; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.WorldlyContainer; +import net.minecraft.world.level.block.entity.BaseContainerBlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.wrapper.SidedInvWrapper; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import toughasnails.block.entity.ThermoregulatorBlockEntity; +import toughasnails.block.entity.WaterPurifierBlockEntity; + +@Mixin(value = ThermoregulatorBlockEntity.class) +public abstract class MixinThermoregulatorBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer { + protected MixinThermoregulatorBlockEntity(BlockEntityType p_155076_, BlockPos p_155077_, BlockState p_155078_) { + super(p_155076_, p_155077_, p_155078_); + } + + @Unique + LazyOptional[] handlers; + + @Inject(method="", at=@At("RETURN")) + public void onConstructed(BlockPos pos, BlockState state, CallbackInfo ci) + { + this.handlers = SidedInvWrapper.create(this, Direction.UP, Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST); + } + + @Override + public LazyOptional getCapability(Capability capability, @Nullable Direction facing) + { + if (!this.remove && facing != null && capability == ForgeCapabilities.ITEM_HANDLER) { + if (facing == Direction.UP) + return handlers[0].cast(); + else if (facing == Direction.DOWN) + return handlers[1].cast(); + else + return handlers[2].cast(); + } + return super.getCapability(capability, facing); + } + + @Override + public void invalidateCaps() + { + super.invalidateCaps(); + for (int x = 0; x < handlers.length; x++) + handlers[x].invalidate(); + } + + @Override + public void reviveCaps() + { + super.reviveCaps(); + this.handlers = SidedInvWrapper.create(this, Direction.UP, Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST); + } +} diff --git a/forge/src/main/resources/toughasnails.forge.mixins.json b/forge/src/main/resources/toughasnails.forge.mixins.json index c538da46..3a33bf4a 100644 --- a/forge/src/main/resources/toughasnails.forge.mixins.json +++ b/forge/src/main/resources/toughasnails.forge.mixins.json @@ -5,6 +5,7 @@ "refmap": "toughasnails.refmap.json", "mixins": [ "MixinTemperatureHelperImpl", + "MixinThermoregulatorBlockEntity", "MixinWaterPurifierBlockEntity" ], "client": [