diff --git a/changelog.md b/changelog.md index e3b3314e4b..3ef38cdf24 100644 --- a/changelog.md +++ b/changelog.md @@ -128,6 +128,7 @@ _Now using Flywheel 1.0_ - Fixed stations voiding schedules when disassembling the train - Fixed lighting on signal block indicators - Fixed vaults and tanks rotated in place not updating their multiblock correctly +- Hose pulley now deletes lilypads and other surface foliage #### API Changes diff --git a/gradle.properties b/gradle.properties index 2da9e7c4b2..4cf77839d5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,7 +23,7 @@ registrate_version = MC1.21-1.3.0+62 # Dependency Versions flywheel_minecraft_version = 1.21.1 -flywheel_version = 1.0.0-beta-4 +flywheel_version = 1.0.0-beta-6 flywheel_version_range = [1.0.0-alpha,2.0) ponder_version = 1.0.32 jei_minecraft_version = 1.21.1 diff --git a/src/generated/resources/data/create/recipes/crushing/compat/immersiveengineering/coal_coke.json b/src/generated/resources/data/create/recipes/crushing/compat/immersiveengineering/coal_coke.json new file mode 100644 index 0000000000..13bd0f682f --- /dev/null +++ b/src/generated/resources/data/create/recipes/crushing/compat/immersiveengineering/coal_coke.json @@ -0,0 +1,20 @@ +{ + "type": "create:crushing", + "conditions": [ + { + "type": "forge:mod_loaded", + "modid": "immersiveengineering" + } + ], + "ingredients": [ + { + "item": "immersiveengineering:coal_coke" + } + ], + "processingTime": 200, + "results": [ + { + "item": "immersiveengineering:dust_coke" + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/create/recipes/crushing/compat/immersiveengineering/coke_block.json b/src/generated/resources/data/create/recipes/crushing/compat/immersiveengineering/coke_block.json new file mode 100644 index 0000000000..b20f9375c9 --- /dev/null +++ b/src/generated/resources/data/create/recipes/crushing/compat/immersiveengineering/coke_block.json @@ -0,0 +1,21 @@ +{ + "type": "create:crushing", + "conditions": [ + { + "type": "forge:mod_loaded", + "modid": "immersiveengineering" + } + ], + "ingredients": [ + { + "item": "immersiveengineering:coke" + } + ], + "processingTime": 200, + "results": [ + { + "count": 9, + "item": "immersiveengineering:dust_coke" + } + ] +} \ No newline at end of file diff --git a/src/generated/resources/data/create/recipes/crushing/compat/immersiveengineering/slag.json b/src/generated/resources/data/create/recipes/crushing/compat/immersiveengineering/slag.json new file mode 100644 index 0000000000..bb6ea9091f --- /dev/null +++ b/src/generated/resources/data/create/recipes/crushing/compat/immersiveengineering/slag.json @@ -0,0 +1,20 @@ +{ + "type": "create:crushing", + "conditions": [ + { + "type": "forge:mod_loaded", + "modid": "immersiveengineering" + } + ], + "ingredients": [ + { + "item": "immersiveengineering:slag" + } + ], + "processingTime": 200, + "results": [ + { + "item": "immersiveengineering:slag_gravel" + } + ] +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/compat/Mods.java b/src/main/java/com/simibubi/create/compat/Mods.java index abcbe7d24f..7ecc605730 100644 --- a/src/main/java/com/simibubi/create/compat/Mods.java +++ b/src/main/java/com/simibubi/create/compat/Mods.java @@ -21,7 +21,6 @@ public enum Mods { AETHER_II, BETTEREND, COMPUTERCRAFT, - CONNECTIVITY, CURIOS, DYNAMICTREES, FUNCTIONALSTORAGE, diff --git a/src/main/java/com/simibubi/create/compat/trainmap/FTBChunksTrainMap.java b/src/main/java/com/simibubi/create/compat/trainmap/FTBChunksTrainMap.java index f74b55c82d..16783553cc 100644 --- a/src/main/java/com/simibubi/create/compat/trainmap/FTBChunksTrainMap.java +++ b/src/main/java/com/simibubi/create/compat/trainmap/FTBChunksTrainMap.java @@ -9,6 +9,7 @@ import com.simibubi.create.infrastructure.config.AllConfigs; import dev.ftb.mods.ftbchunks.client.gui.LargeMapScreen; import dev.ftb.mods.ftbchunks.client.gui.RegionMapPanel; +import dev.ftb.mods.ftblibrary.ui.BaseScreen; import dev.ftb.mods.ftblibrary.ui.ScreenWrapper; import dev.ftb.mods.ftblibrary.ui.Widget; import net.minecraft.client.Minecraft; @@ -152,7 +153,7 @@ public class FTBChunksTrainMap { private static LargeMapScreen getAsLargeMapScreen(Screen screen) { if (!(screen instanceof ScreenWrapper screenWrapper)) return null; - Object wrapped = ObfuscationReflectionHelper.getPrivateValue(ScreenWrapper.class, screenWrapper, "wrappedGui"); + BaseScreen wrapped = screenWrapper.getGui(); if (!(wrapped instanceof LargeMapScreen largeMapScreen)) return null; return largeMapScreen; diff --git a/src/main/java/com/simibubi/create/content/contraptions/AbstractContraptionEntity.java b/src/main/java/com/simibubi/create/content/contraptions/AbstractContraptionEntity.java index 22ae06b46a..0bb78dcad2 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/AbstractContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/AbstractContraptionEntity.java @@ -18,13 +18,13 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.simibubi.create.AllItems; import com.simibubi.create.AllMovementBehaviours; import com.simibubi.create.AllSoundEvents; -import com.simibubi.create.Create; import com.simibubi.create.content.contraptions.actors.psi.PortableStorageInterfaceMovement; import com.simibubi.create.content.contraptions.actors.seat.SeatBlock; import com.simibubi.create.content.contraptions.actors.seat.SeatEntity; import com.simibubi.create.content.contraptions.actors.trainControls.ControlsStopControllingPacket; import com.simibubi.create.content.contraptions.behaviour.MovementBehaviour; import com.simibubi.create.content.contraptions.behaviour.MovementContext; +import com.simibubi.create.content.contraptions.data.ContraptionSyncLimiting; import com.simibubi.create.content.contraptions.elevator.ElevatorContraption; import com.simibubi.create.content.contraptions.glue.SuperGlueEntity; import com.simibubi.create.content.contraptions.mounted.MountedContraption; @@ -605,11 +605,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit CompoundTag compound = new CompoundTag(); writeAdditional(compound, registryFriendlyByteBuf.registryAccess(), true); - if (!CatnipServices.PLATFORM.getLoader().isNeoForge() && ContraptionData.isTooLargeForSync(compound)) { - String info = getContraption().getType().id + " @" + position() + " (" + getStringUUID() + ")"; - Create.LOGGER.warn("Could not send Contraption Spawn Data (Packet too big): {}", info); - compound = null; - } + if (!CatnipServices.PLATFORM.getLoader().isNeoForge() && ContraptionSyncLimiting.isTooLargeForSync(compound)) + compound = null; // don't sync contraption data registryFriendlyByteBuf.writeNbt(compound); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/Contraption.java index c2e4e715ad..601b3d71d7 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/Contraption.java @@ -92,6 +92,7 @@ import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; import net.minecraft.network.protocol.game.DebugPackets; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.ai.village.poi.PoiTypes; @@ -107,6 +108,7 @@ import net.minecraft.world.level.block.PressurePlateBlock; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.SimpleWaterloggedBlock; 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.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.ChestType; @@ -139,6 +141,7 @@ public abstract class Contraption { public boolean disassembled; protected Map blocks; + protected Map updateTags; protected List> actors; protected Map interactors; protected List disabledActors; @@ -166,6 +169,7 @@ public abstract class Contraption { public Contraption() { blocks = new HashMap<>(); + updateTags = new HashMap<>(); seats = new ArrayList<>(); actors = new ArrayList<>(); disabledActors = new ArrayList<>(); @@ -646,6 +650,15 @@ public abstract class Contraption { bounds = bounds.minmax(new AABB(localPos)); BlockEntity be = pair.getValue(); + + if (be != null) { + CompoundTag updateTag = be.getUpdateTag(level.registryAccess()); + // the ID needs to be in the tag so the client can properly add the BlockEntity + ResourceLocation id = Objects.requireNonNull(BlockEntityType.getKey(be.getType())); + updateTag.putString("id", id.toString()); + updateTags.put(localPos, updateTag); + } + storage.addBlock(level, state, pos, localPos, be); captureMultiblock(localPos, structureBlockInfo, be); @@ -793,7 +806,7 @@ public abstract class Contraption { CompoundTag nbt = new CompoundTag(); nbt.putString("Type", getType().id); - CompoundTag blocksNBT = writeBlocksCompound(); + CompoundTag blocksNBT = writeBlocksCompound(spawnPacket); ListTag multiblocksNBT = new ListTag(); capturedMultiblocks.keySet().forEach(controllerPos -> { @@ -886,7 +899,7 @@ public abstract class Contraption { storage.write(nbt, registries, spawnPacket); } - private CompoundTag writeBlocksCompound() { + private CompoundTag writeBlocksCompound(boolean spawnPacket) { CompoundTag compound = new CompoundTag(); HashMapPalette palette = new HashMapPalette<>(GameData.getBlockStateIDMap(), 16, (i, s) -> { throw new IllegalStateException("Palette Map index exceeded maximum"); @@ -895,11 +908,30 @@ public abstract class Contraption { for (StructureBlockInfo block : this.blocks.values()) { int id = palette.idFor(block.state()); + BlockPos pos = block.pos(); CompoundTag c = new CompoundTag(); - c.putLong("Pos", block.pos().asLong()); + c.putLong("Pos", pos.asLong()); c.putInt("State", id); - if (block.nbt() != null) - c.put("Data", block.nbt()); + + CompoundTag updateTag = updateTags.get(pos); + if (spawnPacket) { + // for client sync, treat the updateTag as the data + if (updateTag != null) { + c.put("Data", updateTag); + } + // legacy: use full data if update tag is not available + if (updateTag == null && block.nbt() != null) { + c.put("Data", block.nbt()); + } + } else { + // otherwise, write actual data as the data, save updateTag on its own + if (block.nbt() != null) { + c.put("Data", block.nbt()); + } + if (updateTag != null) { + c.put("UpdateTag", updateTag); + } + } blockList.add(c); } @@ -942,6 +974,13 @@ public abstract class Contraption { this.blocks.put(info.pos(), info); + if (c.contains("UpdateTag", Tag.TAG_COMPOUND)) { + CompoundTag updateTag = c.getCompound("UpdateTag"); + if (!updateTag.isEmpty()) { + this.updateTags.put(info.pos(), updateTag); + } + } + if (!world.isClientSide) return; diff --git a/src/main/java/com/simibubi/create/content/contraptions/ContraptionData.java b/src/main/java/com/simibubi/create/content/contraptions/ContraptionData.java deleted file mode 100644 index fbd3a2efdb..0000000000 --- a/src/main/java/com/simibubi/create/content/contraptions/ContraptionData.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.simibubi.create.content.contraptions; - -import com.simibubi.create.compat.Mods; -import com.simibubi.create.foundation.mixin.accessor.NbtAccounterAccessor; -import com.simibubi.create.infrastructure.config.AllConfigs; - -import io.netty.buffer.Unpooled; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtAccounter; -import net.minecraft.nbt.Tag; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; - -public class ContraptionData { - /** - * A sane, default maximum for contraption data size. - */ - public static final int DEFAULT_LIMIT = 2_000_000; - /** - * Connectivity expands the NBT packet limit to 2 GB. - */ - public static final int CONNECTIVITY_LIMIT = Integer.MAX_VALUE; - /** - * Packet Fixer expands the NBT packet limit to 200 MB. - */ - public static final int PACKET_FIXER_LIMIT = 209_715_200; - /** - * XL Packets expands the NBT packet limit to 2 GB. - */ - public static final int XL_PACKETS_LIMIT = 2_000_000_000; - /** - * Minecart item sizes are limited by the vanilla slot change packet ({@link ClientboundContainerSetSlotPacket}). - * {@link #DEFAULT_LIMIT} is used as the default. - * Connectivity, PacketFixer, and XL Packets expand the size limit. - * If one of these mods is loaded, we take advantage of it and use the higher limit. - */ - public static final int PICKUP_LIMIT; - - static { - int limit = DEFAULT_LIMIT; - - // Check from largest to smallest to use the smallest limit if multiple mods are loaded. - // It is necessary to use the smallest limit because even if multiple mods are loaded, - // not all of their mixins may be applied. Therefore, it is safest to only assume that - // the mod with the smallest limit is actually active. - if (Mods.CONNECTIVITY.isLoaded()) { - limit = CONNECTIVITY_LIMIT; - } - if (Mods.XLPACKETS.isLoaded()) { - limit = XL_PACKETS_LIMIT; - } - if (Mods.PACKETFIXER.isLoaded()) { - limit = PACKET_FIXER_LIMIT; - } - - PICKUP_LIMIT = limit; - } - - /** - * @return true if the given NBT is too large for a contraption to be synced to clients. - */ - public static boolean isTooLargeForSync(CompoundTag data) { - int max = AllConfigs.server().kinetics.maxDataSize.get(); - return max != 0 && packetSize(data) > max; - } - - /** - * @return true if the given NBT is too large for a contraption to be picked up with a wrench. - */ - public static boolean isTooLargeForPickup(Tag data) { - return packetSize(data) > PICKUP_LIMIT; - } - - /** - * @return the size of the given NBT when put through a packet, in bytes. - */ - public static long packetSize(Tag data) { - FriendlyByteBuf test = new FriendlyByteBuf(Unpooled.buffer()); - test.writeNbt(data); - NbtAccounter sizeTracker = NbtAccounter.unlimitedHeap(); - test.readNbt(sizeTracker); - long size = ((NbtAccounterAccessor) sizeTracker).create$getUsage(); - test.release(); - return size; - } -} diff --git a/src/main/java/com/simibubi/create/content/contraptions/data/ContraptionPickupLimiting.java b/src/main/java/com/simibubi/create/content/contraptions/data/ContraptionPickupLimiting.java new file mode 100644 index 0000000000..a862e443d4 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/data/ContraptionPickupLimiting.java @@ -0,0 +1,55 @@ +package com.simibubi.create.content.contraptions.data; + +import com.simibubi.create.compat.Mods; +import com.simibubi.create.foundation.mixin.accessor.NbtAccounterAccessor; + +import io.netty.buffer.Unpooled; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtAccounter; +import net.minecraft.network.FriendlyByteBuf; + +public class ContraptionPickupLimiting { + /// The default NBT limit, defined by {@link FriendlyByteBuf#readNbt()}. + public static final int NBT_LIMIT = 2_097_152; + + // increased nbt limits provided by other mods. + public static final int PACKET_FIXER_LIMIT = NBT_LIMIT * 100; + public static final int XL_PACKETS_LIMIT = Integer.MAX_VALUE; + + // leave some space for the rest of the packet. + public static final int BUFFER = 20_000; + + // the actual limit to be used + public static final int LIMIT = Util.make(() -> { + // the smallest limit needs to be used, as we can't guarantee that all mixins are applied if multiple are present. + if (Mods.PACKETFIXER.isLoaded()) { + return PACKET_FIXER_LIMIT; + } else if (Mods.XLPACKETS.isLoaded()) { + return XL_PACKETS_LIMIT; + } + + // none are present, use vanilla default + return NBT_LIMIT; + }) - BUFFER; + + /** + * @return true if the given NBT is too large for a contraption to be picked up with a wrench. + */ + public static boolean isTooLargeForPickup(CompoundTag data) { + return nbtSize(data) > LIMIT; + } + + /** + * @return the size of the given NBT when read by the client according to {@link NbtAccounter} + */ + private static long nbtSize(CompoundTag data) { + FriendlyByteBuf test = new FriendlyByteBuf(Unpooled.buffer()); + test.writeNbt(data); + NbtAccounter sizeTracker = new NbtAccounter(Long.MAX_VALUE); + test.readNbt(sizeTracker); + long size = ((NbtAccounterAccessor) sizeTracker).create$getUsage(); + test.release(); + return size; + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/data/ContraptionSyncLimiting.java b/src/main/java/com/simibubi/create/content/contraptions/data/ContraptionSyncLimiting.java new file mode 100644 index 0000000000..372a30a529 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/data/ContraptionSyncLimiting.java @@ -0,0 +1,52 @@ +package com.simibubi.create.content.contraptions.data; + +import com.simibubi.create.compat.Mods; + +import io.netty.buffer.Unpooled; +import net.minecraft.Util; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; + +public class ContraptionSyncLimiting { + /** + * Contraption entity sync is limited by the clientbound custom payload limit, since that's what Forge's + * extended spawn packet uses. The NBT limit is irrelevant since it's bypassed on deserialization. + */ + public static final int SIZE_LIMIT = 1_048_576; + + // increased packet limits provided by other mods. + public static final int PACKET_FIXER_LIMIT = SIZE_LIMIT * 100; + public static final int XL_PACKETS_LIMIT = Integer.MAX_VALUE; + + // leave some room for the rest of the packet. + public static final int BUFFER = 20_000; + + // the actual limit to be used + public static final int LIMIT = Util.make(() -> { + // the smallest limit needs to be used, as we can't guarantee that all mixins are applied if multiple are present. + if (Mods.PACKETFIXER.isLoaded()) { + return PACKET_FIXER_LIMIT; + } else if (Mods.XLPACKETS.isLoaded()) { + return XL_PACKETS_LIMIT; + } + + // none are present, use vanilla default + return SIZE_LIMIT; + }) - BUFFER; + + /** + * @return true if the given NBT is too large for a contraption to be synced to clients. + */ + public static boolean isTooLargeForSync(CompoundTag data) { + return byteSize(data) > LIMIT; + } + + /** + * @return the size of the given NBT when encoded, in bytes + */ + private static long byteSize(CompoundTag data) { + FriendlyByteBuf test = new FriendlyByteBuf(Unpooled.buffer()); + test.writeNbt(data); + return test.writerIndex(); + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/data/README.md b/src/main/java/com/simibubi/create/content/contraptions/data/README.md new file mode 100644 index 0000000000..7bae5dbf77 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/data/README.md @@ -0,0 +1,37 @@ +# Data Limiting +This pair of classes prevents clients from getting chunkbanned when contraptions are too large. + +This information is up-to-date as of 1.20.1. + +There's a few different packet limits in play: +- the NBT limit: `2_097_152`, enforced by `FriendlyByteBuf.readNbt()` +- the clientbound custom payload limit: `1_048_576` bytes, applies to `ClientboundCustomPayloadPacket` +- the serverbound custom payload limit: `32767` bytes, applies to `ServerboundCustomPayloadPacket` +- the packet limit: `8_388_608` bytes, applies to all packets + +# NBT Size vs Bytes +There's two units in play as well - NBT Size and Bytes. The NBT limit uses NBT size, while the other +three use bytes. I'm (TropheusJ) not sure what exactly an NBT Size unit is - it's not bits, but it's +close. + +Because of this discrepancy, the NBT limit is actually much lower than it seems. It will usually be +the first limit hit. + +Bytes are found by writing a tag to a buffer and getting its `writerIndex`. NBT Size is found using +an `NbtAccounter`. + +# Sync +Sync is pretty straightforward. + +The only limit relevant here is the clientbound custom payload limit. The NBT limit would be relevant, but +client-side deserialization bypasses it. + +Sync is much less of an issue compared to pickup, since a lot of data can be skipped when syncing. + +# Pickup +Two limits are relevant for pickup: the NBT limit and the packet limit. + +The NBT limit is hit way sooner, and is usually the limiting factor. Other mods may increase it, in +which case the packet limit may become relevant. + +The custom payload limit is not relevant since item sync goes through the vanilla `ClientboundContainerSetSlotPacket`. diff --git a/src/main/java/com/simibubi/create/content/contraptions/mounted/MinecartContraptionItem.java b/src/main/java/com/simibubi/create/content/contraptions/mounted/MinecartContraptionItem.java index 60b6f6afc4..b3e96b4bc1 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/mounted/MinecartContraptionItem.java +++ b/src/main/java/com/simibubi/create/content/contraptions/mounted/MinecartContraptionItem.java @@ -11,11 +11,11 @@ import com.simibubi.create.AllItems; import com.simibubi.create.AllMovementBehaviours; import com.simibubi.create.content.contraptions.AbstractContraptionEntity; import com.simibubi.create.content.contraptions.Contraption; -import com.simibubi.create.content.contraptions.ContraptionData; import com.simibubi.create.content.contraptions.ContraptionMovementSetting; import com.simibubi.create.content.contraptions.OrientedContraptionEntity; import com.simibubi.create.content.contraptions.actors.psi.PortableStorageInterfaceMovement; import com.simibubi.create.content.contraptions.behaviour.MovementContext; +import com.simibubi.create.content.contraptions.data.ContraptionPickupLimiting; import com.simibubi.create.content.kinetics.deployer.DeployerFakePlayer; import com.simibubi.create.foundation.advancement.AllAdvancements; import com.simibubi.create.foundation.utility.CreateLang; @@ -253,7 +253,7 @@ public class MinecartContraptionItem extends Item { ItemStack generatedStack = create(type, oce); generatedStack.set(DataComponents.CUSTOM_NAME, entity.getCustomName()); - if (ContraptionData.isTooLargeForPickup(generatedStack.saveOptional(event.getLevel().registryAccess()))) { + if (ContraptionPickupLimiting.isTooLargeForPickup(generatedStack.saveOptional(event.getLevel().registryAccess()))) { MutableComponent message = CreateLang.translateDirect("contraption.minecart_contraption_too_big") .withStyle(ChatFormatting.RED); player.displayClientMessage(message, true); diff --git a/src/main/java/com/simibubi/create/content/contraptions/wrench/RadialWrenchMenu.java b/src/main/java/com/simibubi/create/content/contraptions/wrench/RadialWrenchMenu.java index 2093334c4b..011a5d2513 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/wrench/RadialWrenchMenu.java +++ b/src/main/java/com/simibubi/create/content/contraptions/wrench/RadialWrenchMenu.java @@ -174,7 +174,8 @@ public class RadialWrenchMenu extends AbstractSimiScreen { @Override public void tick() { ticksOpen++; - + if (!level.getBlockState(pos).is(state.getBlock())) + Minecraft.getInstance().setScreen(null); super.tick(); } @@ -185,8 +186,6 @@ public class RadialWrenchMenu extends AbstractSimiScreen { PoseStack ms = graphics.pose(); - LocalPlayer player = Minecraft.getInstance().player; - ms.pushPose(); ms.translate(x, y, 0); @@ -272,6 +271,8 @@ public class RadialWrenchMenu extends AbstractSimiScreen { .translateY(-(sectorWidth / 2f + innerRadius)) .rotateZDegrees(-i * sectorAngle); + poseStack.translate(0, 0, 100); + try { GuiGameElement.of(blockState, blockEntity) .rotateBlock(player.getXRot(), player.getYRot() + 180, 0f) diff --git a/src/main/java/com/simibubi/create/content/contraptions/wrench/RadialWrenchMenuSubmitPacket.java b/src/main/java/com/simibubi/create/content/contraptions/wrench/RadialWrenchMenuSubmitPacket.java index 5d77c2ab71..d9904f23e8 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/wrench/RadialWrenchMenuSubmitPacket.java +++ b/src/main/java/com/simibubi/create/content/contraptions/wrench/RadialWrenchMenuSubmitPacket.java @@ -29,6 +29,9 @@ public record RadialWrenchMenuSubmitPacket(BlockPos blockPos, BlockState newStat @Override public void handle(ServerPlayer player) { Level level = player.level(); + + if (!level.getBlockState(blockPos).is(newState.getBlock())) + return; BlockState updatedState = Block.updateFromNeighbourShapes(newState, level, blockPos); KineticBlockEntity.switchToBlockState(level, blockPos, updatedState); diff --git a/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatModel.java b/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatModel.java index f052cfa477..f1ba96bec0 100644 --- a/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatModel.java +++ b/src/main/java/com/simibubi/create/content/decoration/copycat/CopycatModel.java @@ -81,6 +81,12 @@ public abstract class CopycatModel extends BakedModelWrapperWithData { occlusionData.occlude(face); } } + + @Override + public List getQuads(BlockState state, Direction side, RandomSource rand) { + return getCroppedQuads(state, side, rand, getMaterial(ModelData.EMPTY), ModelData.EMPTY, + RenderType.cutoutMipped()); + } @Override public List getQuads(BlockState state, Direction side, RandomSource rand, ModelData data, RenderType renderType) { diff --git a/src/main/java/com/simibubi/create/content/fluids/transfer/FluidDrainingBehaviour.java b/src/main/java/com/simibubi/create/content/fluids/transfer/FluidDrainingBehaviour.java index 97e8de8560..3ac64c4a38 100644 --- a/src/main/java/com/simibubi/create/content/fluids/transfer/FluidDrainingBehaviour.java +++ b/src/main/java/com/simibubi/create/content/fluids/transfer/FluidDrainingBehaviour.java @@ -149,8 +149,14 @@ public class FluidDrainingBehaviour extends FluidManipulationBehaviour { playEffect(world, currentPos, fluid, true); blockEntity.award(AllAdvancements.HOSE_PULLEY); - if (!blockEntity.isVirtual()) + if (!blockEntity.isVirtual()) { world.setBlock(currentPos, emptied, 2 | 16); + + BlockState stateAbove = world.getBlockState(currentPos.above()); + if (stateAbove.getFluidState() + .getType() == Fluids.EMPTY && !stateAbove.canSurvive(world, currentPos.above())) + world.setBlock(currentPos.above(), Blocks.AIR.defaultBlockState(), 2 | 16); + } affectedArea = BBHelper.encapsulate(affectedArea, currentPos); queue.dequeue(); diff --git a/src/main/java/com/simibubi/create/content/kinetics/drill/DrillBlockEntity.java b/src/main/java/com/simibubi/create/content/kinetics/drill/DrillBlockEntity.java index fd258f1bc6..d2ccea0a05 100644 --- a/src/main/java/com/simibubi/create/content/kinetics/drill/DrillBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/kinetics/drill/DrillBlockEntity.java @@ -3,6 +3,7 @@ package com.simibubi.create.content.kinetics.drill; import com.simibubi.create.content.kinetics.base.BlockBreakingKineticBlockEntity; import com.simibubi.create.content.kinetics.belt.behaviour.DirectBeltInputBehaviour; import com.simibubi.create.content.kinetics.drill.CobbleGenOptimisation.CobbleGenBlockConfiguration; +import com.simibubi.create.content.logistics.chute.ChuteBlockEntity; import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour; import net.minecraft.core.BlockPos; @@ -41,6 +42,15 @@ public class DrillBlockEntity extends BlockBreakingKineticBlockEntity { } public boolean optimiseCobbleGen(BlockState stateToBreak) { + DirectBeltInputBehaviour inv = + BlockEntityBehaviour.get(level, breakingPos.below(), DirectBeltInputBehaviour.TYPE); + BlockEntity blockEntityBelow = level.getBlockEntity(breakingPos.below()); + BlockEntity blockEntityAbove = level.getBlockEntity(breakingPos.above()); + + if (inv == null && !(blockEntityBelow instanceof HopperBlockEntity) + && !(blockEntityAbove instanceof ChuteBlockEntity chute && chute.getItemMotion() > 0)) + return false; + CobbleGenBlockConfiguration config = CobbleGenOptimisation.getConfig(level, worldPosition, getBlockState().getValue(DrillBlock.FACING)); if (config == null) @@ -57,20 +67,19 @@ public class DrillBlockEntity extends BlockBreakingKineticBlockEntity { if (currentOutput.isAir() || !currentOutput.equals(stateToBreak)) return false; - DirectBeltInputBehaviour inv = - BlockEntityBehaviour.get(level, breakingPos.below(), DirectBeltInputBehaviour.TYPE); - if (inv != null) for (ItemStack stack : Block.getDrops(stateToBreak, sl, breakingPos, null)) inv.handleInsertion(stack, Direction.UP, false); - else { - BlockEntity blockEntity = level.getBlockEntity(breakingPos.below()); - if (blockEntity instanceof HopperBlockEntity hbe) { - IItemHandler handler = level.getCapability(ItemHandler.BLOCK, hbe.getBlockPos(), null); - if (handler != null) - for (ItemStack stack : Block.getDrops(stateToBreak, sl, breakingPos, null)) - ItemHandlerHelper.insertItemStacked(handler, stack, false); - } + else if (blockEntityBelow instanceof HopperBlockEntity hbe) { + IItemHandler handler = level.getCapability(ItemHandler.BLOCK, hbe.getBlockPos(), null); + if (handler != null) + for (ItemStack stack : Block.getDrops(stateToBreak, sl, breakingPos, null)) + ItemHandlerHelper.insertItemStacked(handler, stack, false); + } else if (blockEntityAbove instanceof ChuteBlockEntity chute && chute.getItemMotion() > 0) { + for (ItemStack stack : Block.getDrops(stateToBreak, sl, breakingPos, null)) + if (chute.getItem() + .isEmpty()) + chute.setItem(stack, 0); } level.levelEvent(2001, breakingPos, Block.getId(stateToBreak)); diff --git a/src/main/java/com/simibubi/create/content/kinetics/steamEngine/SteamEngineVisual.java b/src/main/java/com/simibubi/create/content/kinetics/steamEngine/SteamEngineVisual.java index a6618ceaf9..8598932c4f 100644 --- a/src/main/java/com/simibubi/create/content/kinetics/steamEngine/SteamEngineVisual.java +++ b/src/main/java/com/simibubi/create/content/kinetics/steamEngine/SteamEngineVisual.java @@ -26,6 +26,7 @@ public class SteamEngineVisual extends AbstractBlockEntityVisual r.value().getSerializer() == RecipeSerializer.SHAPED_RECIPE - || r.value().getSerializer() == RecipeSerializer.SHAPELESS_RECIPE) .filter(r -> output.getItem() == r.value().getResultItem(level.registryAccess()) .getItem()) .filter(r -> { diff --git a/src/main/java/com/simibubi/create/content/logistics/packager/PackagerBlockEntity.java b/src/main/java/com/simibubi/create/content/logistics/packager/PackagerBlockEntity.java index ac1388144e..8405d75915 100644 --- a/src/main/java/com/simibubi/create/content/logistics/packager/PackagerBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/packager/PackagerBlockEntity.java @@ -167,6 +167,10 @@ public class PackagerBlockEntity extends SmartBlockEntity { } public InventorySummary getAvailableItems() { + return getAvailableItems(false); + } + + public InventorySummary getAvailableItems(boolean scanInputSlots) { if (availableItems != null && invVersionTracker.stillWaiting(targetInventory.getInventory())) return availableItems; @@ -186,9 +190,7 @@ public class PackagerBlockEntity extends SmartBlockEntity { for (int slot = 0; slot < targetInv.getSlots(); slot++) { int slotLimit = targetInv.getSlotLimit(slot); - @NotNull - ItemStack extractItem = targetInv.extractItem(slot, slotLimit, true); - availableItems.add(extractItem); + availableItems.add(scanInputSlots ? targetInv.getStackInSlot(slot) : targetInv.extractItem(slot, slotLimit, true)); } invVersionTracker.awaitNewVersion(targetInventory.getInventory()); diff --git a/src/main/java/com/simibubi/create/content/logistics/vault/ItemVaultBlockEntity.java b/src/main/java/com/simibubi/create/content/logistics/vault/ItemVaultBlockEntity.java index ba076910c8..2e72b46811 100644 --- a/src/main/java/com/simibubi/create/content/logistics/vault/ItemVaultBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/vault/ItemVaultBlockEntity.java @@ -50,6 +50,7 @@ public class ItemVaultBlockEntity extends SmartBlockEntity implements IMultiBloc protected void onContentsChanged(int slot) { super.onContentsChanged(slot); updateComparators(); + level.blockEntityChanged(worldPosition); } }; diff --git a/src/main/java/com/simibubi/create/content/schematics/client/SchematicAndQuillHandler.java b/src/main/java/com/simibubi/create/content/schematics/client/SchematicAndQuillHandler.java index 8c536b6e48..5de08ceef5 100644 --- a/src/main/java/com/simibubi/create/content/schematics/client/SchematicAndQuillHandler.java +++ b/src/main/java/com/simibubi/create/content/schematics/client/SchematicAndQuillHandler.java @@ -63,9 +63,12 @@ public class SchematicAndQuillHandler { if (bb.contains(projectedView)) delta *= -1; - int x = (int) (vec.getX() * delta); - int y = (int) (vec.getY() * delta); - int z = (int) (vec.getZ() * delta); + // Round away from zero to avoid an implicit floor + int intDelta = (int) (delta > 0 ? Math.ceil(delta) : Math.floor(delta)); + + int x = vec.getX() * intDelta; + int y = vec.getY() * intDelta; + int z = vec.getZ() * intDelta; AxisDirection axisDirection = selectedFace.getAxisDirection(); if (axisDirection == AxisDirection.NEGATIVE) diff --git a/src/main/java/com/simibubi/create/content/schematics/client/SchematicHandler.java b/src/main/java/com/simibubi/create/content/schematics/client/SchematicHandler.java index 967f66b0fe..3bdb058248 100644 --- a/src/main/java/com/simibubi/create/content/schematics/client/SchematicHandler.java +++ b/src/main/java/com/simibubi/create/content/schematics/client/SchematicHandler.java @@ -95,8 +95,6 @@ public class SchematicHandler implements LayeredDraw.Layer { if (activeSchematicItem != null && transformation != null) transformation.tick(); - renderers.forEach(SchematicRenderer::tick); - LocalPlayer player = mc.player; ItemStack stack = findBlueprintInHand(player); if (stack == null) { @@ -316,7 +314,7 @@ public class SchematicHandler implements LayeredDraw.Layer { return false; if (selectionScreen.focused) { - selectionScreen.cycle((int) delta); + selectionScreen.cycle((int) Math.signum(delta)); return true; } if (AllKeys.ctrlDown()) diff --git a/src/main/java/com/simibubi/create/content/schematics/client/SchematicRenderer.java b/src/main/java/com/simibubi/create/content/schematics/client/SchematicRenderer.java index e48e80a0a2..b834a1601b 100644 --- a/src/main/java/com/simibubi/create/content/schematics/client/SchematicRenderer.java +++ b/src/main/java/com/simibubi/create/content/schematics/client/SchematicRenderer.java @@ -53,20 +53,17 @@ public class SchematicRenderer { changed = true; } - public void tick() { - if (!active) - return; - Minecraft mc = Minecraft.getInstance(); - if (mc.level == null || mc.player == null || !changed) - return; - - redraw(); - changed = false; - } - public void render(PoseStack ms, SuperRenderTypeBuffer buffers) { if (!active) return; + + Minecraft mc = Minecraft.getInstance(); + if (mc.level == null || mc.player == null) + return; + if (changed) + redraw(); + changed = false; + bufferCache.forEach((layer, buffer) -> { buffer.renderInto(ms, buffers.getBuffer(layer)); }); diff --git a/src/main/java/com/simibubi/create/foundation/data/recipe/CrushingRecipeGen.java b/src/main/java/com/simibubi/create/foundation/data/recipe/CrushingRecipeGen.java index bb0997ec9b..a602d56461 100644 --- a/src/main/java/com/simibubi/create/foundation/data/recipe/CrushingRecipeGen.java +++ b/src/main/java/com/simibubi/create/foundation/data/recipe/CrushingRecipeGen.java @@ -406,7 +406,21 @@ public class CrushingRecipeGen extends ProcessingRecipeGen { .output(0.75f, Mods.AET, "ambrosium_shard", 1) .output(0.125f, Mods.AET, "holystone", 1) .output(0.75f, AllItems.EXP_NUGGET.get()) - .whenModLoaded(Mods.AET.getId())) + .whenModLoaded(Mods.AET.getId())), + + // IE + + IE_COKE_DUST = create(Mods.IE.recipeId("coal_coke"), b -> b.duration(200) + .require(Mods.IE, "coal_coke").output(Mods.IE, "dust_coke") + .whenModLoaded(Mods.IE.getId())), + + IE_COKE_BLOCK = create(Mods.IE.recipeId("coke_block"), b -> b.duration(200) + .require(Mods.IE, "coke").output(1, Mods.IE.asResource("dust_coke"), 9) + .whenModLoaded(Mods.IE.getId())), + + IE_SLAG_GRAVEL = create(Mods.IE.recipeId("slag"), b -> b.duration(200) + .require(Mods.IE, "slag").output(Mods.IE, "slag_gravel") + .whenModLoaded(Mods.IE.getId())); ; diff --git a/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java b/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java index 707e12238f..a06e24abe4 100644 --- a/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java +++ b/src/main/java/com/simibubi/create/infrastructure/config/CKinetics.java @@ -1,6 +1,5 @@ package com.simibubi.create.infrastructure.config; -import com.simibubi.create.content.contraptions.ContraptionData; import com.simibubi.create.content.contraptions.ContraptionMovementSetting; import net.createmod.catnip.config.ConfigBase; @@ -32,8 +31,6 @@ public class CKinetics extends ConfigBase { public final ConfigGroup contraptions = group(1, "contraptions", "Moving Contraptions"); public final ConfigInt maxBlocksMoved = i(2048, 1, "maxBlocksMoved", Comments.maxBlocksMoved); - public final ConfigInt maxDataSize = - i(ContraptionData.DEFAULT_LIMIT, 0, "maxDataSize", Comments.bytes, Comments.maxDataDisable, Comments.maxDataSize, Comments.maxDataSize2); public final ConfigInt maxChassisRange = i(16, 1, "maxChassisRange", Comments.maxChassisRange); public final ConfigInt maxPistonPoles = i(64, 1, "maxPistonPoles", Comments.maxPistonPoles); public final ConfigInt maxRopeLength = i(384, 1, "maxRopeLength", Comments.maxRopeLength); @@ -86,9 +83,6 @@ public class CKinetics extends ConfigBase { "multiplier used for calculating exhaustion from speed when a crank is turned."; static String maxBlocksMoved = "Maximum amount of blocks in a structure movable by Pistons, Bearings or other means."; - static String maxDataSize = "Maximum amount of data a contraption can have before it can't be synced with players."; - static String maxDataSize2 = "Un-synced contraptions will not be visible and will not have collision."; - static String maxDataDisable = "[0 to disable this limit]"; static String maxChassisRange = "Maximum value of a chassis attachment range."; static String maxPistonPoles = "Maximum amount of extension poles behind a Mechanical Piston."; static String maxRopeLength = "Max length of rope available off a Rope Pulley."; diff --git a/src/main/resources/assets/create/textures/block/vault/vault_side_large.png b/src/main/resources/assets/create/textures/block/vault/vault_side_large.png index f58fbe7e00..48d0e4d719 100644 Binary files a/src/main/resources/assets/create/textures/block/vault/vault_side_large.png and b/src/main/resources/assets/create/textures/block/vault/vault_side_large.png differ diff --git a/src/main/resources/assets/create/textures/block/vault/vault_side_medium.png b/src/main/resources/assets/create/textures/block/vault/vault_side_medium.png index e44307a190..94b0c4be9d 100644 Binary files a/src/main/resources/assets/create/textures/block/vault/vault_side_medium.png and b/src/main/resources/assets/create/textures/block/vault/vault_side_medium.png differ diff --git a/src/main/resources/assets/create/textures/block/vault/vault_top_large.png b/src/main/resources/assets/create/textures/block/vault/vault_top_large.png index 44b837d810..770609b600 100644 Binary files a/src/main/resources/assets/create/textures/block/vault/vault_top_large.png and b/src/main/resources/assets/create/textures/block/vault/vault_top_large.png differ diff --git a/src/main/resources/assets/create/textures/block/vault/vault_top_medium.png b/src/main/resources/assets/create/textures/block/vault/vault_top_medium.png index c28936a15c..2ec555bab1 100644 Binary files a/src/main/resources/assets/create/textures/block/vault/vault_top_medium.png and b/src/main/resources/assets/create/textures/block/vault/vault_top_medium.png differ