Skip to content

Commit 3f8c3b0

Browse files
committed
Merge branch 'mc-1.20.x' into mc-1.21.x
2 parents 1278246 + 0a8d505 commit 3f8c3b0

File tree

41 files changed

+1206
-131
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1206
-131
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ neogradle.subsystems.conventions.runs.enabled=false
1212

1313
# Mod properties
1414
isUnstable=true
15-
modVersion=1.114.5
15+
modVersion=1.115.0
1616

1717
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
1818
mcVersion=1.21.1
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
package dan200.computercraft.api.media;
6+
7+
import dan200.computercraft.impl.ComputerCraftAPIService;
8+
import net.minecraft.world.item.ItemStack;
9+
10+
import javax.annotation.Nullable;
11+
import java.util.stream.Stream;
12+
13+
/**
14+
* The contents of a page (or book) created by a ComputerCraft printer.
15+
*
16+
* @since 1.115
17+
*/
18+
@Nullable
19+
public interface PrintoutContents {
20+
/**
21+
* Get the (possibly empty) title for this printout.
22+
*
23+
* @return The title of this printout.
24+
*/
25+
String getTitle();
26+
27+
/**
28+
* Get the text contents of this printout, as a sequence of lines.
29+
* <p>
30+
* The lines in the printout may include blank lines at the end of the document, as well as trailing spaces on each
31+
* line.
32+
*
33+
* @return The text contents of this printout.
34+
*/
35+
Stream<String> getTextLines();
36+
37+
/**
38+
* Get the printout contents for a particular stack.
39+
*
40+
* @param stack The stack to get the contents for.
41+
* @return The printout contents, or {@code null} if this is not a printout item.
42+
*/
43+
static @Nullable PrintoutContents get(ItemStack stack) {
44+
return ComputerCraftAPIService.get().getPrintoutContents(stack);
45+
}
46+
}

projects/common-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import dan200.computercraft.api.lua.GenericSource;
1414
import dan200.computercraft.api.lua.ILuaAPIFactory;
1515
import dan200.computercraft.api.media.MediaProvider;
16+
import dan200.computercraft.api.media.PrintoutContents;
1617
import dan200.computercraft.api.network.PacketNetwork;
1718
import dan200.computercraft.api.network.wired.WiredElement;
1819
import dan200.computercraft.api.network.wired.WiredNode;
@@ -84,6 +85,9 @@ static ComputerCraftAPIService get() {
8485

8586
DetailRegistry<BlockReference> getBlockInWorldDetailRegistry();
8687

88+
@Nullable
89+
PrintoutContents getPrintoutContents(ItemStack stack);
90+
8791
final class Instance {
8892
static final @Nullable ComputerCraftAPIService INSTANCE;
8993
static final @Nullable Throwable ERROR;

projects/common/src/client/java/dan200/computercraft/client/gui/widgets/TerminalWidget.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public TerminalWidget(Terminal terminal, InputHandler computer, int x, int y) {
7070
@Override
7171
public boolean charTyped(char ch, int modifiers) {
7272
var terminalChar = StringUtil.unicodeToTerminal(ch);
73-
if (StringUtil.isTypableChar(terminalChar)) computer.charTyped(terminalChar);
73+
if (StringUtil.isTypableChar(terminalChar)) computer.charTyped((byte) terminalChar);
7474
return true;
7575
}
7676

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
package dan200.computercraft.client.model;
6+
7+
import com.mojang.blaze3d.vertex.PoseStack;
8+
import dan200.computercraft.api.ComputerCraftAPI;
9+
import dan200.computercraft.client.pocket.PocketComputerData;
10+
import dan200.computercraft.client.render.CustomLecternRenderer;
11+
import dan200.computercraft.shared.computer.core.ComputerFamily;
12+
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
13+
import net.minecraft.client.model.geom.ModelPart;
14+
import net.minecraft.client.model.geom.PartPose;
15+
import net.minecraft.client.model.geom.builders.CubeListBuilder;
16+
import net.minecraft.client.model.geom.builders.MeshDefinition;
17+
import net.minecraft.client.renderer.LightTexture;
18+
import net.minecraft.client.renderer.MultiBufferSource;
19+
import net.minecraft.client.renderer.RenderType;
20+
import net.minecraft.client.resources.model.Material;
21+
import net.minecraft.resources.ResourceLocation;
22+
import net.minecraft.world.inventory.InventoryMenu;
23+
import net.minecraft.world.item.component.DyedItemColor;
24+
25+
/**
26+
* A model for {@linkplain PocketComputerItem pocket computers} placed on a lectern.
27+
*
28+
* @see CustomLecternRenderer
29+
*/
30+
public class LecternPocketModel {
31+
public static final ResourceLocation TEXTURE_NORMAL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_normal");
32+
public static final ResourceLocation TEXTURE_ADVANCED = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_advanced");
33+
public static final ResourceLocation TEXTURE_COLOUR = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_colour");
34+
public static final ResourceLocation TEXTURE_FRAME = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_frame");
35+
public static final ResourceLocation TEXTURE_LIGHT = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_light");
36+
37+
private static final Material MATERIAL_NORMAL = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_NORMAL);
38+
private static final Material MATERIAL_ADVANCED = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_ADVANCED);
39+
private static final Material MATERIAL_COLOUR = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_COLOUR);
40+
private static final Material MATERIAL_FRAME = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_FRAME);
41+
private static final Material MATERIAL_LIGHT = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_LIGHT);
42+
43+
// The size of the terminal within the model.
44+
public static final float TERM_WIDTH = 12.0f / 32.0f;
45+
public static final float TERM_HEIGHT = 14.0f / 32.0f;
46+
47+
// The size of the texture. The texture is 36x36, but is at 2x resolution.
48+
private static final int TEXTURE_WIDTH = 36 / 2;
49+
private static final int TEXTURE_HEIGHT = 36 / 2;
50+
51+
private final ModelPart root;
52+
53+
public LecternPocketModel() {
54+
root = buildPages();
55+
}
56+
57+
private static ModelPart buildPages() {
58+
var mesh = new MeshDefinition();
59+
var parts = mesh.getRoot();
60+
parts.addOrReplaceChild(
61+
"root",
62+
CubeListBuilder.create().texOffs(0, 0).addBox(0f, -5.0f, -4.0f, 1f, 10.0f, 8.0f),
63+
PartPose.ZERO
64+
);
65+
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
66+
}
67+
68+
/**
69+
* Render the pocket computer model.
70+
*
71+
* @param poseStack The current pose stack.
72+
* @param bufferSource The buffer source to draw to.
73+
* @param packedLight The current light level.
74+
* @param packedOverlay The overlay texture (used for entity hurt animation).
75+
* @param family The computer family.
76+
* @param frameColour The pocket computer's {@linkplain DyedItemColor colour}.
77+
* @param lightColour The pocket computer's {@linkplain PocketComputerData#getLightState() light colour}.
78+
*/
79+
public void render(PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay, ComputerFamily family, int frameColour, int lightColour) {
80+
if (frameColour != -1) {
81+
root.render(poseStack, MATERIAL_FRAME.buffer(bufferSource, RenderType::entityCutout), packedLight, packedOverlay);
82+
root.render(poseStack, MATERIAL_COLOUR.buffer(bufferSource, RenderType::entityCutout), packedLight, packedOverlay, frameColour);
83+
} else {
84+
var buffer = (family == ComputerFamily.ADVANCED ? MATERIAL_ADVANCED : MATERIAL_NORMAL).buffer(bufferSource, RenderType::entityCutout);
85+
root.render(poseStack, buffer, packedLight, packedOverlay);
86+
}
87+
88+
root.render(poseStack, MATERIAL_LIGHT.buffer(bufferSource, RenderType::entityCutout), LightTexture.FULL_BRIGHT, packedOverlay, lightColour);
89+
}
90+
}

projects/common/src/client/java/dan200/computercraft/client/render/CustomLecternRenderer.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,48 @@
66

77
import com.mojang.blaze3d.vertex.PoseStack;
88
import com.mojang.math.Axis;
9+
import dan200.computercraft.client.model.LecternPocketModel;
910
import dan200.computercraft.client.model.LecternPrintoutModel;
11+
import dan200.computercraft.client.pocket.ClientPocketComputers;
12+
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
13+
import dan200.computercraft.core.terminal.Terminal;
14+
import dan200.computercraft.core.util.Colour;
1015
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
1116
import dan200.computercraft.shared.media.items.PrintoutData;
1217
import dan200.computercraft.shared.media.items.PrintoutItem;
18+
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
1319
import net.minecraft.client.renderer.MultiBufferSource;
1420
import net.minecraft.client.renderer.RenderType;
21+
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
1522
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
1623
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
1724
import net.minecraft.client.renderer.blockentity.LecternRenderer;
25+
import net.minecraft.util.FastColor;
26+
import net.minecraft.world.item.component.DyedItemColor;
1827
import net.minecraft.world.level.block.LecternBlock;
28+
import net.minecraft.world.phys.Vec3;
29+
30+
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
31+
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
32+
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
1933

2034
/**
2135
* A block entity renderer for our {@linkplain CustomLecternBlockEntity lectern}.
2236
* <p>
2337
* This largely follows {@link LecternRenderer}, but with support for multiple types of item.
2438
*/
2539
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> {
40+
private static final int POCKET_TERMINAL_RENDER_DISTANCE = 32;
41+
42+
private final BlockEntityRenderDispatcher berDispatcher;
2643
private final LecternPrintoutModel printoutModel;
44+
private final LecternPocketModel pocketModel;
2745

2846
public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
47+
berDispatcher = context.getBlockEntityRenderDispatcher();
48+
2949
printoutModel = new LecternPrintoutModel();
50+
pocketModel = new LecternPocketModel();
3051
}
3152

3253
@Override
@@ -45,8 +66,46 @@ public void render(CustomLecternBlockEntity lectern, float partialTick, PoseStac
4566
} else {
4667
printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutData.getOrEmpty(item).pages());
4768
}
69+
} else if (item.getItem() instanceof PocketComputerItem pocket) {
70+
var computer = ClientPocketComputers.get(item);
71+
72+
pocketModel.render(
73+
poseStack, buffer, packedLight, packedOverlay, pocket.getFamily(), DyedItemColor.getOrDefault(item, -1),
74+
FastColor.ARGB32.opaque(computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState())
75+
);
76+
77+
// Jiggle the terminal about a bit, so (0, 0) is in the top left of the model's terminal hole.
78+
poseStack.mulPose(Axis.YP.rotationDegrees(90f));
79+
poseStack.translate(-0.5 * LecternPocketModel.TERM_WIDTH, 0.5 * LecternPocketModel.TERM_HEIGHT + 1f / 32.0f, 1 / 16.0f);
80+
poseStack.mulPose(Axis.XP.rotationDegrees(180));
81+
82+
// Either render the terminal or a black screen, depending on how close we are.
83+
var terminal = computer == null ? null : computer.getTerminal();
84+
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(poseStack, buffer.getBuffer(RenderTypes.TERMINAL));
85+
if (terminal != null && Vec3.atCenterOf(lectern.getBlockPos()).closerThan(berDispatcher.camera.getPosition(), POCKET_TERMINAL_RENDER_DISTANCE)) {
86+
renderPocketTerminal(poseStack, quadEmitter, terminal);
87+
} else {
88+
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, LecternPocketModel.TERM_WIDTH, LecternPocketModel.TERM_HEIGHT);
89+
}
4890
}
4991

5092
poseStack.popPose();
5193
}
94+
95+
private static void renderPocketTerminal(PoseStack poseStack, FixedWidthFontRenderer.QuadEmitter quadEmitter, Terminal terminal) {
96+
var width = terminal.getWidth() * FONT_WIDTH;
97+
var height = terminal.getHeight() * FONT_HEIGHT;
98+
99+
// Scale the terminal down to fit in the available space.
100+
var scaleX = LecternPocketModel.TERM_WIDTH / (width + MARGIN * 2);
101+
var scaleY = LecternPocketModel.TERM_HEIGHT / (height + MARGIN * 2);
102+
var scale = Math.min(scaleX, scaleY);
103+
poseStack.scale(scale, scale, -1.0f);
104+
105+
// Convert the model dimensions to terminal space, then find out how large the margin should be.
106+
var marginX = ((LecternPocketModel.TERM_WIDTH / scale) - width) / 2;
107+
var marginY = ((LecternPocketModel.TERM_HEIGHT / scale) - height) / 2;
108+
109+
FixedWidthFontRenderer.drawTerminal(quadEmitter, marginX, marginY, terminal, marginY, marginY, marginX, marginX);
110+
}
52111
}

projects/common/src/client/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public final class FixedWidthFontRenderer {
4343
static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
4444

4545
private static final int BLACK = FastColor.ARGB32.color(255, byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()));
46-
private static final float Z_OFFSET = 1e-3f;
46+
private static final float Z_OFFSET = 1e-4f;
4747

4848
private FixedWidthFontRenderer() {
4949
}

projects/common/src/datagen/java/dan200/computercraft/data/DataProviders.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import dan200.computercraft.api.pocket.IPocketUpgrade;
99
import dan200.computercraft.api.turtle.ITurtleUpgrade;
1010
import dan200.computercraft.client.gui.GuiSprites;
11+
import dan200.computercraft.client.model.LecternPocketModel;
1112
import dan200.computercraft.client.model.LecternPrintoutModel;
1213
import dan200.computercraft.data.client.ExtraModelsProvider;
1314
import dan200.computercraft.shared.turtle.TurtleOverlay;
@@ -71,7 +72,9 @@ public static void add(GeneratorSink generator) {
7172
out.accept(ResourceLocation.withDefaultNamespace("blocks"), makeSprites(Stream.of(
7273
UpgradeSlot.LEFT_UPGRADE,
7374
UpgradeSlot.RIGHT_UPGRADE,
74-
LecternPrintoutModel.TEXTURE
75+
LecternPrintoutModel.TEXTURE,
76+
LecternPocketModel.TEXTURE_NORMAL, LecternPocketModel.TEXTURE_ADVANCED,
77+
LecternPocketModel.TEXTURE_COLOUR, LecternPocketModel.TEXTURE_FRAME, LecternPocketModel.TEXTURE_LIGHT
7578
)));
7679
out.accept(GuiSprites.SPRITE_SHEET, makeSprites(
7780
// Computers

projects/common/src/generated/resources/assets/minecraft/atlases/blocks.json

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

projects/common/src/main/java/dan200/computercraft/impl/AbstractComputerCraftAPI.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import dan200.computercraft.api.lua.GenericSource;
1414
import dan200.computercraft.api.lua.ILuaAPIFactory;
1515
import dan200.computercraft.api.media.MediaProvider;
16+
import dan200.computercraft.api.media.PrintoutContents;
1617
import dan200.computercraft.api.network.PacketNetwork;
1718
import dan200.computercraft.api.network.wired.WiredElement;
1819
import dan200.computercraft.api.network.wired.WiredNode;
@@ -25,6 +26,7 @@
2526
import dan200.computercraft.impl.detail.DetailRegistryImpl;
2627
import dan200.computercraft.impl.network.wired.WiredNodeImpl;
2728
import dan200.computercraft.impl.upgrades.TurtleToolSpec;
29+
import dan200.computercraft.shared.ModRegistry;
2830
import dan200.computercraft.shared.computer.core.ResourceMount;
2931
import dan200.computercraft.shared.computer.core.ServerContext;
3032
import dan200.computercraft.shared.details.BlockDetails;
@@ -153,4 +155,9 @@ public final DetailRegistry<ItemStack> getItemStackDetailRegistry() {
153155
public final DetailRegistry<BlockReference> getBlockInWorldDetailRegistry() {
154156
return blockDetails;
155157
}
158+
159+
@Override
160+
public @Nullable PrintoutContents getPrintoutContents(ItemStack stack) {
161+
return stack.get(ModRegistry.DataComponents.PRINTOUT.get());
162+
}
156163
}

0 commit comments

Comments
 (0)