mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 13:42:59 +00:00 
			
		
		
		
	Support placing pocket computers on lecterns (#2098)
This allows shift+clicking a pocket computer on to a lectern. These computers can be right clicked, opening the no-term computer GUI. Terminal contents is rendered in-world, and broadcast to everyone in range. - Add a new lectern PocketHolder. - Refactor some of the `PocketItemComputer` code to allow ticking pocket computers from a non-player/entity source. - Add a new model for pocket computers. This requires several new textures (somewhat mirroring the item ones), which is a little unfortunate, but looks much better than reusing the map renderer or item form.
This commit is contained in:
		| @@ -0,0 +1,96 @@ | |||||||
|  | // SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers | ||||||
|  | // | ||||||
|  | // SPDX-License-Identifier: MPL-2.0 | ||||||
|  | 
 | ||||||
|  | package dan200.computercraft.client.model; | ||||||
|  | 
 | ||||||
|  | import com.mojang.blaze3d.vertex.PoseStack; | ||||||
|  | import com.mojang.blaze3d.vertex.VertexConsumer; | ||||||
|  | import dan200.computercraft.api.ComputerCraftAPI; | ||||||
|  | import dan200.computercraft.client.pocket.PocketComputerData; | ||||||
|  | import dan200.computercraft.client.render.CustomLecternRenderer; | ||||||
|  | import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||||
|  | import dan200.computercraft.shared.pocket.items.PocketComputerItem; | ||||||
|  | import net.minecraft.client.model.geom.ModelPart; | ||||||
|  | import net.minecraft.client.model.geom.PartPose; | ||||||
|  | import net.minecraft.client.model.geom.builders.CubeListBuilder; | ||||||
|  | import net.minecraft.client.model.geom.builders.MeshDefinition; | ||||||
|  | import net.minecraft.client.renderer.LightTexture; | ||||||
|  | import net.minecraft.client.renderer.MultiBufferSource; | ||||||
|  | import net.minecraft.client.renderer.RenderType; | ||||||
|  | import net.minecraft.client.resources.model.Material; | ||||||
|  | import net.minecraft.resources.ResourceLocation; | ||||||
|  | import net.minecraft.util.FastColor; | ||||||
|  | import net.minecraft.world.inventory.InventoryMenu; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * A model for {@linkplain PocketComputerItem pocket computers} placed on a lectern. | ||||||
|  |  * | ||||||
|  |  * @see CustomLecternRenderer | ||||||
|  |  */ | ||||||
|  | public class LecternPocketModel { | ||||||
|  |     public static final ResourceLocation TEXTURE_NORMAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_normal"); | ||||||
|  |     public static final ResourceLocation TEXTURE_ADVANCED = new ResourceLocation(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_advanced"); | ||||||
|  |     public static final ResourceLocation TEXTURE_COLOUR = new ResourceLocation(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_colour"); | ||||||
|  |     public static final ResourceLocation TEXTURE_FRAME = new ResourceLocation(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_frame"); | ||||||
|  |     public static final ResourceLocation TEXTURE_LIGHT = new ResourceLocation(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_light"); | ||||||
|  | 
 | ||||||
|  |     private static final Material MATERIAL_NORMAL = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_NORMAL); | ||||||
|  |     private static final Material MATERIAL_ADVANCED = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_ADVANCED); | ||||||
|  |     private static final Material MATERIAL_COLOUR = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_COLOUR); | ||||||
|  |     private static final Material MATERIAL_FRAME = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_FRAME); | ||||||
|  |     private static final Material MATERIAL_LIGHT = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_LIGHT); | ||||||
|  | 
 | ||||||
|  |     // The size of the terminal within the model. | ||||||
|  |     public static final float TERM_WIDTH = 12.0f / 32.0f; | ||||||
|  |     public static final float TERM_HEIGHT = 14.0f / 32.0f; | ||||||
|  | 
 | ||||||
|  |     // The size of the texture. The texture is 36x36, but is at 2x resolution. | ||||||
|  |     private static final int TEXTURE_WIDTH = 36 / 2; | ||||||
|  |     private static final int TEXTURE_HEIGHT = 36 / 2; | ||||||
|  | 
 | ||||||
|  |     private final ModelPart root; | ||||||
|  | 
 | ||||||
|  |     public LecternPocketModel() { | ||||||
|  |         root = buildPages(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static ModelPart buildPages() { | ||||||
|  |         var mesh = new MeshDefinition(); | ||||||
|  |         var parts = mesh.getRoot(); | ||||||
|  |         parts.addOrReplaceChild( | ||||||
|  |             "root", | ||||||
|  |             CubeListBuilder.create().texOffs(0, 0).addBox(0f, -5.0f, -4.0f, 1f, 10.0f, 8.0f), | ||||||
|  |             PartPose.ZERO | ||||||
|  |         ); | ||||||
|  |         return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void render(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, int colour) { | ||||||
|  |         int red = FastColor.ARGB32.red(colour), green = FastColor.ARGB32.green(colour), blue = FastColor.ARGB32.blue(colour), alpha = FastColor.ARGB32.alpha(colour); | ||||||
|  |         root.render(poseStack, buffer, packedLight, packedOverlay, red / 255.0f, green / 255.0f, blue / 255.0f, alpha / 255.0f); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Render the pocket computer model. | ||||||
|  |      * | ||||||
|  |      * @param poseStack     The current pose stack. | ||||||
|  |      * @param bufferSource  The buffer source to draw to. | ||||||
|  |      * @param packedLight   The current light level. | ||||||
|  |      * @param packedOverlay The overlay texture (used for entity hurt animation). | ||||||
|  |      * @param family        The computer family. | ||||||
|  |      * @param frameColour   The pocket computer's {@linkplain PocketComputerItem#getColour(ItemStack) colour}. | ||||||
|  |      * @param lightColour   The pocket computer's {@linkplain PocketComputerData#getLightState() light colour}. | ||||||
|  |      */ | ||||||
|  |     public void render(PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay, ComputerFamily family, int frameColour, int lightColour) { | ||||||
|  |         if (frameColour != -1) { | ||||||
|  |             root.render(poseStack, MATERIAL_FRAME.buffer(bufferSource, RenderType::entityCutout), packedLight, packedOverlay, 1, 1, 1, 1); | ||||||
|  |             render(poseStack, MATERIAL_COLOUR.buffer(bufferSource, RenderType::entityCutout), packedLight, packedOverlay, frameColour); | ||||||
|  |         } else { | ||||||
|  |             var buffer = (family == ComputerFamily.ADVANCED ? MATERIAL_ADVANCED : MATERIAL_NORMAL).buffer(bufferSource, RenderType::entityCutout); | ||||||
|  |             root.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         render(poseStack, MATERIAL_LIGHT.buffer(bufferSource, RenderType::entityCutout), LightTexture.FULL_BRIGHT, packedOverlay, lightColour); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -6,15 +6,28 @@ package dan200.computercraft.client.render; | |||||||
| 
 | 
 | ||||||
| import com.mojang.blaze3d.vertex.PoseStack; | import com.mojang.blaze3d.vertex.PoseStack; | ||||||
| import com.mojang.math.Axis; | import com.mojang.math.Axis; | ||||||
|  | import dan200.computercraft.client.model.LecternPocketModel; | ||||||
| import dan200.computercraft.client.model.LecternPrintoutModel; | import dan200.computercraft.client.model.LecternPrintoutModel; | ||||||
|  | import dan200.computercraft.client.pocket.ClientPocketComputers; | ||||||
|  | import dan200.computercraft.client.render.text.FixedWidthFontRenderer; | ||||||
|  | import dan200.computercraft.core.terminal.Terminal; | ||||||
|  | import dan200.computercraft.core.util.Colour; | ||||||
| import dan200.computercraft.shared.lectern.CustomLecternBlockEntity; | import dan200.computercraft.shared.lectern.CustomLecternBlockEntity; | ||||||
| import dan200.computercraft.shared.media.items.PrintoutItem; | import dan200.computercraft.shared.media.items.PrintoutItem; | ||||||
|  | import dan200.computercraft.shared.pocket.items.PocketComputerItem; | ||||||
|  | import dan200.computercraft.shared.util.ARGB32; | ||||||
| import net.minecraft.client.renderer.MultiBufferSource; | import net.minecraft.client.renderer.MultiBufferSource; | ||||||
| import net.minecraft.client.renderer.RenderType; | import net.minecraft.client.renderer.RenderType; | ||||||
|  | import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; | ||||||
| import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; | import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; | ||||||
| import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; | import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; | ||||||
| import net.minecraft.client.renderer.blockentity.LecternRenderer; | import net.minecraft.client.renderer.blockentity.LecternRenderer; | ||||||
| import net.minecraft.world.level.block.LecternBlock; | import net.minecraft.world.level.block.LecternBlock; | ||||||
|  | import net.minecraft.world.phys.Vec3; | ||||||
|  | 
 | ||||||
|  | import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN; | ||||||
|  | import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT; | ||||||
|  | import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * A block entity renderer for our {@linkplain CustomLecternBlockEntity lectern}. |  * A block entity renderer for our {@linkplain CustomLecternBlockEntity lectern}. | ||||||
| @@ -22,10 +35,17 @@ import net.minecraft.world.level.block.LecternBlock; | |||||||
|  * This largely follows {@link LecternRenderer}, but with support for multiple types of item. |  * This largely follows {@link LecternRenderer}, but with support for multiple types of item. | ||||||
|  */ |  */ | ||||||
| public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> { | public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> { | ||||||
|  |     private static final int POCKET_TERMINAL_RENDER_DISTANCE = 32; | ||||||
|  | 
 | ||||||
|  |     private final BlockEntityRenderDispatcher berDispatcher; | ||||||
|     private final LecternPrintoutModel printoutModel; |     private final LecternPrintoutModel printoutModel; | ||||||
|  |     private final LecternPocketModel pocketModel; | ||||||
| 
 | 
 | ||||||
|     public CustomLecternRenderer(BlockEntityRendererProvider.Context context) { |     public CustomLecternRenderer(BlockEntityRendererProvider.Context context) { | ||||||
|  |         berDispatcher = context.getBlockEntityRenderDispatcher(); | ||||||
|  | 
 | ||||||
|         printoutModel = new LecternPrintoutModel(); |         printoutModel = new LecternPrintoutModel(); | ||||||
|  |         pocketModel = new LecternPocketModel(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
| @@ -44,8 +64,46 @@ public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternB | |||||||
|             } else { |             } else { | ||||||
|                 printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutItem.getPageCount(item)); |                 printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutItem.getPageCount(item)); | ||||||
|             } |             } | ||||||
|  |         } else if (item.getItem() instanceof PocketComputerItem pocket) { | ||||||
|  |             var computer = ClientPocketComputers.get(item); | ||||||
|  | 
 | ||||||
|  |             pocketModel.render( | ||||||
|  |                 poseStack, buffer, packedLight, packedOverlay, pocket.getFamily(), pocket.getColour(item), | ||||||
|  |                 ARGB32.opaque(computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState()) | ||||||
|  |             ); | ||||||
|  | 
 | ||||||
|  |             // Jiggle the terminal about a bit, so (0, 0) is in the top left of the model's terminal hole. | ||||||
|  |             poseStack.mulPose(Axis.YP.rotationDegrees(90f)); | ||||||
|  |             poseStack.translate(-0.5 * LecternPocketModel.TERM_WIDTH, 0.5 * LecternPocketModel.TERM_HEIGHT + 1f / 32.0f, 1 / 16.0f); | ||||||
|  |             poseStack.mulPose(Axis.XP.rotationDegrees(180)); | ||||||
|  | 
 | ||||||
|  |             // Either render the terminal or a black screen, depending on how close we are. | ||||||
|  |             var terminal = computer == null ? null : computer.getTerminal(); | ||||||
|  |             var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(poseStack, buffer.getBuffer(RenderTypes.TERMINAL)); | ||||||
|  |             if (terminal != null && Vec3.atCenterOf(lectern.getBlockPos()).closerThan(berDispatcher.camera.getPosition(), POCKET_TERMINAL_RENDER_DISTANCE)) { | ||||||
|  |                 renderPocketTerminal(poseStack, quadEmitter, terminal); | ||||||
|  |             } else { | ||||||
|  |                 FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, LecternPocketModel.TERM_WIDTH, LecternPocketModel.TERM_HEIGHT); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         poseStack.popPose(); |         poseStack.popPose(); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     private static void renderPocketTerminal(PoseStack poseStack, FixedWidthFontRenderer.QuadEmitter quadEmitter, Terminal terminal) { | ||||||
|  |         var width = terminal.getWidth() * FONT_WIDTH; | ||||||
|  |         var height = terminal.getHeight() * FONT_HEIGHT; | ||||||
|  | 
 | ||||||
|  |         // Scale the terminal down to fit in the available space. | ||||||
|  |         var scaleX = LecternPocketModel.TERM_WIDTH / (width + MARGIN * 2); | ||||||
|  |         var scaleY = LecternPocketModel.TERM_HEIGHT / (height + MARGIN * 2); | ||||||
|  |         var scale = Math.min(scaleX, scaleY); | ||||||
|  |         poseStack.scale(scale, scale, -1.0f); | ||||||
|  | 
 | ||||||
|  |         // Convert the model dimensions to terminal space, then find out how large the margin should be. | ||||||
|  |         var marginX = ((LecternPocketModel.TERM_WIDTH / scale) - width) / 2; | ||||||
|  |         var marginY = ((LecternPocketModel.TERM_HEIGHT / scale) - height) / 2; | ||||||
|  | 
 | ||||||
|  |         FixedWidthFontRenderer.drawTerminal(quadEmitter, marginX, marginY, terminal, marginY, marginY, marginX, marginX); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -43,7 +43,7 @@ public final class FixedWidthFontRenderer { | |||||||
|     static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; |     static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; | ||||||
| 
 | 
 | ||||||
|     private static final int BLACK = FastColor.ARGB32.color(255, byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR())); |     private static final int BLACK = FastColor.ARGB32.color(255, byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR())); | ||||||
|     private static final float Z_OFFSET = 1e-3f; |     private static final float Z_OFFSET = 1e-4f; | ||||||
| 
 | 
 | ||||||
|     private FixedWidthFontRenderer() { |     private FixedWidthFontRenderer() { | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ package dan200.computercraft.data; | |||||||
| 
 | 
 | ||||||
| import com.mojang.serialization.Codec; | import com.mojang.serialization.Codec; | ||||||
| import dan200.computercraft.client.gui.GuiSprites; | import dan200.computercraft.client.gui.GuiSprites; | ||||||
|  | import dan200.computercraft.client.model.LecternPocketModel; | ||||||
| import dan200.computercraft.client.model.LecternPrintoutModel; | import dan200.computercraft.client.model.LecternPrintoutModel; | ||||||
| import dan200.computercraft.shared.turtle.inventory.UpgradeSlot; | import dan200.computercraft.shared.turtle.inventory.UpgradeSlot; | ||||||
| import net.minecraft.client.renderer.texture.atlas.SpriteSource; | import net.minecraft.client.renderer.texture.atlas.SpriteSource; | ||||||
| @@ -54,7 +55,9 @@ public final class DataProviders { | |||||||
|             out.accept(new ResourceLocation("blocks"), makeSprites(Stream.of( |             out.accept(new ResourceLocation("blocks"), makeSprites(Stream.of( | ||||||
|                 UpgradeSlot.LEFT_UPGRADE, |                 UpgradeSlot.LEFT_UPGRADE, | ||||||
|                 UpgradeSlot.RIGHT_UPGRADE, |                 UpgradeSlot.RIGHT_UPGRADE, | ||||||
|                 LecternPrintoutModel.TEXTURE |                 LecternPrintoutModel.TEXTURE, | ||||||
|  |                 LecternPocketModel.TEXTURE_NORMAL, LecternPocketModel.TEXTURE_ADVANCED, | ||||||
|  |                 LecternPocketModel.TEXTURE_COLOUR, LecternPocketModel.TEXTURE_FRAME, LecternPocketModel.TEXTURE_LIGHT | ||||||
|             ))); |             ))); | ||||||
|             out.accept(GuiSprites.SPRITE_SHEET, makeSprites( |             out.accept(GuiSprites.SPRITE_SHEET, makeSprites( | ||||||
|                 Stream.of(GuiSprites.TURTLE_NORMAL_SELECTED_SLOT, GuiSprites.TURTLE_ADVANCED_SELECTED_SLOT), |                 Stream.of(GuiSprites.TURTLE_NORMAL_SELECTED_SLOT, GuiSprites.TURTLE_ADVANCED_SELECTED_SLOT), | ||||||
|   | |||||||
| @@ -2,6 +2,11 @@ | |||||||
|   "sources": [ |   "sources": [ | ||||||
|     {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"}, |     {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_left"}, | ||||||
|     {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"}, |     {"type": "minecraft:single", "resource": "computercraft:gui/turtle_upgrade_right"}, | ||||||
|     {"type": "minecraft:single", "resource": "computercraft:entity/printout"} |     {"type": "minecraft:single", "resource": "computercraft:entity/printout"}, | ||||||
|  |     {"type": "minecraft:single", "resource": "computercraft:entity/pocket_computer_normal"}, | ||||||
|  |     {"type": "minecraft:single", "resource": "computercraft:entity/pocket_computer_advanced"}, | ||||||
|  |     {"type": "minecraft:single", "resource": "computercraft:entity/pocket_computer_colour"}, | ||||||
|  |     {"type": "minecraft:single", "resource": "computercraft:entity/pocket_computer_frame"}, | ||||||
|  |     {"type": "minecraft:single", "resource": "computercraft:entity/pocket_computer_light"} | ||||||
|   ] |   ] | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ package dan200.computercraft.shared.lectern; | |||||||
| 
 | 
 | ||||||
| import dan200.computercraft.shared.ModRegistry; | import dan200.computercraft.shared.ModRegistry; | ||||||
| import dan200.computercraft.shared.media.items.PrintoutItem; | import dan200.computercraft.shared.media.items.PrintoutItem; | ||||||
|  | import dan200.computercraft.shared.util.BlockEntityHelpers; | ||||||
| import net.minecraft.core.BlockPos; | import net.minecraft.core.BlockPos; | ||||||
| import net.minecraft.server.level.ServerLevel; | import net.minecraft.server.level.ServerLevel; | ||||||
| import net.minecraft.stats.Stats; | import net.minecraft.stats.Stats; | ||||||
| @@ -14,6 +15,7 @@ import net.minecraft.world.InteractionHand; | |||||||
| import net.minecraft.world.InteractionResult; | import net.minecraft.world.InteractionResult; | ||||||
| import net.minecraft.world.entity.item.ItemEntity; | import net.minecraft.world.entity.item.ItemEntity; | ||||||
| import net.minecraft.world.entity.player.Player; | import net.minecraft.world.entity.player.Player; | ||||||
|  | import net.minecraft.world.item.Item; | ||||||
| import net.minecraft.world.item.ItemStack; | import net.minecraft.world.item.ItemStack; | ||||||
| import net.minecraft.world.item.Items; | import net.minecraft.world.item.Items; | ||||||
| import net.minecraft.world.item.context.UseOnContext; | import net.minecraft.world.item.context.UseOnContext; | ||||||
| @@ -21,8 +23,12 @@ import net.minecraft.world.level.BlockGetter; | |||||||
| import net.minecraft.world.level.Level; | import net.minecraft.world.level.Level; | ||||||
| import net.minecraft.world.level.block.Blocks; | import net.minecraft.world.level.block.Blocks; | ||||||
| import net.minecraft.world.level.block.LecternBlock; | import net.minecraft.world.level.block.LecternBlock; | ||||||
|  | import net.minecraft.world.level.block.entity.BlockEntity; | ||||||
|  | import net.minecraft.world.level.block.entity.BlockEntityTicker; | ||||||
|  | import net.minecraft.world.level.block.entity.BlockEntityType; | ||||||
| import net.minecraft.world.level.block.state.BlockState; | import net.minecraft.world.level.block.state.BlockState; | ||||||
| import net.minecraft.world.phys.BlockHitResult; | import net.minecraft.world.phys.BlockHitResult; | ||||||
|  | import org.jetbrains.annotations.Nullable; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Extends {@link LecternBlock} with support for {@linkplain PrintoutItem printouts}. |  * Extends {@link LecternBlock} with support for {@linkplain PrintoutItem printouts}. | ||||||
| @@ -55,6 +61,27 @@ public class CustomLecternBlock extends LecternBlock { | |||||||
|         if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity be) be.setItem(item.split(1)); |         if (level.getBlockEntity(pos) instanceof CustomLecternBlockEntity be) be.setItem(item.split(1)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * A default implementation of {@link Item#useOn(UseOnContext)} for items that can be placed on a lectern. | ||||||
|  |      * | ||||||
|  |      * @param context The context of this item usage action. | ||||||
|  |      * @return Whether the item was placed or not. | ||||||
|  |      */ | ||||||
|  |     public static InteractionResult defaultUseItemOn(UseOnContext context) { | ||||||
|  |         var level = context.getLevel(); | ||||||
|  |         var blockPos = context.getClickedPos(); | ||||||
|  |         var blockState = level.getBlockState(blockPos); | ||||||
|  |         if (blockState.is(Blocks.LECTERN) && !blockState.getValue(LecternBlock.HAS_BOOK)) { | ||||||
|  |             // If we have an empty lectern, place our book into it. | ||||||
|  |             if (!level.isClientSide) { | ||||||
|  |                 CustomLecternBlock.replaceLectern(level, blockPos, blockState, context.getItemInHand()); | ||||||
|  |             } | ||||||
|  |             return InteractionResult.sidedSuccess(level.isClientSide); | ||||||
|  |         } else { | ||||||
|  |             return InteractionResult.PASS; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Remove a custom lectern and replace it with an empty vanilla one. |      * Remove a custom lectern and replace it with an empty vanilla one. | ||||||
|      * |      * | ||||||
| @@ -131,7 +158,7 @@ public class CustomLecternBlock extends LecternBlock { | |||||||
|                 clearLectern(level, pos, state); |                 clearLectern(level, pos, state); | ||||||
|             } else { |             } else { | ||||||
|                 // Otherwise open the screen. |                 // Otherwise open the screen. | ||||||
|                 player.openMenu(lectern); |                 lectern.openMenu(player); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             player.awardStat(Stats.INTERACT_WITH_LECTERN); |             player.awardStat(Stats.INTERACT_WITH_LECTERN); | ||||||
| @@ -139,4 +166,11 @@ public class CustomLecternBlock extends LecternBlock { | |||||||
| 
 | 
 | ||||||
|         return InteractionResult.sidedSuccess(level.isClientSide); |         return InteractionResult.sidedSuccess(level.isClientSide); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) { | ||||||
|  |         return level.isClientSide ? null : BlockEntityHelpers.createTickerHelper(type, ModRegistry.BlockEntities.LECTERN.get(), serverTicker); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static final BlockEntityTicker<CustomLecternBlockEntity> serverTicker = (level, pos, state, lectern) -> lectern.tick(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,27 +9,25 @@ import dan200.computercraft.shared.container.BasicContainer; | |||||||
| import dan200.computercraft.shared.container.SingleContainerData; | import dan200.computercraft.shared.container.SingleContainerData; | ||||||
| import dan200.computercraft.shared.media.PrintoutMenu; | import dan200.computercraft.shared.media.PrintoutMenu; | ||||||
| import dan200.computercraft.shared.media.items.PrintoutItem; | import dan200.computercraft.shared.media.items.PrintoutItem; | ||||||
|  | import dan200.computercraft.shared.pocket.core.PocketHolder; | ||||||
|  | import dan200.computercraft.shared.pocket.items.PocketComputerItem; | ||||||
| import dan200.computercraft.shared.util.BlockEntityHelpers; | import dan200.computercraft.shared.util.BlockEntityHelpers; | ||||||
| import net.minecraft.core.BlockPos; | import net.minecraft.core.BlockPos; | ||||||
| import net.minecraft.nbt.CompoundTag; | import net.minecraft.nbt.CompoundTag; | ||||||
| import net.minecraft.nbt.Tag; | import net.minecraft.nbt.Tag; | ||||||
| import net.minecraft.network.chat.Component; |  | ||||||
| import net.minecraft.network.protocol.Packet; | import net.minecraft.network.protocol.Packet; | ||||||
| import net.minecraft.network.protocol.game.ClientGamePacketListener; | import net.minecraft.network.protocol.game.ClientGamePacketListener; | ||||||
| import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; | import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; | ||||||
| import net.minecraft.util.Mth; | import net.minecraft.util.Mth; | ||||||
| import net.minecraft.world.Container; | import net.minecraft.world.Container; | ||||||
| import net.minecraft.world.MenuProvider; | import net.minecraft.world.SimpleMenuProvider; | ||||||
| import net.minecraft.world.entity.player.Inventory; |  | ||||||
| import net.minecraft.world.entity.player.Player; | import net.minecraft.world.entity.player.Player; | ||||||
| import net.minecraft.world.inventory.AbstractContainerMenu; |  | ||||||
| import net.minecraft.world.inventory.ContainerData; | import net.minecraft.world.inventory.ContainerData; | ||||||
| import net.minecraft.world.item.ItemStack; | import net.minecraft.world.item.ItemStack; | ||||||
| import net.minecraft.world.level.block.LecternBlock; | import net.minecraft.world.level.block.LecternBlock; | ||||||
| import net.minecraft.world.level.block.entity.BlockEntity; | import net.minecraft.world.level.block.entity.BlockEntity; | ||||||
| import net.minecraft.world.level.block.entity.LecternBlockEntity; | import net.minecraft.world.level.block.entity.LecternBlockEntity; | ||||||
| import net.minecraft.world.level.block.state.BlockState; | import net.minecraft.world.level.block.state.BlockState; | ||||||
| import org.jetbrains.annotations.Nullable; |  | ||||||
| 
 | 
 | ||||||
| import java.util.AbstractList; | import java.util.AbstractList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -39,7 +37,7 @@ import java.util.List; | |||||||
|  * |  * | ||||||
|  * @see LecternBlockEntity |  * @see LecternBlockEntity | ||||||
|  */ |  */ | ||||||
| public final class CustomLecternBlockEntity extends BlockEntity implements MenuProvider { | public final class CustomLecternBlockEntity extends BlockEntity { | ||||||
|     private static final String NBT_ITEM = "Item"; |     private static final String NBT_ITEM = "Item"; | ||||||
|     private static final String NBT_PAGE = "Page"; |     private static final String NBT_PAGE = "Page"; | ||||||
| 
 | 
 | ||||||
| @@ -81,6 +79,12 @@ public final class CustomLecternBlockEntity extends BlockEntity implements MenuP | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void tick() { | ||||||
|  |         if (item.getItem() instanceof PocketComputerItem pocket) { | ||||||
|  |             pocket.tick(item, new PocketHolder.LecternHolder(this), false); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Set the current page, emitting a redstone pulse if needed. |      * Set the current page, emitting a redstone pulse if needed. | ||||||
|      * |      * | ||||||
| @@ -123,24 +127,17 @@ public final class CustomLecternBlockEntity extends BlockEntity implements MenuP | |||||||
|         return tag; |         return tag; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Nullable |     void openMenu(Player player) { | ||||||
|     @Override |  | ||||||
|     public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) { |  | ||||||
|         var item = getItem(); |         var item = getItem(); | ||||||
|         if (item.getItem() instanceof PrintoutItem) { |         if (item.getItem() instanceof PrintoutItem) { | ||||||
|             return new PrintoutMenu( |             player.openMenu(new SimpleMenuProvider((id, inventory, entity) -> new PrintoutMenu( | ||||||
|                 containerId, new LecternContainer(), 0, |                 id, new LecternContainer(), 0, | ||||||
|                 p -> Container.stillValidBlockEntity(this, player, Container.DEFAULT_DISTANCE_LIMIT), |                 p -> Container.stillValidBlockEntity(this, p, Container.DEFAULT_DISTANCE_LIMIT), | ||||||
|                 new PrintoutContainerData() |                 new PrintoutContainerData() | ||||||
|             ); |             ), getItem().getDisplayName())); | ||||||
|  |         } else if (item.getItem() instanceof PocketComputerItem pocket) { | ||||||
|  |             pocket.open(player, item, new PocketHolder.LecternHolder(this), true); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public Component getDisplayName() { |  | ||||||
|         return getItem().getDisplayName(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -19,8 +19,6 @@ import net.minecraft.world.item.ItemStack; | |||||||
| import net.minecraft.world.item.TooltipFlag; | import net.minecraft.world.item.TooltipFlag; | ||||||
| import net.minecraft.world.item.context.UseOnContext; | import net.minecraft.world.item.context.UseOnContext; | ||||||
| import net.minecraft.world.level.Level; | import net.minecraft.world.level.Level; | ||||||
| import net.minecraft.world.level.block.Blocks; |  | ||||||
| import net.minecraft.world.level.block.LecternBlock; |  | ||||||
| 
 | 
 | ||||||
| import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -56,18 +54,7 @@ public class PrintoutItem extends Item { | |||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public InteractionResult useOn(UseOnContext context) { |     public InteractionResult useOn(UseOnContext context) { | ||||||
|         var level = context.getLevel(); |         return CustomLecternBlock.defaultUseItemOn(context); | ||||||
|         var blockPos = context.getClickedPos(); |  | ||||||
|         var blockState = level.getBlockState(blockPos); |  | ||||||
|         if (blockState.is(Blocks.LECTERN) && !blockState.getValue(LecternBlock.HAS_BOOK)) { |  | ||||||
|             // If we have an empty lectern, place our book into it. |  | ||||||
|             if (!level.isClientSide) { |  | ||||||
|                 CustomLecternBlock.replaceLectern(level, blockPos, blockState, context.getItemInHand()); |  | ||||||
|             } |  | ||||||
|             return InteractionResult.sidedSuccess(level.isClientSide); |  | ||||||
|         } else { |  | ||||||
|             return InteractionResult.PASS; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
| @@ -5,7 +5,9 @@ | |||||||
| package dan200.computercraft.shared.pocket.core; | package dan200.computercraft.shared.pocket.core; | ||||||
| 
 | 
 | ||||||
| import dan200.computercraft.shared.computer.core.ServerComputer; | import dan200.computercraft.shared.computer.core.ServerComputer; | ||||||
|  | import dan200.computercraft.shared.lectern.CustomLecternBlockEntity; | ||||||
| import dan200.computercraft.shared.pocket.items.PocketComputerItem; | import dan200.computercraft.shared.pocket.items.PocketComputerItem; | ||||||
|  | import dan200.computercraft.shared.util.BlockEntityHelpers; | ||||||
| import net.minecraft.core.BlockPos; | import net.minecraft.core.BlockPos; | ||||||
| import net.minecraft.server.level.ServerLevel; | import net.minecraft.server.level.ServerLevel; | ||||||
| import net.minecraft.server.level.ServerPlayer; | import net.minecraft.server.level.ServerPlayer; | ||||||
| @@ -51,6 +53,15 @@ public sealed interface PocketHolder { | |||||||
|      */ |      */ | ||||||
|     void setChanged(); |     void setChanged(); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Whether the terminal is visible to all players in range, and so should be broadcast to everyone. | ||||||
|  |      * | ||||||
|  |      * @return Whether to send the terminal. | ||||||
|  |      */ | ||||||
|  |     default boolean isTerminalAlwaysVisible() { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * An {@link Entity} holding a pocket computer. |      * An {@link Entity} holding a pocket computer. | ||||||
|      */ |      */ | ||||||
| @@ -112,4 +123,41 @@ public sealed interface PocketHolder { | |||||||
|             entity.setItem(entity.getItem().copy()); |             entity.setItem(entity.getItem().copy()); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * A pocket computer in a {@link CustomLecternBlockEntity}. | ||||||
|  |      * | ||||||
|  |      * @param lectern The lectern holding this item. | ||||||
|  |      */ | ||||||
|  |     record LecternHolder(CustomLecternBlockEntity lectern) implements PocketHolder { | ||||||
|  |         @Override | ||||||
|  |         public ServerLevel level() { | ||||||
|  |             return (ServerLevel) lectern.getLevel(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public Vec3 pos() { | ||||||
|  |             return Vec3.atCenterOf(lectern.getBlockPos()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public BlockPos blockPos() { | ||||||
|  |             return lectern.getBlockPos(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public boolean isValid(ServerComputer computer) { | ||||||
|  |             return !lectern().isRemoved() && PocketComputerItem.isServerComputer(computer, lectern.getItem()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public void setChanged() { | ||||||
|  |             BlockEntityHelpers.updateBlock(lectern()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public boolean isTerminalAlwaysVisible() { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -72,7 +72,7 @@ public final class PocketServerComputer extends ServerComputer { | |||||||
|             // Broadcast the state to new players. |             // Broadcast the state to new players. | ||||||
|             var added = newTracking.stream().filter(x -> !tracking.contains(x)).toList(); |             var added = newTracking.stream().filter(x -> !tracking.contains(x)).toList(); | ||||||
|             if (!added.isEmpty()) { |             if (!added.isEmpty()) { | ||||||
|                 ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), added); |                 ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, brain.holder().isTerminalAlwaysVisible()), added); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @@ -83,9 +83,15 @@ public final class PocketServerComputer extends ServerComputer { | |||||||
|     protected void onTerminalChanged() { |     protected void onTerminalChanged() { | ||||||
|         super.onTerminalChanged(); |         super.onTerminalChanged(); | ||||||
| 
 | 
 | ||||||
|         if (brain.holder() instanceof PocketHolder.PlayerHolder holder && holder.isValid(this)) { |         var holder = brain.holder() instanceof PocketHolder.PlayerHolder h && h.isValid(this) ? h.entity() : null; | ||||||
|             // Broadcast the terminal to the current player. |         if (brain.holder().isTerminalAlwaysVisible() && !tracking.isEmpty()) { | ||||||
|             ServerNetworking.sendToPlayer(new PocketComputerDataMessage(this, true), holder.entity()); |             // If the terminal is always visible, send it to all players *and* the holder. | ||||||
|  |             var packet = new PocketComputerDataMessage(this, true); | ||||||
|  |             ServerNetworking.sendToPlayers(packet, tracking); | ||||||
|  |             if (holder != null && !tracking.contains(holder)) ServerNetworking.sendToPlayer(packet, holder); | ||||||
|  |         } else if (holder != null) { | ||||||
|  |             // Otherwise just send it to the holder. | ||||||
|  |             ServerNetworking.sendToPlayer(new PocketComputerDataMessage(this, true), holder); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ import dan200.computercraft.shared.computer.core.ServerContext; | |||||||
| import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; | import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; | ||||||
| import dan200.computercraft.shared.computer.items.IComputerItem; | import dan200.computercraft.shared.computer.items.IComputerItem; | ||||||
| import dan200.computercraft.shared.config.Config; | import dan200.computercraft.shared.config.Config; | ||||||
|  | import dan200.computercraft.shared.lectern.CustomLecternBlock; | ||||||
| import dan200.computercraft.shared.network.container.ComputerContainerData; | import dan200.computercraft.shared.network.container.ComputerContainerData; | ||||||
| import dan200.computercraft.shared.platform.PlatformHelper; | import dan200.computercraft.shared.platform.PlatformHelper; | ||||||
| import dan200.computercraft.shared.pocket.core.PocketBrain; | import dan200.computercraft.shared.pocket.core.PocketBrain; | ||||||
| @@ -44,6 +45,7 @@ import net.minecraft.world.entity.player.Player; | |||||||
| import net.minecraft.world.item.Item; | import net.minecraft.world.item.Item; | ||||||
| import net.minecraft.world.item.ItemStack; | import net.minecraft.world.item.ItemStack; | ||||||
| import net.minecraft.world.item.TooltipFlag; | import net.minecraft.world.item.TooltipFlag; | ||||||
|  | import net.minecraft.world.item.context.UseOnContext; | ||||||
| import net.minecraft.world.level.Level; | import net.minecraft.world.level.Level; | ||||||
| 
 | 
 | ||||||
| import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||||
| @@ -83,10 +85,18 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I | |||||||
|      * |      * | ||||||
|      * @param stack   The current pocket computer stack. |      * @param stack   The current pocket computer stack. | ||||||
|      * @param holder  The entity holding the pocket item. |      * @param holder  The entity holding the pocket item. | ||||||
|      * @param brain  The pocket computer brain. |      * @param passive If set, the pocket computer will not be created if it doesn't exist, and will not be kept alive. | ||||||
|      */ |      */ | ||||||
|     private void tick(ItemStack stack, PocketHolder holder, PocketBrain brain) { |     public void tick(ItemStack stack, PocketHolder holder, boolean passive) { | ||||||
|         brain.updateHolder(holder); |         PocketBrain brain; | ||||||
|  |         if (passive) { | ||||||
|  |             var computer = getServerComputer(holder.level().getServer(), stack); | ||||||
|  |             if (computer == null) return; | ||||||
|  |             brain = computer.getBrain(); | ||||||
|  |         } else { | ||||||
|  |             brain = getOrCreateBrain(holder.level(), holder, stack); | ||||||
|  |             brain.computer().keepAlive(); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         // Update pocket upgrade |         // Update pocket upgrade | ||||||
|         var upgrade = brain.getUpgrade(); |         var upgrade = brain.getUpgrade(); | ||||||
| @@ -139,11 +149,7 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I | |||||||
|         if (slot < 0) return; |         if (slot < 0) return; | ||||||
| 
 | 
 | ||||||
|         // If we're in the inventory, create a computer and keep it alive. |         // If we're in the inventory, create a computer and keep it alive. | ||||||
|         var holder = new PocketHolder.PlayerHolder(player, slot); |         tick(stack, new PocketHolder.PlayerHolder(player, slot), false); | ||||||
|         var brain = getOrCreateBrain((ServerLevel) world, holder, stack); |  | ||||||
|         brain.computer().keepAlive(); |  | ||||||
| 
 |  | ||||||
|         tick(stack, holder, brain); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @ForgeOverride |     @ForgeOverride | ||||||
| @@ -153,12 +159,16 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I | |||||||
| 
 | 
 | ||||||
|         // If we're an item entity, tick an already existing computer (as to update the position), but do not keep the |         // If we're an item entity, tick an already existing computer (as to update the position), but do not keep the | ||||||
|         // computer alive. |         // computer alive. | ||||||
|         var computer = getServerComputer(level.getServer(), stack); |         tick(stack, new PocketHolder.ItemEntityHolder(entity), true); | ||||||
|         if (computer != null) tick(stack, new PocketHolder.ItemEntityHolder(entity), computer.getBrain()); |  | ||||||
| 
 | 
 | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Override | ||||||
|  |     public InteractionResult useOn(UseOnContext context) { | ||||||
|  |         return CustomLecternBlock.defaultUseItemOn(context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) { |     public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) { | ||||||
|         var stack = player.getItemInHand(hand); |         var stack = player.getItemInHand(hand); | ||||||
| @@ -171,25 +181,39 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I | |||||||
|             var stop = false; |             var stop = false; | ||||||
|             var upgrade = getUpgrade(stack); |             var upgrade = getUpgrade(stack); | ||||||
|             if (upgrade != null) { |             if (upgrade != null) { | ||||||
|                 brain.updateHolder(holder); |  | ||||||
|                 stop = upgrade.onRightClick(world, brain, computer.getPeripheral(ComputerSide.BACK)); |                 stop = upgrade.onRightClick(world, brain, computer.getPeripheral(ComputerSide.BACK)); | ||||||
|                 // Sync back just in case. We don't need to setChanged, as we'll return the item anyway. |                 // Sync back just in case. We don't need to setChanged, as we'll return the item anyway. | ||||||
|                 updateItem(stack, brain); |                 updateItem(stack, brain); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if (!stop) { |             if (!stop) openImpl(player, stack, holder, hand == InteractionHand.OFF_HAND, computer); | ||||||
|                 PlatformHelper.get().openMenu( |  | ||||||
|                     player, stack.getHoverName(), |  | ||||||
|                     (id, inventory, entity) -> new ComputerMenuWithoutInventory( |  | ||||||
|                         hand == InteractionHand.OFF_HAND ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.COMPUTER.get(), |  | ||||||
|                         id, inventory, p -> isServerComputer(computer, p.getItemInHand(hand)), computer |  | ||||||
|                     ), |  | ||||||
|                     new ComputerContainerData(computer, stack)); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), stack); |         return new InteractionResultHolder<>(InteractionResult.sidedSuccess(world.isClientSide), stack); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Open a container for this pocket computer. | ||||||
|  |      * | ||||||
|  |      * @param player       The player to show the menu for. | ||||||
|  |      * @param stack        The pocket computer stack. | ||||||
|  |      * @param holder       The holder of the pocket computer. | ||||||
|  |      * @param isTypingOnly Open the off-hand pocket screen (only supporting typing, with no visible terminal). | ||||||
|  |      */ | ||||||
|  |     public void open(Player player, ItemStack stack, PocketHolder holder, boolean isTypingOnly) { | ||||||
|  |         var brain = getOrCreateBrain(holder.level(), holder, stack); | ||||||
|  |         var computer = brain.computer(); | ||||||
|  |         computer.turnOn(); | ||||||
|  |         openImpl(player, stack, holder, isTypingOnly, computer); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static void openImpl(Player player, ItemStack stack, PocketHolder holder, boolean isTypingOnly, ServerComputer computer) { | ||||||
|  |         PlatformHelper.get().openMenu(player, stack.getHoverName(), (id, inventory, entity) -> new ComputerMenuWithoutInventory( | ||||||
|  |             isTypingOnly ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.COMPUTER.get(), id, inventory, | ||||||
|  |             p -> holder.isValid(computer), | ||||||
|  |             computer | ||||||
|  |         ), new ComputerContainerData(computer, stack)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public Component getName(ItemStack stack) { |     public Component getName(ItemStack stack) { | ||||||
|         var baseString = getDescriptionId(stack); |         var baseString = getDescriptionId(stack); | ||||||
| @@ -233,7 +257,11 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I | |||||||
|         var registry = ServerContext.get(level.getServer()).registry(); |         var registry = ServerContext.get(level.getServer()).registry(); | ||||||
|         { |         { | ||||||
|             var computer = getServerComputer(registry, stack); |             var computer = getServerComputer(registry, stack); | ||||||
|             if (computer != null) return computer.getBrain(); |             if (computer != null) { | ||||||
|  |                 var brain = computer.getBrain(); | ||||||
|  |                 brain.updateHolder(holder); | ||||||
|  |                 return brain; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         var computerID = getComputerID(stack); |         var computerID = getComputerID(stack); | ||||||
| @@ -252,8 +280,7 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I | |||||||
|         tag.putInt(NBT_SESSION, registry.getSessionID()); |         tag.putInt(NBT_SESSION, registry.getSessionID()); | ||||||
|         tag.putUUID(NBT_INSTANCE, computer.register()); |         tag.putUUID(NBT_INSTANCE, computer.register()); | ||||||
| 
 | 
 | ||||||
|         // Only turn on when initially creating the computer, rather than each tick. |         if (isMarkedOn(stack)) computer.turnOn(); | ||||||
|         if (isMarkedOn(stack) && holder instanceof PocketHolder.PlayerHolder) computer.turnOn(); |  | ||||||
| 
 | 
 | ||||||
|         updateItem(stack, brain); |         updateItem(stack, brain); | ||||||
| 
 | 
 | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 152 B | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 179 B | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 119 B | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 92 B | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 152 B | 
		Reference in New Issue
	
	Block a user
	 Jonathan Coates
					Jonathan Coates