1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-04-20 01:33:13 +00:00

Merge branch 'mc-1.20.x' into mc-1.21.x

This commit is contained in:
Jonathan Coates 2024-09-11 19:28:13 +01:00
commit 4f3247a0e2
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
27 changed files with 422 additions and 196 deletions
CONTRIBUTING.mdcrowdin.ymlgradle.properties
gradle
projects
common
build.gradle.kts
src
client/java/dan200/computercraft/client/render
main/resources/assets/computercraft/lang
testMod
kotlin/dan200/computercraft/gametest
resources/data/cctest/structures
core
build.gradle.kts
src
main
java/dan200/computercraft/core
resources/data/computercraft/lua/rom
test/resources/test-rom/spec/apis
fabric/src/client/java/dan200/computercraft/client/integration
forge
build.gradle.kts
src/client/java/dan200/computercraft/client/integration

@ -12,6 +12,7 @@ If you've any other questions, [just ask the community][community] or [open an i
## Table of Contents
- [Reporting issues](#reporting-issues)
- [Translations](#translations)
- [Setting up a development environment](#setting-up-a-development-environment)
- [Developing CC: Tweaked](#developing-cc-tweaked)
- [Writing documentation](#writing-documentation)
@ -20,6 +21,10 @@ If you've any other questions, [just ask the community][community] or [open an i
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, do
use the issue templates - they provide a useful hint on what information to provide.
## Translations
Translations are managed through [CrowdIn], an online interface for managing language strings. Translations may either
be contributed there, or directly via a pull request.
## Setting up a development environment
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
@ -102,3 +107,4 @@ about how you can build on that until you've covered everything!
[busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing."
[node]: https://nodejs.org/en/ "Node.js"
[architecture]: projects/ARCHITECTURE.md
[Crowdin]: https://crowdin.com/project/cc-tweaked/

26
crowdin.yml Normal file

@ -0,0 +1,26 @@
# SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
#
# SPDX-License-Identifier: MPL-2.0
files:
- source: projects/common/src/generated/resources/assets/computercraft/lang/en_us.json
translation: /projects/common/src/main/resources/assets/computercraft/lang/%locale_with_underscore%.json
languages_mapping:
locale_with_underscore:
cs: cs_cs # Czech
da: da_dk # Danish
de: de_de # German
es-ES: es_es # Spanish
fr: fr_fr # French
it: it_it # Italian
ja: ja_jp # Japanese
ko: ko_kr # Korean
nb: nb_no # Norwegian Bokmal
nl: nl_nl # Dutch
pl: pl_pl # Polish
pt-BR: pt_br # Portuguese, Brazilian
ru: ru_ru # Russian
sv-SE: sv_se # Sweedish
tok: tok # Toki Pona
uk: uk_ua # Ukraine
vi: vi_vn # Vietnamese
zh-CN: zh_cn # Chinese Simplified

@ -12,7 +12,7 @@ neogradle.subsystems.conventions.runs.enabled=false
# Mod properties
isUnstable=true
modVersion=1.113.0
modVersion=1.113.1
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
mcVersion=1.21.1

@ -71,6 +71,7 @@ lwjgl = "3.3.3"
minotaur = "2.8.7"
neoGradle = "7.0.152"
nullAway = "0.10.25"
shadow = "8.3.1"
spotless = "6.23.3"
taskTree = "2.1.1"
teavm = "0.11.0-SQUID.1"
@ -94,9 +95,10 @@ jzlib = { module = "com.jcraft:jzlib", version.ref = "jzlib" }
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" }
netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
netty-socks = { module = "io.netty:netty-codec-socks", version.ref = "netty" }
netty-proxy = { module = "io.netty:netty-handler-proxy", version.ref = "netty" }
netty-socks = { module = "io.netty:netty-codec-socks", version.ref = "netty" }
nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" }
nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" }
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
@ -173,6 +175,7 @@ yarn = { module = "net.fabricmc:yarn", version.ref = "yarn" }
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }

@ -38,9 +38,9 @@ repositories {
dependencies {
// Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
implementation(project(":core"))
implementation(commonClasses(project(":common-api")))
clientImplementation(clientClasses(project(":common-api")))
api(project(":core"))
api(commonClasses(project(":common-api")))
clientApi(clientClasses(project(":common-api")))
compileOnly(libs.mixin)
compileOnly(libs.mixinExtra)

@ -14,6 +14,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.util.FastColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.DyedItemColor;
import org.joml.Matrix4f;
@ -93,16 +94,11 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
}
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
var r = (byte) ((colour >>> 16) & 0xFF);
var g = (byte) ((colour >>> 8) & 0xFF);
var b = (byte) (colour & 0xFF);
var c = new byte[]{ r, g, b, (byte) 255 };
var buffer = render.getBuffer(RenderTypes.TERMINAL);
FixedWidthFontRenderer.drawQuad(
FixedWidthFontRenderer.toVertexConsumer(transform, buffer),
width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT,
c, RenderTypes.FULL_BRIGHT_LIGHTMAP
FastColor.ARGB32.opaque(colour), RenderTypes.FULL_BRIGHT_LIGHTMAP
);
}
}

@ -12,9 +12,11 @@ import dan200.computercraft.core.terminal.Palette;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.core.util.Colour;
import net.minecraft.util.FastColor;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.*;
import static org.lwjgl.system.MemoryUtil.*;
@ -37,10 +39,12 @@ import static org.lwjgl.system.MemoryUtil.*;
* {@link FixedWidthFontRenderer}.
*/
public final class DirectFixedWidthFontRenderer {
private static final boolean IS_LITTLE_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
private DirectFixedWidthFontRenderer() {
}
private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour) {
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour) {
// Short circuit to avoid the common case - the texture should be blank here after all.
if (index == '\0' || index == ' ') return;
@ -157,8 +161,8 @@ public final class DirectFixedWidthFontRenderer {
return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2;
}
private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
buffer.quad(x1, y1, x2, y2, z, rgba, u1, v1, u2, v2);
private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
buffer.quad(x1, y1, x2, y2, z, colour, u1, v1, u2, v2);
}
public interface QuadEmitter {
@ -166,7 +170,7 @@ public final class DirectFixedWidthFontRenderer {
ByteBuffer buffer();
void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2);
void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2);
}
public record ByteBufferEmitter(ByteBuffer buffer) implements QuadEmitter {
@ -176,12 +180,12 @@ public final class DirectFixedWidthFontRenderer {
}
@Override
public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, rgba, u1, v1, u2, v2);
public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, colour, u1, v1, u2, v2);
}
}
static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
// Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the
// underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write.
// This provides significant performance gains, at the cost of well, using Unsafe.
@ -195,16 +199,15 @@ public final class DirectFixedWidthFontRenderer {
if (position < 0 || 112 > buffer.limit() - position) throw new IndexOutOfBoundsException();
// Require the pointer to be aligned to a 32-bit boundary.
if ((addr & 3) != 0) throw new IllegalStateException("Memory is not aligned");
// Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances.
if (rgba.length != 4) throw new IllegalStateException();
// Pack colour so it is equivalent to rgba:BBBB. This matches the logic in BufferBuilder.
var colourAbgr = FastColor.ABGR32.fromArgb32(colour);
var nativeColour = IS_LITTLE_ENDIAN ? colourAbgr : Integer.reverseBytes(colourAbgr);
memPutFloat(addr + 0, x1);
memPutFloat(addr + 4, y1);
memPutFloat(addr + 8, z);
memPutByte(addr + 12, rgba[0]);
memPutByte(addr + 13, rgba[1]);
memPutByte(addr + 14, rgba[2]);
memPutByte(addr + 15, (byte) 255);
memPutInt(addr + 12, nativeColour);
memPutFloat(addr + 16, u1);
memPutFloat(addr + 20, v1);
memPutShort(addr + 24, (short) 0xF0);
@ -213,10 +216,7 @@ public final class DirectFixedWidthFontRenderer {
memPutFloat(addr + 28, x1);
memPutFloat(addr + 32, y2);
memPutFloat(addr + 36, z);
memPutByte(addr + 40, rgba[0]);
memPutByte(addr + 41, rgba[1]);
memPutByte(addr + 42, rgba[2]);
memPutByte(addr + 43, (byte) 255);
memPutInt(addr + 40, nativeColour);
memPutFloat(addr + 44, u1);
memPutFloat(addr + 48, v2);
memPutShort(addr + 52, (short) 0xF0);
@ -225,10 +225,7 @@ public final class DirectFixedWidthFontRenderer {
memPutFloat(addr + 56, x2);
memPutFloat(addr + 60, y2);
memPutFloat(addr + 64, z);
memPutByte(addr + 68, rgba[0]);
memPutByte(addr + 69, rgba[1]);
memPutByte(addr + 70, rgba[2]);
memPutByte(addr + 71, (byte) 255);
memPutInt(addr + 68, nativeColour);
memPutFloat(addr + 72, u2);
memPutFloat(addr + 76, v2);
memPutShort(addr + 80, (short) 0xF0);
@ -237,10 +234,7 @@ public final class DirectFixedWidthFontRenderer {
memPutFloat(addr + 84, x2);
memPutFloat(addr + 88, y1);
memPutFloat(addr + 92, z);
memPutByte(addr + 96, rgba[0]);
memPutByte(addr + 97, rgba[1]);
memPutByte(addr + 98, rgba[2]);
memPutByte(addr + 99, (byte) 255);
memPutInt(addr + 96, nativeColour);
memPutFloat(addr + 100, u2);
memPutFloat(addr + 104, v1);
memPutShort(addr + 108, (short) 0xF0);

@ -12,6 +12,7 @@ import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.core.util.Colour;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.FastColor;
import org.joml.Matrix4f;
import org.joml.Vector3f;
@ -41,7 +42,7 @@ public final class FixedWidthFontRenderer {
static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
private static final byte[] BLACK = new byte[]{ byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), (byte) 255 };
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 FixedWidthFontRenderer() {
@ -59,7 +60,7 @@ public final class FixedWidthFontRenderer {
return 15 - Terminal.getColour(c, def);
}
private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour, int light) {
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour, int light) {
// Short circuit to avoid the common case - the texture should be blank here after all.
if (index == '\0' || index == ' ') return;
@ -75,7 +76,7 @@ public final class FixedWidthFontRenderer {
);
}
public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light) {
public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, int colour, int light) {
quad(emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light);
}
@ -216,10 +217,10 @@ public final class FixedWidthFontRenderer {
return new QuadEmitter(transform.last().pose(), consumer);
}
private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light) {
private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2, int light) {
var poseMatrix = c.poseMatrix();
var consumer = c.consumer();
byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3];
int r = FastColor.ARGB32.red(colour), g = FastColor.ARGB32.green(colour), b = FastColor.ARGB32.blue(colour), a = FastColor.ARGB32.alpha(colour);
consumer.addVertex(poseMatrix, x1, y1, z).setColor(r, g, b, a).setUv(u1, v1).setLight(light);
consumer.addVertex(poseMatrix, x1, y2, z).setColor(r, g, b, a).setUv(u1, v2).setLight(light);

@ -1,8 +1,14 @@
{
"argument.computercraft.argument_expected": "引数が期待される",
"argument.computercraft.computer.distance": "エンティティまでの距離",
"argument.computercraft.computer.family": "コンピューターファミリー",
"argument.computercraft.computer.id": "コンピューターID",
"argument.computercraft.computer.instance": "固有インスタンスID",
"argument.computercraft.computer.label": "コンピューターラベル",
"argument.computercraft.computer.many_matching": "'%s'に一致する複数のコンピューター (インスタンス %s)",
"argument.computercraft.computer.no_matching": "'%s'に一致するコンピュータはありません",
"argument.computercraft.computer.no_matching": "'%s'に一致するコンピュータはありません",
"argument.computercraft.tracking_field.no_field": "'%s'は未知のフィールドです",
"argument.computercraft.unknown_computer_family": "'%s'は未知のコンピューターファミリーです",
"block.computercraft.cable": "ネットワークケーブル",
"block.computercraft.computer_advanced": "高度なコンピューター",
"block.computercraft.computer_command": "コマンドコンピューター",
@ -12,9 +18,9 @@
"block.computercraft.monitor_normal": "モニター",
"block.computercraft.printer": "プリンター",
"block.computercraft.speaker": "スピーカー",
"block.computercraft.turtle_advanced": "高度なタートル",
"block.computercraft.turtle_advanced.upgraded": "高度な%sタートル",
"block.computercraft.turtle_advanced.upgraded_twice": "高度な%s%sタートル",
"block.computercraft.turtle_advanced": "アドバンスドタートル",
"block.computercraft.turtle_advanced.upgraded": "アドバンスド%sタートル",
"block.computercraft.turtle_advanced.upgraded_twice": "アドバンスド%s%sタートル",
"block.computercraft.turtle_normal": "タートル",
"block.computercraft.turtle_normal.upgraded": "%sタートル",
"block.computercraft.turtle_normal.upgraded_twice": "%s%sタートル",
@ -24,11 +30,11 @@
"block.computercraft.wireless_modem_normal": "無線モデム",
"chat.computercraft.wired_modem.peripheral_connected": "周辺の\"%s\"のネットワークに接続されました",
"chat.computercraft.wired_modem.peripheral_disconnected": "周辺の\"%s\"のネットワークから切断されました",
"commands.computercraft.desc": "/computercraft コマンドは、コンピュータとの制御および対話するためのさまざまなデバッグツールと管理者ツールを提供します。",
"commands.computercraft.dump.action": "このコンピュータの詳細を表示します",
"commands.computercraft.dump.desc": "すべてのコンピューターの状態、または一台のコンピューターの特定の情報を表示する。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
"commands.computercraft.dump.open_path": "このコンピュータのファイルを表示します",
"commands.computercraft.dump.synopsis": "コンピュータの状態を表示します。",
"commands.computercraft.desc": "/computercraft コマンドは、コンピュータとの制御および対話するためのさまざまなデバッグツールと管理者ツールを提供します。",
"commands.computercraft.dump.action": "このコンピュータの詳細を表示します",
"commands.computercraft.dump.desc": "すべてのコンピューターの状態、または一台のコンピューターの特定の情報を表示する。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
"commands.computercraft.dump.open_path": "このコンピュータのファイルを表示します",
"commands.computercraft.dump.synopsis": "コンピュータの状態を表示します。",
"commands.computercraft.generic.additional_rows": "%d行を追加…",
"commands.computercraft.generic.exception": "未処理の例外 (%s)",
"commands.computercraft.generic.no": "N",
@ -39,65 +45,182 @@
"commands.computercraft.help.no_children": "%s にサブコマンドはありません",
"commands.computercraft.help.no_command": "%s というコマンドはありません",
"commands.computercraft.help.synopsis": "特定のコマンドのヘルプを提供します",
"commands.computercraft.queue.desc": "追加の引数を通過する computer_command インベントをコマンドコンピューターに送信します。これは主にマップメーカーのために設計されており、よりコンピュータフレンドリーバージョンの /trigger として機能します。 どのプレイヤーでもコマンドを実行できます。これは、テキストコンポーネントのクリックイベントを介して行われる可能性があります。",
"commands.computercraft.queue.desc": "追加の引数を通過する computer_command インベントをコマンドコンピューターに送信します。これは主にマップメーカーのために設計されており、よりコンピュータフレンドリーバージョンの /trigger として機能します。 どのプレイヤーでもコマンドを実行できます。これは、テキストコンポーネントのクリックイベントを介して行われる可能性があります。",
"commands.computercraft.queue.synopsis": "computer_command インベントをコマンドコンピューターに送信します",
"commands.computercraft.shutdown.desc": "指定されたコンピュータ、指定されていない場合はすべてのコンピュータをシャットダウンします。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
"commands.computercraft.shutdown.desc": "指定されたコンピュータ、指定されていない場合はすべてのコンピュータをシャットダウンします。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
"commands.computercraft.shutdown.done": "%s/%s コンピューターをシャットダウンしました",
"commands.computercraft.shutdown.synopsis": "コンピュータをリモートでシャットダウンする。",
"commands.computercraft.synopsis": "コンピュータを制御するためのさまざまなコマンド。",
"commands.computercraft.shutdown.synopsis": "コンピュータをリモートでシャットダウンする。",
"commands.computercraft.synopsis": "コンピュータを制御するためのさまざまなコマンド。",
"commands.computercraft.tp.action": "このコンピューターへテレポートします",
"commands.computercraft.tp.desc": "コンピュータの場所にテレポート.コンピュータのインスタンスID例えば 123またはコンピュータID例えば #123を指定することができます。",
"commands.computercraft.tp.synopsis": "特定のコンピュータにテレポート。",
"commands.computercraft.track.desc": "コンピュータの実行時間を追跡するだけでなく、イベントを確認することができます。 これは /forge と同様の方法で情報を提示し、遅れを診断するのに役立ちます。",
"commands.computercraft.tp.desc": "コンピュータの場所にテレポート.コンピュータのインスタンスID例えば 123またはコンピュータID例えば #123を指定することができます。",
"commands.computercraft.tp.synopsis": "特定のコンピュータにテレポート。",
"commands.computercraft.track.desc": "コンピュータの実行時間を追跡するだけでなく、イベントを確認することができます。 これは /forge と同様の方法で情報を提示し、遅れを診断するのに役立ちます。",
"commands.computercraft.track.dump.computer": "コンピューター",
"commands.computercraft.track.dump.desc": "コンピュータの最新の追跡結果をダンプしてください。",
"commands.computercraft.track.dump.desc": "コンピュータの最新の追跡結果をダンプしてください。",
"commands.computercraft.track.dump.no_timings": "利用可能なタイミングはありません",
"commands.computercraft.track.dump.synopsis": "最新の追跡結果をダンプしてください",
"commands.computercraft.track.start.desc": "すべてのコンピュータの実行時間とイベント数の追跡を開始します。 これにより、以前の実行結果が破棄されます。",
"commands.computercraft.track.start.desc": "すべてのコンピュータの実行時間とイベント数の追跡を開始します。 これにより、以前の実行結果が破棄されます。",
"commands.computercraft.track.start.stop": "トラッキングを停止して結果を表示するには %s を実行してください",
"commands.computercraft.track.start.synopsis": "すべてのコンピュータの追跡を開始します",
"commands.computercraft.track.start.synopsis": "すべてのコンピュータの追跡を開始します",
"commands.computercraft.track.stop.action": "追跡を中止するためにクリックしてください",
"commands.computercraft.track.stop.desc": "すべてのコンピュータのイベントと実行時間の追跡を停止します",
"commands.computercraft.track.stop.not_enabled": "現在コンピュータを追跡していません",
"commands.computercraft.track.stop.synopsis": "すべてのコンピュータの追跡を停止します",
"commands.computercraft.track.synopsis": "コンピュータの実行時間を追跡します。",
"commands.computercraft.turn_on.desc": "指定されているコンピュータを起動します。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
"commands.computercraft.track.stop.desc": "すべてのコンピュータのイベントと実行時間の追跡を停止します",
"commands.computercraft.track.stop.not_enabled": "現在コンピュータを追跡していません",
"commands.computercraft.track.stop.synopsis": "すべてのコンピュータの追跡を停止します",
"commands.computercraft.track.synopsis": "コンピュータの実行時間を追跡します。",
"commands.computercraft.turn_on.desc": "指定されているコンピュータを起動します。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。",
"commands.computercraft.turn_on.done": "%s/%s コンピューターを起動しました",
"commands.computercraft.turn_on.synopsis": "コンピューターをリモートで起動します。",
"commands.computercraft.view.action": "このコンピュータを見ます",
"commands.computercraft.view.desc": "コンピュータのターミナルを開き、コンピュータのリモートコントロールを可能にします。 これはタートルのインベントリへのアクセスを提供しません。 コンピュータのインスタンスID例えば 123またはコンピュータID例えば #123を指定することができます。",
"commands.computercraft.view.action": "このコンピュータを見ます",
"commands.computercraft.view.desc": "コンピュータのターミナルを開き、コンピュータのリモートコントロールを可能にします。 これはタートルのインベントリへのアクセスを提供しません。 コンピュータのインスタンスID例えば 123またはコンピュータID例えば #123を指定することができます。",
"commands.computercraft.view.not_player": "非プレイヤー用のターミナルを開くことができません",
"commands.computercraft.view.synopsis": "コンピュータのターミナルを表示します。",
"gui.computercraft.pocket_computer_overlay": "ポケットコンピュータを開いています。 ESCを押して閉じます。",
"gui.computercraft.tooltip.computer_id": "コンピュータID: %s",
"commands.computercraft.view.synopsis": "コンピューターのターミナルを表示します。",
"gui.computercraft.config.command_require_creative": "コマンドコンピューターはクリエイティブモードが必要です。",
"gui.computercraft.config.command_require_creative.tooltip": "コマンドコンピューターと対話するためにはプレイヤーがクリエイティブモードかつOP権限保有者でなければなりません。\nこれはバニラのコマンドブロックのデフォルト挙動です。",
"gui.computercraft.config.computer_space_limit": "コンピューターの限容制限(バイト)",
"gui.computercraft.config.computer_space_limit.tooltip": "コンピューターとタートルのディスク容量制限、バイト単位。",
"gui.computercraft.config.default_computer_settings": "デフォルトのコンピューター設定",
"gui.computercraft.config.default_computer_settings.tooltip": "新しいコンピューターに設定するデフォルトのシステム設定のコンマ区切りのリスト。\n例: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\"\nは全ての自動補完を無効にします。",
"gui.computercraft.config.disabled_generic_methods": "無効化するジェネリックメソッド",
"gui.computercraft.config.disabled_generic_methods.tooltip": "無効にするジェネリックメソッドまたはメソッドソースのリスト。\nジェネリックメソッドは、明示的な周辺プロバイダがない場合にブロック/ブロックエンティティに追加されるメソッドです。\nこれには、インベントリメソッド (inventory.getItemDetail や inventory.pushItems) や、(Forgeであれば)fluid_storage や energy_storage メソッドが含まれます。\nこのリストに含まれるメソッドは、メソッド群全体 (computercraft:inventory) か、単一のメソッド (computercraft:inventory#pushItems) のどちらかになります。",
"gui.computercraft.config.execution": "実行",
"gui.computercraft.config.execution.computer_threads": "コンピューターのスレッド",
"gui.computercraft.config.execution.computer_threads.tooltip": "コンピューターが実行できるスレッド数を設定する。\n数値が高いほどより多くのコンピューターが一度に実行できますが、ラグを誘発する可能性があります。\nスレッド数が1より大きいと動作しないMODもあるので注意してください\n範囲: > 1",
"gui.computercraft.config.execution.max_main_computer_time": "サーバーティックのコンピューター時間上限",
"gui.computercraft.config.execution.max_main_computer_time.tooltip": "コンピューターが1ティックで実行できる理想的な最大時間、ミリ秒単位。\nどれぐらい時間がかかるか不明であるため、上限を超える可能性があることに注意。\nこれは平均時間の上限を目的とする。\n範囲: > 1",
"gui.computercraft.config.execution.max_main_global_time": "サーバーティックのグローバル回数上限",
"gui.computercraft.config.execution.max_main_global_time.tooltip": "1ティックでタスクを実行できる最大時間、ミリ秒単位。\nどれぐらい時間がかかるか不明であるため、上限を超える可能性があることに注意。\nこれは平均回数の上限を時間とする。",
"gui.computercraft.config.execution.tooltip": "コンピュータの実行挙動を制御する。\nこれは主にサーバーを微調整するためのもので、一般的には触る必要はない。",
"gui.computercraft.config.floppy_space_limit": "フロッピーディスクの容量制限(バイト)",
"gui.computercraft.config.floppy_space_limit.tooltip": "フロッピーディスクのディスク容量制限(バイト単位)。",
"gui.computercraft.config.http": "HTTP",
"gui.computercraft.config.http.bandwidth": "帯域幅",
"gui.computercraft.config.http.bandwidth.global_download": "グローバルダウンロード制限",
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "1秒間にダウンロードできるバイト数。これはすべてのコンピュータで共有されます。(byte/s).\n範囲: > 1",
"gui.computercraft.config.http.bandwidth.global_upload": "グローバルアップロード制限",
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "1秒間にアップロードできるバイト数。これはすべてのコンピュータで共有されます。(byte/s).\n範囲: > 1",
"gui.computercraft.config.http.bandwidth.tooltip": "コンピュータが使用する帯域幅を制限する。",
"gui.computercraft.config.http.enabled": "HTTP APIを有効にする",
"gui.computercraft.config.http.enabled.tooltip": "コンピュータの\"http\" APIを有効にする。これを無効にすると、多くのユーザーが依存している\"pastebin\"と\"wget\"プログラムも無効になる。\nこのオプションはオンのままにしておき、よりきめ細かい制御を行うために\"ルール\"の設定オプションを使用することを推奨する。",
"gui.computercraft.config.http.max_requests": "最大同時リクエスト数",
"gui.computercraft.config.http.max_requests.tooltip": "コンピューターが一度にできるhttpリクエストの数。追加のリクエストはキューに入れられ、実行中のリクエストが終了したときに送信されます。無制限の場合は0に設定します。\n範囲: > 0",
"gui.computercraft.config.http.max_websockets": "最大同時ウェブソケット数",
"gui.computercraft.config.http.max_websockets.tooltip": "コンピュータが一度に開くことのできるウェブソケットの数。\n範囲: > 1",
"gui.computercraft.config.http.proxy": "プロキシ",
"gui.computercraft.config.http.proxy.host": "ホスト名",
"gui.computercraft.config.http.proxy.host.tooltip": "プロキシサーバーのホスト名またはIPアドレス。",
"gui.computercraft.config.http.proxy.port": "ポート",
"gui.computercraft.config.http.proxy.port.tooltip": "プロキシサーバーのポート。\n範囲: 1 ~ 65536",
"gui.computercraft.config.http.proxy.tooltip": "HTTPとウェブソケットリクエストをプロキシサーバ経由でトンネリングする。\"use_proxy\"が\"true\"(デフォルトでは\"off\")に設定されている HTTPルールにのみ影響します。\nプロキシに認証が必要な場合は、\"computercraft-server.toml\"と同じディレクトリに\"myuser:mypassword\"のようにユーザー名とパスワードをコロンで区切って記述した\"computercraft-proxy.pw\"ファイルを作成します。\nSOCKS4プロキシでは、ユーザー名のみが必要です。",
"gui.computercraft.config.http.proxy.type": "プロキシ種類",
"gui.computercraft.config.http.proxy.type.tooltip": "使用するプロキシの種類。\n許可された値: HTTP, HTTPS, SOCKS4, SOCKS5",
"gui.computercraft.config.http.rules": "ルールの許可/拒否",
"gui.computercraft.config.http.rules.tooltip": "特定のドメインやIPに対する\"http\" APIの動作を制御するルールのリスト。それぞれのルールはホスト名とオプションのポートに対して対応し、 リクエストに対していくつかのプロパティを設定します。 ルールは順番に評価され、前のルールが後のルールを上書きします。\n\n有効なプロパティ:\n - \"host\" (必須): このルールが対応するドメインまたはIPアドレス。 これはドメイン名(\"pastebin.com\")、ワイルドカード(\"*.pastebin.com\")、あるいはCIDR表記(\"127.0.0.0/8\")となります。\n - \"port\" (オプション): 80や 443など、特定のポートに対するリクエストにのみマッチする。.\n\n - \"action\" (オプション): このリクエストを許可するか拒否するか。\n - \"max_download\" (オプション): このリクエストでコンピューターがダウンロードできる最大サイズ(バイト単位)。\n - \"max_upload\" (オプション): このリクエストでコンピューターがアップロードできる最大サイズ(バイト)。\n - \"max_websocket_message\" (オプション): コンピューターが1つのウェブソケット・パケットで送受信できる最大サイズバイト。\n - \"use_proxy\" (オプション): HTTP/SOCKSプロキシが設定されている場合は、その使用を有効にする。",
"gui.computercraft.config.http.tooltip": "HTTP APIの制御",
"gui.computercraft.config.http.websocket_enabled": "ウェブソケットを有効にする",
"gui.computercraft.config.http.websocket_enabled.tooltip": "httpウェブソケットの使用を有効にする。これには、\"http_enable\"オプションもtrueである必要があります。.",
"gui.computercraft.config.log_computer_errors": "コンピュータのエラーを記録する",
"gui.computercraft.config.log_computer_errors.tooltip": "周辺機器やその他のLuaオブジェクトが発生させた例外を記録します。これにより、MODの作者が問題をデバッグしやすくなりますが、バグを含んだメソッドを使用した場合、ログスパムが発生する可能性があります。",
"gui.computercraft.config.maximum_open_files": "1台のコンピューターで開けるファイルの最大数",
"gui.computercraft.config.maximum_open_files.tooltip": "コンピューターが同時に開くことができるファイルの数を設定します。無制限の場合は0に設定します。\n範囲: > 0",
"gui.computercraft.config.monitor_distance": "モニター距離",
"gui.computercraft.config.monitor_distance.tooltip": "モニターがレンダリングする最大距離。デフォルトは標準的なタイルエンティティの制限値ですが、より大きなモニタを構築したい場合は拡張することができます。\n範囲: 16 ~ 1024",
"gui.computercraft.config.monitor_renderer": "モニターレンダラー",
"gui.computercraft.config.monitor_renderer.tooltip": "モニターに使用するレンダラー。一般的に、この値は\"best\"に保つたれるべきです。 - モニターにパフォーマンス上の問題がある場合は、別のレンダラーを試してみるとよいでしょう。\n許可された値: BEST, TBO, VBO",
"gui.computercraft.config.peripheral": "周辺機器",
"gui.computercraft.config.peripheral.command_block_enabled": "コマンドブロック周辺機器を有効にする",
"gui.computercraft.config.peripheral.command_block_enabled.tooltip": "コマンドブロック周辺機器サポートを有効にする",
"gui.computercraft.config.peripheral.max_notes_per_tick": "コンピューターが一度に演奏できる最大音符数",
"gui.computercraft.config.peripheral.max_notes_per_tick.tooltip": "スピーカーが一度に演奏できる最大音符数。\n範囲: > 1",
"gui.computercraft.config.peripheral.modem_high_altitude_range": "モデム範囲(高高度)",
"gui.computercraft.config.peripheral.modem_high_altitude_range.tooltip": "晴天時の最大高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm": "モデム範囲(高高度、悪天候)",
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm.tooltip": "悪天候の最大高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
"gui.computercraft.config.peripheral.modem_range": "モデム範囲(デフォルト)",
"gui.computercraft.config.peripheral.modem_range.tooltip": "晴天時の低高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
"gui.computercraft.config.peripheral.modem_range_during_storm": "モデム範囲(悪天候)",
"gui.computercraft.config.peripheral.modem_range_during_storm.tooltip": "荒天時の低高度におけるワイヤレスモデムの通信距離、メートル単位。\n範囲: 0 ~ 100000",
"gui.computercraft.config.peripheral.monitor_bandwidth": "モニター帯域幅",
"gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "1ティックあたりのモニターデータ送信量の上限。注:\n - 帯域幅は圧縮前に測定されるため、クライアントに送信されるデータはより小さくなります。\n - これは、パケットを送信するプレーヤーの数を無視する。1人のプレーヤーのモニターを更新することは、20人に送信するのと同じ帯域幅の制限を消費する。\n - フルサイズのモニターは~25kbのデータを送信する。そのため、デフォルト(1MB)では、1回のティックで~40個のモニターを更新することができる。\n 無効にするには 0 を設定する。\n範囲: > 0",
"gui.computercraft.config.peripheral.tooltip": "周辺機器に関する各種オプション。",
"gui.computercraft.config.term_sizes": "ターミナルサイズ",
"gui.computercraft.config.term_sizes.computer": "コンピューター",
"gui.computercraft.config.term_sizes.computer.height": "ターミナルの高さ",
"gui.computercraft.config.term_sizes.computer.height.tooltip": "範囲: 1 ~ 255",
"gui.computercraft.config.term_sizes.computer.tooltip": "コンピュータの端末サイズ。",
"gui.computercraft.config.term_sizes.computer.width": "ターミナルの幅",
"gui.computercraft.config.term_sizes.computer.width.tooltip": "範囲: 1 ~ 255",
"gui.computercraft.config.term_sizes.monitor": "モニター",
"gui.computercraft.config.term_sizes.monitor.height": "モニターの最大高さ",
"gui.computercraft.config.term_sizes.monitor.height.tooltip": "範囲: 1 ~ 32",
"gui.computercraft.config.term_sizes.monitor.tooltip": "モニターの最大サイズ(ブロック単位).",
"gui.computercraft.config.term_sizes.monitor.width": "モニターの最大幅",
"gui.computercraft.config.term_sizes.monitor.width.tooltip": "範囲: 1 ~ 32",
"gui.computercraft.config.term_sizes.pocket_computer": "ポケットコンピューター",
"gui.computercraft.config.term_sizes.pocket_computer.height": "ターミナルの高さ",
"gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "範囲: 1 ~ 255",
"gui.computercraft.config.term_sizes.pocket_computer.tooltip": "ポケットコンピュータのターミナルサイズ。",
"gui.computercraft.config.term_sizes.pocket_computer.width": "ターミナルの幅",
"gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "範囲: 1 ~ 255",
"gui.computercraft.config.term_sizes.tooltip": "各種コンピュータのターミナルサイズを設定します。ターミナルサイズが大きくなるとより多くの帯域幅を必要としますので、注意して使用してください。",
"gui.computercraft.config.turtle": "タートル",
"gui.computercraft.config.turtle.advanced_fuel_limit": "アドバンスドタートルの燃料制限",
"gui.computercraft.config.turtle.advanced_fuel_limit.tooltip": "アドバンスドタートルの燃料制限。\n範囲: > 0",
"gui.computercraft.config.turtle.can_push": "タートルのによるエンティティ押出し",
"gui.computercraft.config.turtle.can_push.tooltip": "trueに設定すると、タートルは空間がある場合停止する代わりにエンティティを押し出す。",
"gui.computercraft.config.turtle.need_fuel": "燃料を有効にする",
"gui.computercraft.config.turtle.need_fuel.tooltip": "タートルズが移動に燃料を必要とするかどうかを設定する。",
"gui.computercraft.config.turtle.normal_fuel_limit": "タートルの燃料制限",
"gui.computercraft.config.turtle.normal_fuel_limit.tooltip": "タートルの燃料制限。\n範囲: > 0",
"gui.computercraft.config.turtle.tooltip": "タートルに関する様々なオプション。",
"gui.computercraft.config.upload_max_size": "ファイルアップロードサイズ制限(バイト)",
"gui.computercraft.config.upload_max_size.tooltip": "ファイルアップロードサイズの上限をバイト数で指定します。1KiBから16MiBの範囲でなければなりません。 \nアップロードは1ティックで処理されることを覚えておいてください - 大きなファイルやネットワークパフォーマンスの低下はネットワーキングスレッドを停止させる可能性があります。\nディスク容量にも注意してください\n範囲: 1024 ~ 16777216",
"gui.computercraft.config.upload_nag_delay": "アップロード催促遅延",
"gui.computercraft.config.upload_nag_delay.tooltip": "未処理の入力について通知するまでの遅延時間(秒)。無効にするには0を設定する。\n範囲: 0 ~ 60",
"gui.computercraft.pocket_computer_overlay": "ポケットコンピューターを開いています。 ESCを押して閉じます。",
"gui.computercraft.terminal": "コンピューターターミナル",
"gui.computercraft.tooltip.computer_id": "コンピューターID: %s",
"gui.computercraft.tooltip.copy": "クリップボードにコピー",
"gui.computercraft.tooltip.disk_id": "ディスクID: %s",
"gui.computercraft.tooltip.terminate": "現在実行中のコードを停止する",
"gui.computercraft.tooltip.terminate.key": "Ctrl+T 長押し",
"gui.computercraft.tooltip.turn_off": "このコンピュータをオフにする",
"gui.computercraft.tooltip.turn_off": "このコンピュータをオフにする",
"gui.computercraft.tooltip.turn_off.key": "Ctrl+S 長押し",
"gui.computercraft.tooltip.turn_on": "このコンピュータをオンにする",
"gui.computercraft.tooltip.turn_on": "このコンピュータをオンにする",
"gui.computercraft.upload.failed": "アップロードに失敗しました",
"gui.computercraft.upload.failed.computer_off": "ファイルをアップロードする前にコンピュータを起動する必要があります。",
"gui.computercraft.upload.failed.computer_off": "ファイルをアップロードする前にコンピュータを起動する必要があります。",
"gui.computercraft.upload.failed.corrupted": "アップロード時にファイルが破損しました。 もう一度やり直してください。",
"gui.computercraft.upload.failed.generic": "ファイルのアップロードに失敗しました(%s)",
"gui.computercraft.upload.failed.name_too_long": "ファイル名が長すぎてアップロードできません。",
"gui.computercraft.upload.failed.too_many_files": "多くのファイルをアップロードできません。",
"gui.computercraft.upload.failed.too_much": "アップロードするにはファイルが大きスギます。",
"gui.computercraft.upload.no_response": "ファイルの転送",
"gui.computercraft.upload.no_response.msg": "コンピュータが転送されたファイルを使用していません。プログラム %s を実行して再試行する必要があります。",
"item.computercraft.disk": "フロッピーディスク",
"item.computercraft.pocket_computer_advanced": "高度なポケットコンピュータ",
"item.computercraft.pocket_computer_advanced.upgraded": "高度な%sポケットコンピュータ",
"item.computercraft.pocket_computer_normal": "ポケットコンピュータ",
"item.computercraft.pocket_computer_normal.upgraded": "%sポケットコンピュータ",
"item.computercraft.pocket_computer_advanced": "高度なポケットコンピュータ",
"item.computercraft.pocket_computer_advanced.upgraded": "高度な%sポケットコンピュータ",
"item.computercraft.pocket_computer_normal": "ポケットコンピュータ",
"item.computercraft.pocket_computer_normal.upgraded": "%sポケットコンピュータ",
"item.computercraft.printed_book": "印刷された本",
"item.computercraft.printed_page": "印刷された紙",
"item.computercraft.printed_pages": "印刷された紙束",
"item.computercraft.treasure_disk": "フロッピーディスク",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.computer": "コンピューター",
"tag.item.computercraft.monitor": "モニター",
"tag.item.computercraft.turtle": "タートル",
"tag.item.computercraft.wired_modem": "有線モデム",
"tracking_field.computercraft.avg": "%s (平均)",
"tracking_field.computercraft.computer_tasks.name": "タスク",
"tracking_field.computercraft.count": "%s (回)",
"tracking_field.computercraft.fs.name": "ファイルシステム演算",
"tracking_field.computercraft.http_download.name": "HTTPダウンロード",
"tracking_field.computercraft.http_requests.name": "HTTPリクエスト",
"tracking_field.computercraft.http_upload.name": "HTTPアップロード",
"tracking_field.computercraft.java_allocation.name": "Java割当",
"tracking_field.computercraft.max": "%s (最大)",
"tracking_field.computercraft.peripheral.name": "実行呼び出し",
"tracking_field.computercraft.server_tasks.name": "サーバータスク",
"tracking_field.computercraft.turtle_ops.name": "タートル操作",
"tracking_field.computercraft.websocket_incoming.name": "Websocket 受信",
"tracking_field.computercraft.websocket_outgoing.name": "Websocket 送信",
"upgrade.computercraft.speaker.adjective": "騒音",

@ -160,5 +160,7 @@ class Monitor_Test {
}
thenScreenshot()
thenExecute { helper.level.dayTime = Times.NOON }
}
}

@ -53,5 +53,7 @@ class Printout_Test {
}
thenScreenshot()
thenExecute { helper.level.dayTime = Times.NOON }
}
}

@ -742,7 +742,7 @@ class Turtle_Test {
@GameTest
fun Breaks_exploding_block(context: GameTestHelper) = context.sequence {
thenOnComputer { turtle.dig(Optional.empty()) }
thenIdle(2)
thenWaitUntil { context.assertBlockPresent(Blocks.AIR, BlockPos(2, 2, 2)) }
thenExecute {
context.assertItemEntityCountIs(ModRegistry.Items.TURTLE_NORMAL.get(), 1)
context.assertItemEntityCountIs(Items.BONE_BLOCK, 65)

@ -73,6 +73,7 @@ object ClientTestHooks {
minecraft.options.cloudStatus().set(CloudStatus.OFF)
minecraft.options.particles().set(ParticleStatus.MINIMAL)
minecraft.options.tutorialStep = TutorialSteps.NONE
minecraft.options.pauseOnLostFocus = false
minecraft.options.renderDistance().set(6)
minecraft.options.gamma().set(1.0)
minecraft.options.getSoundSourceOptionInstance(SoundSource.MUSIC).set(0.0)
@ -92,10 +93,7 @@ object ClientTestHooks {
LEVEL_NAME,
LevelSettings("Test Level", GameType.CREATIVE, false, Difficulty.EASY, true, rules, WorldDataConfiguration.DEFAULT),
WorldOptions(WorldOptions.randomSeed(), false, false),
{
it.registryOrThrow(Registries.WORLD_PRESET).getHolderOrThrow(WorldPresets.FLAT).value()
.createWorldDimensions()
},
{ it.registryOrThrow(Registries.WORLD_PRESET).getOrThrow(WorldPresets.FLAT).createWorldDimensions() },
screen,
)
}
@ -112,7 +110,20 @@ object ClientTestHooks {
val testTracker = when (val tracker = this.testTracker) {
null -> {
if (server.overworld().players().isEmpty()) return
// Place our players above where the tests will run, looking down. This at least ensures they're in the
// right area when the tests start running.
for (player in server.overworld().players()) {
player.abilities.flying = true
player.onUpdateAbilities()
player.connection.teleport(0.0, -30.0, 0.0, 0.0f, 90.0f)
player.inventory.clearContent()
}
// Wait for all chunks to be rendered.
if (!Minecraft.getInstance().isRenderingStable()) return
// Then a little more just in case.
if (startupDelay >= 0) {
// TODO: Is there a better way? Maybe set a flag when the client starts rendering?
startupDelay--

@ -130,10 +130,10 @@
],
entities: [
{blockPos: [2, 1, 0], pos: [2.5d, 1.0d, 0.5d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CustomName: '{"text":"turtle_test.render_turtle_items"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [125.5d, -58.0d, 53.6501934495752d], Pose: {}, Rotation: [0.14965993f, 4.066999f], ShowArms: 0b, Small: 0b, UUID: [I; -1678989666, 1780632657, -1267321893, 665166246], id: "minecraft:armor_stand"}},
{blockPos: [3, 1, 3], pos: [3.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 671334450, -268547745, -1360971514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
{blockPos: [3, 3, 3], pos: [3.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 671334422, -268542345, -1362491514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
{blockPos: [1, 1, 3], pos: [1.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 625334450, -268647745, -1360971514, -649724242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, LeftUpgrade: {id: "cctest:netherite_pickaxe", components: {"minecraft:enchantments": {levels: {"minecraft:silk_touch": 1}}}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
{blockPos: [1, 3, 3], pos: [1.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 675334422, -268542245, -1362491514, -649755242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, LeftUpgrade: {id: "cctest:netherite_pickaxe", components: {"minecraft:enchantments": {levels: {"minecraft:silk_touch": 1}}}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}}
{blockPos: [3, 1, 3], pos: [3.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 671334450, -268547745, -1360971514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
{blockPos: [3, 3, 3], pos: [3.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [90.0f, 0.0f], UUID: [I; 671334422, -268542345, -1362491514, -649716242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, RightUpgrade: "minecraft:diamond_pickaxe", RightUpgradeNbt: {Tag: {Damage: 0}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
{blockPos: [1, 1, 3], pos: [1.5d, 1.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 625334450, -268647745, -1360971514, -649724242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], On: 1b, LeftUpgrade: {id: "cctest:netherite_pickaxe", components: {"minecraft:enchantments": {levels: {"minecraft:silk_touch": 1}}}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}},
{blockPos: [1, 3, 3], pos: [1.5d, 3.5d, 3.5d], nbt: {Air: 300s, FallDistance: 0.0f, Fire: 0s, Invulnerable: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [126.5d, -57.5d, 56.5d], Rotation: [-90.0f, 0.0f], UUID: [I; 675334422, -268542245, -1362491514, -649755242], billboard: "fixed", glow_color_override: -1, height: 0.0f, id: "minecraft:item_display", interpolation_duration: 0, item: {Count: 1b, id: "computercraft:turtle_normal", tag: {ComputerId: 8, Fuel: 0, Items: [], display: {Name: '{"text":"Dinnerbone"}'}, On: 1b, LeftUpgrade: {id: "cctest:netherite_pickaxe", components: {"minecraft:enchantments": {levels: {"minecraft:silk_touch": 1}}}}}}, item_display: "none", shadow_radius: 0.0f, shadow_strength: 1.0f, transformation: {left_rotation: [0.0f, 0.0f, 0.0f, 1.0f], right_rotation: [0.0f, 0.0f, 0.0f, 1.0f], scale: [1.0f, 1.0f, 1.0f], translation: [0.0f, 0.0f, 0.0f]}, view_range: 1.0f, width: 0.0f}}
],
palette: [
"minecraft:polished_andesite",

@ -7,6 +7,7 @@ import cc.tweaked.gradle.getAbsolutePath
plugins {
`java-library`
`java-test-fixtures`
alias(libs.plugins.shadow)
id("cc-tweaked.kotlin-convention")
id("cc-tweaked.java-convention")
@ -57,3 +58,22 @@ val checkChangelog by tasks.registering(cc.tweaked.gradle.CheckChangelog::class)
}
tasks.check { dependsOn(checkChangelog) }
// We configure the shadow jar to ship netty-codec and all its dependencies, relocating them under the
// dan200.computercraft.core package.
// This is used as part of the Forge build, so that our version of netty-codec is loaded under the GAME layer, and so
// has access to our jar-in-jar'ed jzlib.
tasks.shadowJar {
minimize()
dependencies {
include(dependency(libs.netty.codec.get()))
include(dependency(libs.netty.http.get()))
include(dependency(libs.netty.socks.get()))
include(dependency(libs.netty.proxy.get()))
}
for (pkg in listOf("io.netty.handler.codec", "io.netty.handler.proxy")) {
relocate(pkg, "dan200.computercraft.core.vendor.$pkg")
}
}

@ -95,11 +95,8 @@ public class CobaltLuaMachine implements ILuaMachine {
private void addAPI(LuaState state, LuaTable globals, ILuaAPI api) throws LuaError {
// Add the methods of an API to the global table
var table = wrapLuaObject(api);
if (table == null) {
LOG.warn("API {} does not provide any methods", api);
table = new LuaTable();
}
var table = new LuaTable();
if (!makeLuaObject(api, table)) LOG.warn("API {} does not provide any methods", api);
var names = api.getNames();
for (var name : names) globals.rawset(name, table);
@ -163,13 +160,16 @@ public class CobaltLuaMachine implements ILuaMachine {
timeout.removeListener(timeoutListener);
}
@Nullable
private LuaTable wrapLuaObject(Object object) {
var table = new LuaTable();
var found = luaMethods.forEachMethod(object, (target, name, method, info) ->
/**
* Populate a table with methods from an object.
*
* @param object The object to draw methods from.
* @param table The table to fill.
* @return Whether any methods were found.
*/
private boolean makeLuaObject(Object object, LuaTable table) {
return luaMethods.forEachMethod(object, (target, name, method, info) ->
table.rawset(name, new ResultInterpreterFunction(this, method, target, context, name)));
return found ? table : null;
}
private LuaValue toValue(@Nullable Object object, @Nullable IdentityHashMap<Object, LuaValue> values) throws LuaError {
@ -184,47 +184,35 @@ public class CobaltLuaMachine implements ILuaMachine {
return ValueFactory.valueOf(bytes);
}
// Don't share singleton values, and instead convert them to a new table.
if (LuaUtil.isSingletonCollection(object)) return new LuaTable();
// We have a more complex object, which is possibly recursive. First look up our object in the lookup map,
// and reuse it if present.
if (values == null) values = new IdentityHashMap<>(1);
var result = values.get(object);
if (result != null) return result;
var wrapped = toValueWorker(object, values);
if (wrapped == null) {
LOG.warn(Logging.JAVA_ERROR, "Received unknown type '{}', returning nil.", object.getClass().getName());
return Constants.NIL;
}
values.put(object, wrapped);
return wrapped;
}
/**
* Convert a complex Java object (such as a collection or Lua object) to a Lua value.
* <p>
* This is a worker function for {@link #toValue(Object, IdentityHashMap)}, which handles the actual construction
* of values, without reading/writing from the value map.
*
* @param object The object to convert.
* @param values The map of Java to Lua values.
* @return The converted value, or {@code null} if it could not be converted.
* @throws LuaError If the value could not be converted.
*/
private @Nullable LuaValue toValueWorker(Object object, IdentityHashMap<Object, LuaValue> values) throws LuaError {
if (object instanceof ILuaFunction) {
return new ResultInterpreterFunction(this, FUNCTION_METHOD, object, context, object.toString());
var function = new ResultInterpreterFunction(this, FUNCTION_METHOD, object, context, object.toString());
values.put(object, function);
return function;
}
if (object instanceof IDynamicLuaObject) {
LuaValue wrapped = wrapLuaObject(object);
if (wrapped == null) wrapped = new LuaTable();
return wrapped;
var table = new LuaTable();
makeLuaObject(object, table);
values.put(object, table);
return table;
}
// The following objects may be recursive. In these instances, we need to be careful to store the value *before*
// recursing, to avoid stack overflows.
if (object instanceof Map<?, ?> map) {
// Don't share singleton values, and instead convert them to a new table.
if (LuaUtil.isSingletonMap(map)) return new LuaTable();
var table = new LuaTable();
values.put(object, table);
for (var pair : map.entrySet()) {
var key = toValue(pair.getKey(), values);
var value = toValue(pair.getValue(), values);
@ -234,7 +222,12 @@ public class CobaltLuaMachine implements ILuaMachine {
}
if (object instanceof Collection<?> objects) {
// Don't share singleton values, and instead convert them to a new table.
if (LuaUtil.isSingletonCollection(objects)) return new LuaTable();
var table = new LuaTable(objects.size(), 0);
values.put(object, table);
var i = 0;
for (var child : objects) table.rawset(++i, toValue(child, values));
return table;
@ -242,11 +235,20 @@ public class CobaltLuaMachine implements ILuaMachine {
if (object instanceof Object[] objects) {
var table = new LuaTable(objects.length, 0);
values.put(object, table);
for (var i = 0; i < objects.length; i++) table.rawset(i + 1, toValue(objects[i], values));
return table;
}
return wrapLuaObject(object);
var table = new LuaTable();
if (makeLuaObject(object, table)) {
values.put(object, table);
return table;
}
LOG.warn(Logging.JAVA_ERROR, "Received unknown type '{}', returning nil.", object.getClass().getName());
return Constants.NIL;
}
Varargs toValues(@Nullable Object[] objects) throws LuaError {

@ -12,15 +12,13 @@ public class Palette {
private final boolean colour;
private final double[][] colours = new double[PALETTE_SIZE][3];
private final byte[][] byteColours = new byte[PALETTE_SIZE][4];
private final int[] byteColours = new int[PALETTE_SIZE];
public static final Palette DEFAULT = new Palette(true);
public Palette(boolean colour) {
this.colour = colour;
resetColours();
for (var i = 0; i < PALETTE_SIZE; i++) byteColours[i][3] = (byte) 255;
}
public void setColour(int i, double r, double g, double b) {
@ -30,15 +28,17 @@ public class Palette {
colours[i][2] = b;
if (colour) {
byteColours[i][0] = (byte) (int) (r * 255);
byteColours[i][1] = (byte) (int) (g * 255);
byteColours[i][2] = (byte) (int) (b * 255);
byteColours[i] = packColour((int) (r * 255), (int) (g * 255), (int) (b * 255));
} else {
var grey = (byte) (int) ((r + g + b) / 3 * 255);
byteColours[i][0] = byteColours[i][1] = byteColours[i][2] = grey;
var grey = (int) ((r + g + b) / 3 * 255);
byteColours[i] = packColour(grey, grey, grey);
}
}
private static int packColour(int r, int g, int b) {
return 255 << 24 | (r & 255) << 16 | (g & 255) << 8 | b & 255;
}
public void setColour(int i, Colour colour) {
setColour(i, colour.getR(), colour.getG(), colour.getB());
}
@ -48,26 +48,20 @@ public class Palette {
}
/**
* Get the colour as a set of RGB values suitable for rendering. Colours are automatically converted to greyscale
* Get the colour as a set of ARGB values suitable for rendering. Colours are automatically converted to greyscale
* when using a black and white palette.
* <p>
* This returns a byte array, suitable for being used directly by our terminal vertex format.
* This returns a packed 32-bit ARGB colour.
*
* @param i The colour index.
* @return The number as a tuple of bytes.
* @return The actual RGB colour.
*/
public byte[] getRenderColours(int i) {
public int getRenderColours(int i) {
return byteColours[i];
}
public void resetColour(int i) {
if (i >= 0 && i < PALETTE_SIZE) setColour(i, Colour.VALUES[i]);
}
public void resetColours() {
for (var i = 0; i < Colour.VALUES.length; i++) {
resetColour(i);
}
for (var i = 0; i < Colour.VALUES.length; i++) setColour(i, Colour.VALUES[i]);
}
public static int encodeRGB8(double[] rgb) {

@ -36,7 +36,20 @@ public class LuaUtil {
* @param value The value to test.
* @return Whether this is a singleton collection.
*/
public static boolean isSingletonCollection(Object value) {
return value == EMPTY_LIST || value == EMPTY_SET || value == EMPTY_MAP;
public static boolean isSingletonCollection(Collection<?> value) {
return value == EMPTY_LIST || value == EMPTY_SET;
}
/**
* Determine whether a value is a singleton map, such as one created with {@link Map#of()}.
* <p>
* These collections are treated specially by {@link ILuaMachine} implementations: we skip sharing for them, and
* create a new table each time.
*
* @param value The value to test.
* @return Whether this is a singleton map.
*/
public static boolean isSingletonMap(Map<?, ?> value) {
return value == EMPTY_MAP;
}
}

@ -944,22 +944,21 @@ unserialiseJSON = unserialise_json
-- @since 1.31
function urlEncode(str)
expect(1, str, "string")
if str then
str = string.gsub(str, "\n", "\r\n")
str = string.gsub(str, "([^A-Za-z0-9 %-%_%.])", function(c)
local n = string.byte(c)
if n < 128 then
-- ASCII
return string.format("%%%02X", n)
else
-- Non-ASCII (encode as UTF-8)
return
string.format("%%%02X", 192 + bit32.band(bit32.arshift(n, 6), 31)) ..
string.format("%%%02X", 128 + bit32.band(n, 63))
end
end)
str = string.gsub(str, " ", "+")
end
local gsub, byte, format, band, arshift = string.gsub, string.byte, string.format, bit32.band, bit32.arshift
str = gsub(str, "\n", "\r\n")
str = gsub(str, "[^A-Za-z0-9%-%_%.]", function(c)
if c == " " then return "+" end
local n = byte(c)
if n < 128 then
-- ASCII
return format("%%%02X", n)
else
-- Non-ASCII (encode as UTF-8)
return format("%%%02X%%%02X", 192 + band(arshift(n, 6), 31), 128 + band(n, 63))
end
end)
return str
end

@ -1,3 +1,12 @@
# New features in CC: Tweaked 1.113.1
* Update Japanese translation (konumatakaki).
* Improve performance of `textutils.urlEncode`.
Several bug fixes:
* Fix overflow when converting recursive objects from Java to Lua.
* Fix websocket compression not working under Forge.
# New features in CC: Tweaked 1.113.0
* Allow placing printed pages and books in lecterns.

@ -1,13 +1,10 @@
New features in CC: Tweaked 1.113.0
New features in CC: Tweaked 1.113.1
* Allow placing printed pages and books in lecterns.
* Update Japanese translation (konumatakaki).
* Improve performance of `textutils.urlEncode`.
Several bug fixes:
* Various documentation fixes (MCJack123)
* Fix computers and turtles not being dropped when exploded with TNT.
* Fix crash when turtles are broken while mining a block.
* Fix pocket computer terminals not updating when in the off-hand.
* Fix disk drives not being exposed as a peripheral.
* Fix item details being non-serialisable due to duplicated tables.
* Fix overflow when converting recursive objects from Java to Lua.
* Fix websocket compression not working under Forge.
Type "help changelog" to see the full version history.

@ -6,8 +6,7 @@
Convert between streams of DFPWM audio data and a list of amplitudes.
DFPWM (Dynamic Filter Pulse Width Modulation) is an audio codec designed by GreaseMonkey. It's a relatively compact
format compared to raw PCM data, only using 1 bit per sample, but is simple enough to simple enough to encode and decode
in real time.
format compared to raw PCM data, only using 1 bit per sample, but is simple enough to encode and decode in real time.
Typically DFPWM audio is read from [the filesystem][`fs.ReadHandle`] or a [a web request][`http.Response`] as a string,
and converted a format suitable for [`speaker.playAudio`].

@ -202,6 +202,14 @@ describe("The os library", function()
expect(xs[1]):eq(xs[2])
end)
it("handles recursive tables", function()
local tbl = {}
tbl[1] = tbl
local xs = roundtrip(tbl)
expect(xs):eq(xs[1])
end)
it("does not preserve references in separate args", function()
-- I'm not sure I like this behaviour, but it is what CC has always done.
local tbl = {}

@ -296,6 +296,22 @@ describe("The textutils library", function()
textutils.urlEncode("")
expect.error(textutils.urlEncode, nil):eq("bad argument #1 (string expected, got nil)")
end)
it("encodes newlines", function()
expect(textutils.urlEncode("a\nb")):eq("a%0D%0Ab")
end)
it("leaves normal characters as-is", function()
expect(textutils.urlEncode("abcABC0123")):eq("abcABC0123")
end)
it("escapes spaces", function()
expect(textutils.urlEncode("a b c")):eq("a+b+c")
end)
it("escapes special characters", function()
expect(textutils.urlEncode("a%b\0\255")):eq("a%25b%00%C3%BF")
end)
end)
describe("textutils.complete", function()

@ -11,6 +11,7 @@ import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import net.fabricmc.loader.api.FabricLoader;
import net.irisshaders.iris.api.v0.IrisApi;
import net.irisshaders.iris.api.v0.IrisTextVertexSink;
import net.minecraft.util.FastColor;
import java.nio.ByteBuffer;
import java.util.Optional;
@ -54,12 +55,8 @@ public class IrisShaderMod implements ShaderMod.Provider {
}
@Override
public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
sink.quad(x1, y1, x2, y2, z, pack(rgba[0], rgba[1], rgba[2], rgba[3]), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP);
}
private static int pack(int r, int g, int b, int a) {
return (a & 255) << 24 | (b & 255) << 16 | (g & 255) << 8 | r & 255;
public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
sink.quad(x1, y1, x2, y2, z, FastColor.ABGR32.fromArgb32(colour), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP);
}
}
}

@ -99,9 +99,16 @@ runs {
}
configurations {
val minecraftEmbed by registering {
isCanBeResolved = false
isCanBeConsumed = false
}
named("jarJar") { extendsFrom(minecraftEmbed.get()) }
val minecraftLibrary by registering {
isCanBeResolved = true
isCanBeConsumed = false
extendsFrom(minecraftEmbed.get())
}
runtimeOnly { extendsFrom(minecraftLibrary.get()) }
@ -129,8 +136,11 @@ dependencies {
clientApi(clientClasses(project(":forge-api"))) { cct.exclude(this) }
implementation(project(":core")) { cct.exclude(this) }
"minecraftLibrary"(libs.cobalt)
"minecraftLibrary"(libs.jzlib)
"minecraftEmbed"(libs.cobalt)
"minecraftEmbed"(libs.jzlib)
// We don't jar-in-jar our additional netty dependencies (see the tasks.jarJar configuration), but still want them
// on the legacy classpath.
"minecraftLibrary"(libs.netty.http)
"minecraftLibrary"(libs.netty.socks)
"minecraftLibrary"(libs.netty.proxy)
@ -165,9 +175,17 @@ tasks.processResources {
tasks.jar {
archiveClassifier.set("slim")
duplicatesStrategy = DuplicatesStrategy.FAIL
// Include all classes from other projects except core.
val coreSources = project(":core").sourceSets["main"]
for (source in cct.sourceDirectories.get()) {
if (source.classes && source.external) from(source.sourceSet.output)
if (source.classes && source.sourceSet != coreSources) from(source.sourceSet.output)
}
// Include core separately, along with the relocated netty classes.
from(zipTree(project(":core").tasks.named("shadowJar", AbstractArchiveTask::class).map { it.archiveFile })) {
exclude("META-INF/**")
}
}
@ -175,15 +193,8 @@ tasks.sourcesJar {
for (source in cct.sourceDirectories.get()) from(source.sourceSet.allSource)
}
jarJar.enable()
tasks.jarJar {
archiveClassifier.set("")
configuration(project.configurations["minecraftLibrary"])
for (source in cct.sourceDirectories.get()) {
if (source.classes) from(source.sourceSet.output)
}
}
tasks.assemble { dependsOn("jarJar") }

@ -10,6 +10,7 @@ import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import net.irisshaders.iris.api.v0.IrisApi;
import net.irisshaders.iris.api.v0.IrisTextVertexSink;
import net.minecraft.util.FastColor;
import net.neoforged.fml.ModList;
import java.nio.ByteBuffer;
@ -57,12 +58,8 @@ public class IrisShaderMod implements ShaderMod.Provider {
}
@Override
public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
sink.quad(x1, y1, x2, y2, z, pack(rgba[0], rgba[1], rgba[2], rgba[3]), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP);
}
private static int pack(int r, int g, int b, int a) {
return (a & 255) << 24 | (b & 255) << 16 | (g & 255) << 8 | r & 255;
public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
sink.quad(x1, y1, x2, y2, z, FastColor.ABGR32.fromArgb32(colour), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP);
}
}
}