From 83eddb01d8d692dc4523380a78b359ae90eda3af Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 28 Apr 2026 16:21:01 +0100 Subject: [PATCH 01/13] Update supported versions --- .github/ISSUE_TEMPLATE/bug_report.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index ca60bfdc4..331f3177b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -10,9 +10,9 @@ body: description: | What version of Minecraft are you using? If your version is not listed, please try to reproduce on one of the supported versions. options: - - 1.20.1 - - 1.21.1 - - 1.21.7 + - "1.20.1" + - "1.21.1" + - "26.1" validations: required: true - type: input From abe3d3410fd446a2484383c8078bd9ef20b03aa4 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 29 Apr 2026 11:42:56 +0100 Subject: [PATCH 02/13] Remove pathWithExtension This doesn't actually do anything: paths will never have a trailing slash, so it's just equivalent to `x..".lua"`. --- .../data/computercraft/lua/rom/programs/shell.lua | 14 ++------------ .../test-rom/spec/programs/shell_spec.lua | 12 ++++++++++++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/programs/shell.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/programs/shell.lua index 2e7e19654..0d3e36edb 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/programs/shell.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/programs/shell.lua @@ -350,16 +350,6 @@ function shell.resolve(path) end end -local function pathWithExtension(_sPath, _sExt) - local nLen = #sPath - local sEndChar = string.sub(_sPath, nLen, nLen) - -- Remove any trailing slashes so we can add an extension to the path safely - if sEndChar == "/" or sEndChar == "\\" then - _sPath = string.sub(_sPath, 1, nLen - 1) - end - return _sPath .. "." .. _sExt -end - --- Resolve a program, using the [program path][`path`] and list of [aliases][`aliases`]. -- -- @tparam string command The name of the program @@ -384,7 +374,7 @@ function shell.resolveProgram(command) if fs.exists(sPath) and not fs.isDir(sPath) then return sPath else - local sPathLua = pathWithExtension(sPath, "lua") + local sPathLua = sPath .. ".lua" if fs.exists(sPathLua) and not fs.isDir(sPathLua) then return sPathLua end @@ -398,7 +388,7 @@ function shell.resolveProgram(command) if fs.exists(sPath) and not fs.isDir(sPath) then return sPath else - local sPathLua = pathWithExtension(sPath, "lua") + local sPathLua = sPath .. ".lua" if fs.exists(sPathLua) and not fs.isDir(sPathLua) then return sPathLua end diff --git a/projects/core/src/test/resources/test-rom/spec/programs/shell_spec.lua b/projects/core/src/test/resources/test-rom/spec/programs/shell_spec.lua index aa80d4602..7e7a717a1 100644 --- a/projects/core/src/test/resources/test-rom/spec/programs/shell_spec.lua +++ b/projects/core/src/test/resources/test-rom/spec/programs/shell_spec.lua @@ -152,6 +152,18 @@ describe("The shell", function() shell.resolveProgram("ls") expect.error(shell.resolveProgram, nil):eq("bad argument #1 (string expected, got nil)") end) + + it("finds files on the shell path", function() + expect(shell.resolveProgram("edit")):eq("rom/programs/edit.lua") + expect(shell.resolveProgram("edit.lua")):eq("rom/programs/edit.lua") + end) + + it("finds programs even with a trailing slash", function() + -- This feels silly: "edit/" is a directory, not the name of a file. However, + -- fs.combine (and CC's other path normalisation code) strips trailing slashes, + -- so this should still resolve the program. + expect(shell.resolveProgram("/rom/programs/edit/")):eq("rom/programs/edit.lua") + end) end) describe("shell.complete", function() From 5be300dcb83bd28601bc9472feddaca60ae2f771 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 29 Apr 2026 12:18:44 +0100 Subject: [PATCH 03/13] Update dependencies --- gradle/libs.versions.toml | 30 +++---- gradle/wrapper/gradle-wrapper.jar | Bin 46175 -> 48462 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 2 +- gradlew.bat | 31 +++---- package-lock.json | 102 +++++++++++------------ 6 files changed, 80 insertions(+), 89 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 510c27dff..8236e1887 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,15 +25,15 @@ slf4j = "2.0.1" # Core dependencies (independent of Minecraft) asm = "9.9.1" autoService = "1.1.1" -checkerFramework = "3.51.1" +checkerFramework = "4.0.0" cobalt = { strictly = "0.9.7" } -commonsCli = "1.10.0" -jetbrainsAnnotations = "26.0.2-1" +commonsCli = "1.11.0" +jetbrainsAnnotations = "26.1.0" jspecify = "1.0.0" jzlib = "1.1.3" -kotlin = "2.3.20" +kotlin = "2.3.21" kotlin-coroutines = "1.10.2" -nightConfig = "3.8.3" +nightConfig = "3.8.4" # Minecraft mods emi = "1.0.8+1.20.1" @@ -52,29 +52,29 @@ create-fabric = "6.0.7.0+mc1.20.1-build.1716" # Testing hamcrest = "3.0" jqwik = "1.9.3" -junit = "6.0.1" -junitPlatform = "6.0.1" +junit = "6.0.3" +junitPlatform = "6.0.3" jmh = "1.37" # Build tools cctJavadoc = "1.9.0" -checkstyle = "12.1.1" -errorProne-core = "2.45.0" +checkstyle = "13.4.1" +errorProne-core = "2.49.0" errorProne-plugin = "4.3.0" fabric-loom = "1.16.1" githubRelease = "2.5.2" -gradleVersions = "0.53.0" +gradleVersions = "0.54.0" ideaExt = "1.3" illuaminate = "0.1.0-83-g1131f68" -lwjgl = "3.3.6" +lwjgl = "3.4.1" minotaur = "2.8.7" modDevGradle = "2.0.141" -nullAway = "0.12.11" -shadow = "9.2.2" -spotless = "8.0.0" +nullAway = "0.13.4" +shadow = "9.4.1" +spotless = "8.4.0" teavm = "0.14.0-SQUID.1" vanillaExtract = "0.3.1" -versionCatalogUpdate = "1.0.1" +versionCatalogUpdate = "1.1.0" [libraries] # Normal dependencies diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 61285a659d17295f1de7c53e24fdf13ad755c379..b1b8ef56b44f16b14dc800fa8103a6d89abb526f 100644 GIT binary patch delta 39773 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 delta 37584 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c61a118f7..b52fb7e71 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,9 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip networkTimeout=10000 +retries=0 +retryBackOffMs=500 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index adff685a0..b9bb139f7 100755 --- a/gradlew +++ b/gradlew @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/gradlew.bat b/gradlew.bat index c4bdd3ab8..24c62d56f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -23,8 +23,8 @@ @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @@ -51,7 +51,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,7 +65,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line @@ -73,21 +73,10 @@ goto fail @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/package-lock.json b/package-lock.json index 64499e04d..08cfea40c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -971,9 +971,9 @@ } }, "node_modules/@swc/core": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.30.tgz", - "integrity": "sha512-R8VQbQY1BZcbIF2p3gjlTCwAQzx1A194ugWfwld5y+WgVVWqVKm7eURGGOVbQVubgKWzidP2agomBbg96rZilQ==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.32.tgz", + "integrity": "sha512-/eWL0n43D64QWEUHLtTE+jDqjkJhyidjkDhv6f0uJohOUAhywxQ9wXYp845DNNds0JpCdI4Uo0a9bl+vbXf+ew==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -989,18 +989,18 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.15.30", - "@swc/core-darwin-x64": "1.15.30", - "@swc/core-linux-arm-gnueabihf": "1.15.30", - "@swc/core-linux-arm64-gnu": "1.15.30", - "@swc/core-linux-arm64-musl": "1.15.30", - "@swc/core-linux-ppc64-gnu": "1.15.30", - "@swc/core-linux-s390x-gnu": "1.15.30", - "@swc/core-linux-x64-gnu": "1.15.30", - "@swc/core-linux-x64-musl": "1.15.30", - "@swc/core-win32-arm64-msvc": "1.15.30", - "@swc/core-win32-ia32-msvc": "1.15.30", - "@swc/core-win32-x64-msvc": "1.15.30" + "@swc/core-darwin-arm64": "1.15.32", + "@swc/core-darwin-x64": "1.15.32", + "@swc/core-linux-arm-gnueabihf": "1.15.32", + "@swc/core-linux-arm64-gnu": "1.15.32", + "@swc/core-linux-arm64-musl": "1.15.32", + "@swc/core-linux-ppc64-gnu": "1.15.32", + "@swc/core-linux-s390x-gnu": "1.15.32", + "@swc/core-linux-x64-gnu": "1.15.32", + "@swc/core-linux-x64-musl": "1.15.32", + "@swc/core-win32-arm64-msvc": "1.15.32", + "@swc/core-win32-ia32-msvc": "1.15.32", + "@swc/core-win32-x64-msvc": "1.15.32" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -1012,9 +1012,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.30.tgz", - "integrity": "sha512-VvpP+vq08HmGYewMWvrdsxh9s2lthz/808zXm8Yu5kaqeR8Yia2b0eYXleHQ3VAjoStUDk6LzTheBW9KXYQdMA==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.32.tgz", + "integrity": "sha512-/YWMvJDPu+AAwuUsM2G+DNQ/7zhodURGzdQyewEqcvgklAdDHs3LwQmLLnyn6SJl8DT8UOxkbzK+D1PmPeelRg==", "cpu": [ "arm64" ], @@ -1029,9 +1029,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.30.tgz", - "integrity": "sha512-WiJA0hiZI3nwQAO6mu5RqigtWGDtth4Hiq6rbZxAaQyhIcqKIg5IoMRc1Y071lrNJn29eEDMC86Rq58xgUxlDg==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.32.tgz", + "integrity": "sha512-KOTXJXdAhWL+hZ77MYP3z+4pcMFaQhQ74yqyN1uz093q0YnbxpqMtYpPISbYvMHzVRNNx5kN+9RZAXEaadhWVA==", "cpu": [ "x64" ], @@ -1046,9 +1046,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.30.tgz", - "integrity": "sha512-YANuFUo48kIT6plJgCD0keae9HFXfjxsbvsgevqc0hr/07X/p7sAWTFOGYEc2SXcASaK7UvuQqzlbW8pr7R79g==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.32.tgz", + "integrity": "sha512-oOoxLweljlc0A4X8ybsgxV7cVaYTwBOg2iMDJcFR3Sr48C+lsv9VzSmqdK/IVIXF4W4GjLc3VqTAdSMXlfVLuQ==", "cpu": [ "arm" ], @@ -1063,9 +1063,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.30.tgz", - "integrity": "sha512-VndG8jaR4ugY6u+iVOT0Q+d2fZd7sLgjPgN8W/Le+3EbZKl+cRfFxV7Eoz4gfLqhmneZPdcIzf9T3LkgkmqNLg==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.32.tgz", + "integrity": "sha512-oDzEkdl6D6BAWdMtU5KGO7y3HR5fJcvByNLyEk9+ugj8nP5Ovb7P4kBcStBXc4MPExFGQryehiINMlmY8HlclA==", "cpu": [ "arm64" ], @@ -1083,9 +1083,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.30.tgz", - "integrity": "sha512-1SYGs2l0Yyyi0pR/P/NKz/x0kqxkoiw+BXeJjLUdecSk/KasncWlJrc6hOvFSgKHOBrzgM5jwuluKtlT8dnrcA==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.32.tgz", + "integrity": "sha512-omcqjoZP/b8D8PuczVoRwJieC6ibj7qIxTftNYokz4/aSmKFHvsd7nIFfPk5ZvtzncbH4AY7+Dkr/Lp2gWxYeA==", "cpu": [ "arm64" ], @@ -1103,9 +1103,9 @@ } }, "node_modules/@swc/core-linux-ppc64-gnu": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.30.tgz", - "integrity": "sha512-TXREtiXeRhbfDFbmhnkIsXpKfzbfT73YkV2ZF6w0sfxgjC5zI2ZAbaCOq25qxvegofj2K93DtOpm9RLaBgqR2g==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.32.tgz", + "integrity": "sha512-KGkTMyz/Tbn3PBNu0AVZ4GTDFKnICrYcTiNPZq8DrvK42pnFsf3GNDrIG9E5AtQlTmC0YigkWKmu0eMcfTrmgA==", "cpu": [ "ppc64" ], @@ -1123,9 +1123,9 @@ } }, "node_modules/@swc/core-linux-s390x-gnu": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.30.tgz", - "integrity": "sha512-DCR2YYeyd6DQE4OuDhImouuNcjXEiEdnn1Y0DyGteugPEDvVuvYk8Xddi+4o2SgWH6jiW8/I+3emZvbep1NC+g==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.32.tgz", + "integrity": "sha512-G3Aa4tVS/3OGZBkoNIwUF9F6RAy+Osb4GOlo62SinLmDiErz/ykmM7KH0wkz6l9kM8jJq1HyAM6atJTUEbBk7g==", "cpu": [ "s390x" ], @@ -1143,9 +1143,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.30.tgz", - "integrity": "sha512-5Pizw3NgfOJ5BJOBK8TIRa59xFW2avESTOBDPTAYwZYa1JNDs+KMF9lUfjJiJLM5HiMs/wPheA9eiT0q9m2AoA==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.32.tgz", + "integrity": "sha512-ERsjfGcj6CBmj3vJnGDO8m8rTvw6RqMcWo1dogOtNx3/+/0+NNpJiXDobJrr1GwInI/BHAEkvSFIH6d2LqPcUQ==", "cpu": [ "x64" ], @@ -1163,9 +1163,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.30.tgz", - "integrity": "sha512-qyqydP/wyH8alcIP4a2hnGSjHLJjm9H7yDFup+CPy9oTahFgLLwnNcv5UHXqO2Qs3AIND+cls5f/Bb6hqpxdgA==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.32.tgz", + "integrity": "sha512-N4Ggahe/8SUbTX50P6EdhbW9YWcgbZVb52R4cq6MK+zsoMjRq7rGvV5ztA05QnbaCYqMYx8rTY7KAIA3Crdo4Q==", "cpu": [ "x64" ], @@ -1183,9 +1183,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.30.tgz", - "integrity": "sha512-CaQENgDHVGOg1mSF5sQVgvfFHG9kjMor2rkLMLeLOkfZYNj13ppnJ9+lfaBZLZUMMbnlGQnavCJb8PVBUOso7Q==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.32.tgz", + "integrity": "sha512-01yN0o9jvo8xBTP12aPK2wW8b41jmOlGbDDlAnoynotc4pO6xA0zby9f1z6j++qXDpGBttLySq1omgVrlQKYcw==", "cpu": [ "arm64" ], @@ -1200,9 +1200,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.30.tgz", - "integrity": "sha512-30VdLeGk6fugiUs/kUdJ/pAg7z/zpvVbR11RH60jZ0Z42WIeIniYx0rLEWN7h/pKJ3CopqsQ3RsogCAkRKiA2g==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.32.tgz", + "integrity": "sha512-fLagI9XZYNpTcmlqAcp3KBtmj7E19WCmYD80Jxj1Kn5tGNa7yxNLd3NNdWxuZGUPl5iC0/KqZru7g08gF6Fsrw==", "cpu": [ "ia32" ], @@ -1217,9 +1217,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.30", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.30.tgz", - "integrity": "sha512-4iObHPR+Q4oDY110EF5SF5eIaaVJNpMdG9C0q3Q92BsJ5y467uHz7sYQhP60WYlLFsLQ1el2YrIPUItUAQGOKg==", + "version": "1.15.32", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.32.tgz", + "integrity": "sha512-gbc2bQ/T2CiR+w0OvcVKwLOFAcPZBvmWmolbwpg1E8UrpeC03DGtyMUApOHNXNYWA3SHFrYXCQtosrcMza1YFg==", "cpu": [ "x64" ], From 39a12893e327af2efebf2600fb883f3298528143 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 2 May 2026 13:46:52 +0100 Subject: [PATCH 04/13] Fix handling of integer indexes in CobaltLuaTable - Add a new LuaTable.get(int) overload to handle getting an integer key instead. - Update CobaltLuaMachine for more sensible Java->LuaValue conversion for looking up keys. - Add some tests for our two LuaTable implementations. - Randomly reformat two files because spotless is ??? Fixes #2430 --- .../gametest/core/ManagedComputers.kt | 2 + .../computercraft/api/lua/LuaTable.java | 33 +++++++--- .../computercraft/core/lua/TableImpl.java | 30 ++++++++- .../core/lua/CobaltLuaTableTest.java | 35 ++++++++++ .../core/lua/LuaTableContract.java | 66 +++++++++++++++++++ .../core/lua/ObjectLuaTableTest.java | 16 +++++ .../kotlin/cc/tweaked/linter/SideChecker.kt | 1 + 7 files changed, 170 insertions(+), 13 deletions(-) create mode 100644 projects/core/src/test/java/dan200/computercraft/core/lua/CobaltLuaTableTest.java create mode 100644 projects/core/src/test/java/dan200/computercraft/core/lua/LuaTableContract.java create mode 100644 projects/core/src/test/java/dan200/computercraft/core/lua/ObjectLuaTableTest.java diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt index 6804668b3..0a7fcf694 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/core/ManagedComputers.kt @@ -68,7 +68,9 @@ object ManagedComputers : ILuaMachine.Factory { val label = os.computerLabel return when { id != 1 -> CobaltLuaMachine(environment, bios) + label != null && label[0] != null -> KotlinMachine(environment, label[0] as String) + else -> { LOGGER.error("Kotlin Lua machine must have a label") CobaltLuaMachine(environment, bios) diff --git a/projects/core-api/src/main/java/dan200/computercraft/api/lua/LuaTable.java b/projects/core-api/src/main/java/dan200/computercraft/api/lua/LuaTable.java index fa42b1395..ecc091565 100644 --- a/projects/core-api/src/main/java/dan200/computercraft/api/lua/LuaTable.java +++ b/projects/core-api/src/main/java/dan200/computercraft/api/lua/LuaTable.java @@ -21,6 +21,19 @@ import static dan200.computercraft.api.lua.LuaValues.*; * @see ObjectArguments */ public interface LuaTable extends Map { + /** + * Return the value to which a specific integer key is mapped, or {@code null} if there is no mapping for the key. + *

+ * This should be used when accessing integer keys, as numeric keys within the table are typically normalised to + * doubles. + * + * @param index The key to look up. + * @return The corresponding value, or {@code null} if not present. + */ + default @Nullable Object get(int index) { + return get((double) index); + } + /** * Compute the length of the array part of this table. * @@ -42,7 +55,7 @@ public interface LuaTable extends Map { * @since 1.116 */ default double getDouble(int index) throws LuaException { - Object value = get((double) index); + var value = get(index); if (!(value instanceof Number number)) throw badTableItem(index, "number", getType(value)); return number.doubleValue(); } @@ -70,7 +83,7 @@ public interface LuaTable extends Map { * @throws LuaException If the value is not an integer. */ default long getLong(int index) throws LuaException { - Object value = get((double) index); + var value = get(index); if (!(value instanceof Number number)) throw badTableItem(index, "number", getType(value)); checkFiniteIndex(index, number.doubleValue()); return number.longValue(); @@ -145,7 +158,7 @@ public interface LuaTable extends Map { * @since 1.116 */ default boolean getBoolean(int index) throws LuaException { - Object value = get((double) index); + var value = get(index); if (!(value instanceof Boolean bool)) throw badTableItem(index, "boolean", getType(value)); return bool; } @@ -173,7 +186,7 @@ public interface LuaTable extends Map { * @since 1.116 */ default String getString(int index) throws LuaException { - Object value = get((double) index); + var value = get(index); if (!(value instanceof String string)) throw badTableItem(index, "string", getType(value)); return string; } @@ -204,7 +217,7 @@ public interface LuaTable extends Map { * @since 1.116 */ default Map getTable(int index) throws LuaException { - Object value = get((double) index); + var value = get(index); if (!(value instanceof Map table)) throw badTableItem(index, "table", getType(value)); return table; } @@ -236,7 +249,7 @@ public interface LuaTable extends Map { * @since 1.116 */ default Optional optDouble(int index) throws LuaException { - Object value = get((double) index); + var value = get(index); if (value == null) return Optional.empty(); if (!(value instanceof Number number)) throw badTableItem(index, "number", getType(value)); return Optional.of(number.doubleValue()); @@ -267,7 +280,7 @@ public interface LuaTable extends Map { * @since 1.116 */ default Optional optLong(int index) throws LuaException { - Object value = get((double) index); + var value = get(index); if (value == null) return Optional.empty(); if (!(value instanceof Number number)) throw badTableItem(index, "number", getType(value)); checkFiniteIndex(index, number.doubleValue()); @@ -351,7 +364,7 @@ public interface LuaTable extends Map { * @since 1.116 */ default Optional optBoolean(int index) throws LuaException { - Object value = get((double) index); + var value = get(index); if (value == null) return Optional.empty(); if (!(value instanceof Boolean bool)) throw badTableItem(index, "boolean", getType(value)); return Optional.of(bool); @@ -381,7 +394,7 @@ public interface LuaTable extends Map { * @since 1.116 */ default Optional optString(int index) throws LuaException { - Object value = get((double) index); + var value = get(index); if (value == null) return Optional.empty(); if (!(value instanceof String string)) throw badTableItem(index, "string", getType(value)); return Optional.of(string); @@ -414,7 +427,7 @@ public interface LuaTable extends Map { * @since 1.116 */ default Optional> optTable(int index) throws LuaException { - Object value = get((double) index); + var value = get(index); if (value == null) return Optional.empty(); if (!(value instanceof Map table)) throw badTableItem(index, "table", getType(value)); return Optional.of(table); diff --git a/projects/core/src/main/java/dan200/computercraft/core/lua/TableImpl.java b/projects/core/src/main/java/dan200/computercraft/core/lua/TableImpl.java index 8c3fa6108..5497d16c0 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/lua/TableImpl.java +++ b/projects/core/src/main/java/dan200/computercraft/core/lua/TableImpl.java @@ -6,9 +6,11 @@ package dan200.computercraft.core.lua; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaValues; +import org.jetbrains.annotations.VisibleForTesting; import org.jspecify.annotations.Nullable; import org.squiddev.cobalt.*; +import java.nio.ByteBuffer; import java.util.*; import static dan200.computercraft.api.lua.LuaValues.badTableItem; @@ -37,6 +39,7 @@ class TableImpl implements dan200.computercraft.api.lua.LuaTable @Override public long getLong(int index) throws LuaException { + checkValid(); var value = table.rawget(index); if (!(value instanceof LuaNumber)) throw LuaValues.badTableItem(index, "number", value.typeName()); if (value instanceof LuaInteger) return value.toInteger(); @@ -58,9 +61,24 @@ class TableImpl implements dan200.computercraft.api.lua.LuaTable private LuaValue getImpl(Object o) { checkValid(); - if (o instanceof String s) return table.rawget(s); - if (o instanceof Integer i) return table.rawget(i); - return Constants.NIL; + var value = convertValue(o); + return value == null ? Constants.NIL : table.rawget(value); + } + + @VisibleForTesting + static @Nullable LuaValue convertValue(@Nullable Object object) { + if (object == null) return Constants.NIL; + if (object instanceof Boolean bool) return ValueFactory.valueOf(bool); + if (object instanceof Double num) return ValueFactory.valueOf(num); + if (object instanceof String str) return ValueFactory.valueOf(str); + if (object instanceof byte[] b) return ValueFactory.valueOf(Arrays.copyOf(b, b.length)); + if (object instanceof ByteBuffer b) { + var bytes = new byte[b.remaining()]; + b.get(bytes); + return ValueFactory.valueOf(bytes); + } + + return null; } @Override @@ -74,6 +92,12 @@ class TableImpl implements dan200.computercraft.api.lua.LuaTable return CobaltLuaMachine.toObject(getImpl(o), null); } + @Override + public @Nullable Object get(int index) { + checkValid(); + return CobaltLuaMachine.toObject(table.rawget(index), null); + } + private Map getBackingMap() { checkValid(); if (backingMap != null) return backingMap; diff --git a/projects/core/src/test/java/dan200/computercraft/core/lua/CobaltLuaTableTest.java b/projects/core/src/test/java/dan200/computercraft/core/lua/CobaltLuaTableTest.java new file mode 100644 index 000000000..391d3e9e8 --- /dev/null +++ b/projects/core/src/test/java/dan200/computercraft/core/lua/CobaltLuaTableTest.java @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2026 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.core.lua; + +import org.squiddev.cobalt.*; + +import java.util.Map; + +class CobaltLuaTableTest implements LuaTableContract { + @Override + public TableImpl create(Map map) { + try { + return new TableImpl(VarargArguments.of(Constants.NONE), convertMap(map)); + } catch (LuaError e) { + throw new RuntimeException(e); + } + } + + private static LuaValue convert(Object object) throws LuaError { + var value = TableImpl.convertValue(object); + if (value != null) return value; + + if (object instanceof Map x) return convertMap(x); + if (object instanceof Integer x) return ValueFactory.valueOf(x); + throw new IllegalArgumentException("Unknown value " + object); + } + + private static LuaTable convertMap(Map map) throws LuaError { + var out = new LuaTable(); + for (var entry : map.entrySet()) out.rawset(convert(entry.getKey()), convert(entry.getValue())); + return out; + } +} diff --git a/projects/core/src/test/java/dan200/computercraft/core/lua/LuaTableContract.java b/projects/core/src/test/java/dan200/computercraft/core/lua/LuaTableContract.java new file mode 100644 index 000000000..1423ccf3b --- /dev/null +++ b/projects/core/src/test/java/dan200/computercraft/core/lua/LuaTableContract.java @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2026 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.core.lua; + +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.LuaTable; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * Test Interface defining the behaviour of a {@link LuaTable} implementation. + * + * @param The implementation of {@link LuaTable} we are testing. + */ +public interface LuaTableContract> { + T create(Map map); + + default T createList(List list) { + var out = new HashMap<>(); + var i = 0; + for (var elem : list) { + var idx = ++i; + // We normalise our index to be a double, to match the behaviour of CobaltLuaMachine.toValue, which converts + // all numbers to doubles. + if (elem != null) out.put((double) idx, elem); + } + return create(out); + } + + @Test + default void testLength() { + assertEquals(0, createList(List.of()).length()); + assertEquals(1, createList(List.of("a")).length()); + assertEquals(2, createList(List.of("a", "a")).length()); + assertEquals(1, createList(Arrays.asList("a", null, "a")).length()); + } + + @Test + default void testGetIntLikeKey() { + assertEquals("a", createList(List.of("a", "b", "c")).get(1)); + assertEquals("a", createList(List.of("a", "b", "c")).get(1.0)); + // This is a little dubious, but ensures we have consistent behaviour between implementations (doubles are + // the only number) and we don't need to handle double/int normalisation within ObjectLuaTable. + assertNull(createList(List.of("a", "b", "c")).get((Object) 1)); + assertNull(createList(List.of("a", "b", "c")).get(1.0f)); + assertNull(createList(List.of("a", "b", "c")).get((Object) (short) 1)); + } + + @Test + default void testGetInt() throws LuaException { + assertEquals("a", createList(List.of("a")).get(1)); + assertEquals(true, createList(List.of(true)).getBoolean(1)); + assertEquals(12345, createList(List.of(12345.0)).getInt(1)); + assertEquals(12345L, createList(List.of(12345.0)).getLong(1)); + assertEquals("abc", createList(List.of("abc")).getString(1)); + } +} diff --git a/projects/core/src/test/java/dan200/computercraft/core/lua/ObjectLuaTableTest.java b/projects/core/src/test/java/dan200/computercraft/core/lua/ObjectLuaTableTest.java new file mode 100644 index 000000000..66df7407f --- /dev/null +++ b/projects/core/src/test/java/dan200/computercraft/core/lua/ObjectLuaTableTest.java @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2026 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.core.lua; + +import dan200.computercraft.api.lua.ObjectLuaTable; + +import java.util.Map; + +class ObjectLuaTableTest implements LuaTableContract { + @Override + public ObjectLuaTable create(Map map) { + return new ObjectLuaTable(map); + } +} diff --git a/projects/lints/src/main/kotlin/cc/tweaked/linter/SideChecker.kt b/projects/lints/src/main/kotlin/cc/tweaked/linter/SideChecker.kt index e2b963e25..36d145a24 100644 --- a/projects/lints/src/main/kotlin/cc/tweaked/linter/SideChecker.kt +++ b/projects/lints/src/main/kotlin/cc/tweaked/linter/SideChecker.kt @@ -75,6 +75,7 @@ internal class SideProvider { private fun getSideImpl(sym: Symbol): Optional = when (sym.getKind()) { ElementKind.MODULE -> Optional.empty() + ElementKind.PACKAGE -> { val pkg = sym.toString() when { From 663ffed4337da0dc3d82ace1e813e3c78b4a8c99 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 2 May 2026 17:32:53 +0100 Subject: [PATCH 05/13] Stop using our fork of TeaVM 0.14.0 is in maven central, which has all the patches we need. --- buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts | 1 - gradle/libs.versions.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts b/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts index 25fa69168..c29cd29fc 100644 --- a/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts +++ b/buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts @@ -57,7 +57,6 @@ repositories { includeGroup("me.shedaniel.cloth") includeGroup("me.shedaniel") includeGroup("mezz.jei") - includeGroup("org.teavm") includeModule("com.terraformersmc", "modmenu") includeModule("me.lucko", "fabric-permissions-api") } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8236e1887..18f1d3c6d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -72,7 +72,7 @@ modDevGradle = "2.0.141" nullAway = "0.13.4" shadow = "9.4.1" spotless = "8.4.0" -teavm = "0.14.0-SQUID.1" +teavm = "0.14.0" vanillaExtract = "0.3.1" versionCatalogUpdate = "1.1.0" From 3abbb4ff4bf67b0f2653f27115f6f21fa561f217 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 5 May 2026 11:01:32 +0100 Subject: [PATCH 06/13] Remove Fabric Create integration This hasn't updated to 1.21.1. A separate mod (Create Fly) adds support for 1.21.1/26.1, but has an incompatible API with normal Fabric, so wait to see what happens rather than jumping to one or the other. --- projects/fabric/build.gradle.kts | 1 - .../computercraft/shared/ComputerCraft.java | 3 -- .../shared/integration/CreateIntegration.java | 34 ------------------- 3 files changed, 38 deletions(-) delete mode 100644 projects/fabric/src/main/java/dan200/computercraft/shared/integration/CreateIntegration.java diff --git a/projects/fabric/build.gradle.kts b/projects/fabric/build.gradle.kts index 6d4dc69eb..53f19eac2 100644 --- a/projects/fabric/build.gradle.kts +++ b/projects/fabric/build.gradle.kts @@ -78,7 +78,6 @@ dependencies { exclude("net.fabricmc", "fabric-loader") exclude("net.fabricmc.fabric-api") } - modCompileOnly(libs.create.fabric) { isTransitive = false } modClientRuntimeOnly(libs.bundles.externalMods.fabric.runtime) { exclude("net.fabricmc", "fabric-loader") diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java index 904ea1bff..5808b7d55 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java @@ -17,7 +17,6 @@ import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.shared.command.CommandComputerCraft; import dan200.computercraft.shared.config.ConfigSpec; import dan200.computercraft.shared.details.FluidDetails; -import dan200.computercraft.shared.integration.CreateIntegration; import dan200.computercraft.shared.network.NetworkMessages; import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods; import dan200.computercraft.shared.platform.FabricConfigFile; @@ -125,8 +124,6 @@ public class ComputerCraft { ComputerCraftAPI.registerGenericSource(new InventoryMethods()); Peripherals.addGenericLookup(InventoryMethods::extractContainer); - - if (FabricLoader.getInstance().isModLoaded(CreateIntegration.ID)) CreateIntegration.setup(); } private static void registerPayloadType(PayloadTypeRegistry registry, CustomPacketPayload.TypeAndCodec type) { diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/integration/CreateIntegration.java b/projects/fabric/src/main/java/dan200/computercraft/shared/integration/CreateIntegration.java deleted file mode 100644 index a606f9376..000000000 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/integration/CreateIntegration.java +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.integration; - -import com.simibubi.create.api.contraption.BlockMovementChecks; -import com.simibubi.create.api.contraption.BlockMovementChecks.CheckResult; -import dan200.computercraft.shared.peripheral.modem.wired.CableBlock; -import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlock; - -/** - * Integration with Create. - */ -public final class CreateIntegration { - public static final String ID = "create"; - - private CreateIntegration() { - } - - public static void setup() { - // Allow modems to be treated as "attached" to their adjacent block. - BlockMovementChecks.registerAttachedCheck((state, world, pos, direction) -> { - var block = state.getBlock(); - if (block instanceof WirelessModemBlock) { - return CheckResult.of(state.getValue(WirelessModemBlock.FACING) == direction); - } else if (block instanceof CableBlock) { - return CheckResult.of(state.getValue(CableBlock.MODEM).getFacing() == direction); - } else { - return CheckResult.PASS; - } - }); - } -} From defb713483285a41f9b791458252814376fe9440 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Thu, 7 May 2026 13:36:05 +0100 Subject: [PATCH 07/13] Add commands.getDimension() Debated "commands.getBlockDimension()" instead, to match getBlockPosition, but I think this is fine. Fixes #1866. --- .../shared/computer/apis/CommandAPI.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java index 0613620d8..8fbda83b1 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java @@ -157,6 +157,18 @@ public class CommandAPI implements ILuaAPI { return Collections.unmodifiableList(result); } + /** + * Get the name of the dimension the current command computer is in, such as {@code minecraft:overworld}. + * + * @return The dimension the computer is in. + * @see #getBlockPosition() + * @since 1.119.0 + */ + @LuaFunction + public final String getDimension() { + return computer.getLevel().dimension().location().toString(); + } + /** * Get the position of the current command computer. * @@ -165,10 +177,10 @@ public class CommandAPI implements ILuaAPI { * @cc.treturn number This computer's y position. * @cc.treturn number This computer's z position. * @cc.see gps.locate To get the position of a non-command computer. + * @see #getDimension() */ @LuaFunction public final Object[] getBlockPosition() { - // This is probably safe to do on the Lua thread. Probably. var pos = computer.getPosition(); return new Object[]{ pos.getX(), pos.getY(), pos.getZ() }; } From b23ac67702fe2b78f9ac3fb51e6de73a518434b0 Mon Sep 17 00:00:00 2001 From: sircfenner Date: Sat, 9 May 2026 11:55:07 +0200 Subject: [PATCH 08/13] Fix `min` & `sec` field defaults in os.time (#2436) These defaults are wrongly set to 12. They should be 0 for parity with stock Lua. --- .../computercraft/core/apis/LuaDateTime.java | 4 ++-- .../resources/test-rom/spec/apis/os_spec.lua | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/LuaDateTime.java b/projects/core/src/main/java/dan200/computercraft/core/apis/LuaDateTime.java index d53f60795..d860f2eda 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/LuaDateTime.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/LuaDateTime.java @@ -77,8 +77,8 @@ final class LuaDateTime { var month = getField(table, "month", -1); var day = getField(table, "day", -1); var hour = getField(table, "hour", 12); - var minute = getField(table, "min", 12); - var second = getField(table, "sec", 12); + var minute = getField(table, "min", 0); + var second = getField(table, "sec", 0); var time = LocalDateTime.of(year, month, day, hour, minute, second); var isDst = getBoolField(table, "isdst"); diff --git a/projects/core/src/test/resources/test-rom/spec/apis/os_spec.lua b/projects/core/src/test/resources/test-rom/spec/apis/os_spec.lua index 9c5980adf..4ce6138cc 100644 --- a/projects/core/src/test/resources/test-rom/spec/apis/os_spec.lua +++ b/projects/core/src/test/resources/test-rom/spec/apis/os_spec.lua @@ -143,6 +143,24 @@ describe("The os library", function() local t2 = os.time { year = 2000, month = 10, day = 1, hour = 23, min = 10, sec = 19 } expect(t1 - t2):eq(60 * 2 - 2) end) + + it("uses correct date table default for hour", function() + local t1 = os.time { year = 1970, month = 1, day = 1, hour = nil, min = 0, sec = 0 } + local t2 = os.time { year = 1970, month = 1, day = 1, hour = 12, min = 0, sec = 0 } + expect(t1):eq(t2) + end) + + it("uses correct date table default for min", function() + local t1 = os.time { year = 1970, month = 1, day = 1, hour = 0, min = nil, sec = 0 } + local t2 = os.time { year = 1970, month = 1, day = 1, hour = 0, min = 0, sec = 0 } + expect(t1):eq(t2) + end) + + it("uses correct date table default for sec", function() + local t1 = os.time { year = 1970, month = 1, day = 1, hour = 0, min = 0, sec = nil } + local t2 = os.time { year = 1970, month = 1, day = 1, hour = 0, min = 0, sec = 0 } + expect(t1):eq(t2) + end) end) describe("os.day", function() From 2f7897315980195530753fd4b53f7d6c5192cde9 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Thu, 14 May 2026 18:44:35 +0100 Subject: [PATCH 09/13] Update Cobalt to 0.9.9 Closes #2361 --- doc/reference/feature_compat.md | 6 ++++++ gradle/libs.versions.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/reference/feature_compat.md b/doc/reference/feature_compat.md index 1fc50c355..405d25cee 100644 --- a/doc/reference/feature_compat.md +++ b/doc/reference/feature_compat.md @@ -91,6 +91,12 @@ compatibility for these newer versions. | Remove `*` from `file:read` modes | ✔ | | | Metamethods respected in `table.*`, `ipairs` | ✔ | | +## Lua 5.5 +| Feature | Supported? | Notes | +|------------------------------------|------------|-------| +| `table.create` | ✔ | | +| `utf8.offset` returns end position | ✔ | | + ## Lua 5.0 | Feature | Supported? | Notes | |----------------------------------|------------|--------------------------------------------------| diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 18f1d3c6d..76f9ba08e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ slf4j = "2.0.1" asm = "9.9.1" autoService = "1.1.1" checkerFramework = "4.0.0" -cobalt = { strictly = "0.9.7" } +cobalt = { strictly = "0.9.9" } commonsCli = "1.11.0" jetbrainsAnnotations = "26.1.0" jspecify = "1.0.0" From 39aab8c38401f509bc7abb302385cc164874b5d5 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Fri, 15 May 2026 14:30:48 +0100 Subject: [PATCH 10/13] Add cc.base64 module Don't love the API here, but really trying to overthink *everything*. Closes #1960. --- .../lua/rom/modules/main/cc/base64.lua | 182 ++++++++++++++++++ .../test-rom/spec/modules/cc/base64_spec.lua | 66 +++++++ 2 files changed, 248 insertions(+) create mode 100644 projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/base64.lua create mode 100644 projects/core/src/test/resources/test-rom/spec/modules/cc/base64_spec.lua diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/base64.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/base64.lua new file mode 100644 index 000000000..b11b99f50 --- /dev/null +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/base64.lua @@ -0,0 +1,182 @@ +-- SPDX-FileCopyrightText: 2026 The CC: Tweaked Developers +-- +-- SPDX-License-Identifier: MPL-2.0 + +--[[- The [`cc.base64`] module provides functions for converting binary data to +and from [Base64](https://en.wikipedia.org/wiki/Base64). + +@usage Encode and decode a string from Base64. + + local base64 = require "cc.base64" + print(base64.encode("Hello, world")) + print(base64.decode("SGVsbG8sIHdvcmxk")) + +@since 1.119.0 +]] + +local expect = require "cc.expect".expect + +local rshift, byte, char, sub = bit32.rshift, string.byte, string.char, string.sub + +local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + +--[[- +Encode a binary string to Base64. + +@tparam string str The binary data to encode. +@tparam[opt="+/"] string alt_chars A string of length 2, used to encode the 62nd +and 63rd bit. +@treturn string The Base64 encoded data. + +@usage Convert a string to Base64 + + local base64 = require "cc.base64" + print(base64.encode("Hello, world!")) + +@usage Convert a string to [base64url]. This is an alternative form of Base64, +where the string is encoded with `"-_"` instead of `"+/"`. This allows the string +to be more easily used in URLs, though the padding `=` will still need escaping +with [`textutils.urlEncode`]. + + local base64 = require "cc.base64" + print(base64.encode("Test: \255\230", "-_")) + +[base64url]: https://datatracker.ietf.org/doc/html/rfc4648#section-5 "Base 64 Encoding with URL and Filename Safe Alphabet" +]] +local function encode(str, alt_chars) + expect(1, str, "string") + expect(2, alt_chars, "string", "nil") + + if alt_chars and #alt_chars ~= 2 then + error("alt_chars must be exactly two characters", 2) + end + + --[[ + The below code is optimised to run against Cobalt, so the code is not + entirely idiomatic. + - It's quicker to build use a table lookup and do `lookup[x]` than call + `sub(alphabet, x, x)`. + - As we don't have bit operations, it's quicker to do `x % y`, rather than + `band(x, y - 1)` + - Naive concatenation is quicker than appending to a table. + ]] + + local alphabet = alphabet .. (alt_chars or "+/") + local lookup = {} + for i = 1, #alphabet do lookup[i] = sub(alphabet, i, i) end + + local len = #str + local remainder = len % 3 + local out = "" + for i = 1, len - remainder, 3 do + local c1, c2, c3 = byte(str, i, i + 2) + out = out .. + lookup[rshift(c1, 2) + 1] .. + lookup[c1 % 4 * 16 + rshift(c2, 4) + 1] .. + lookup[c2 % 16 * 4 + rshift(c3, 6) + 1] .. + lookup[c3 % 64 + 1] + end + + if remainder == 2 then + local c1, c2 = byte(str, len - 1, len) + out = out .. + lookup[rshift(c1, 2) + 1] .. + lookup[c1 % 4 * 16 + rshift(c2, 4) + 1] .. + lookup[c2 % 16 * 4 + 1] .. + "=" + elseif remainder == 1 then + local c1 = byte(str, len) + out = out .. lookup[rshift(c1, 2) + 1] .. lookup[c1 % 4 * 16 + 1] .. "==" + end + + return out +end + +--[[- +Decode a Base64-encoded string back to its original data. + +This function requires the data to be valid Base64 with the trailing padding +bytes. + +@tparam string str The Base64-encoded data to decode. +@tparam[opt="+/"] string alt_chars A string of length 2, used to encode the 62nd +and 63rd bit. +@treturn[1] string The decoded data. +@treturn[2] nil If the data is not valid Base64, or is missing the trailing padding. +@treturn[2] string The reason the data failed to decode. + +@usage Decode a string from Base64 + + local base64 = require "cc.base64" + print(base64.decode("SGVsbG8sIHdvcmxk")) + +@usage Decode [base64url]-encoded data. + + local base64 = require "cc.base64" + print(base64.decode("VGVzdDog_-Y=", "-_")) + +[base64url]: https://datatracker.ietf.org/doc/html/rfc4648#section-5 "Base 64 Encoding with URL and Filename Safe Alphabet" +]] +local function decode(str, alt_chars) + expect(1, str, "string") + expect(2, alt_chars, "string", "nil") + + if alt_chars and #alt_chars ~= 2 then + error("alt_chars must be exactly two characters", 2) + end + + if not alt_chars then alt_chars = "+/" end + + local len = #str + + if (len % 4) ~= 0 or not str:find("^[%w%" .. alt_chars:sub(1, 1) .. "%" .. alt_chars:sub(2, 2) .. "]*=?=?$") then + return nil, "input is not valid base64" + end + + local alphabet = alphabet .. alt_chars + local lookup = {} + for i = 1, #alphabet do lookup[byte(alphabet, i)] = i - 1 end + + local padding + if sub(str, -2) == "==" then + padding = 2 + elseif sub(str, -1) == "=" then + padding = 1 + else + padding = 0 + end + + local out = "" + for i = 1, padding == 0 and len or len - 4, 4 do + local e1, e2, e3, e4 = byte(str, i, i + 3) + e1 = lookup[e1] + e2 = lookup[e2] + e3 = lookup[e3] + e4 = lookup[e4] + out = out .. char( + e1 * 4 + rshift(e2, 4), + e2 % 16 * 16 + rshift(e3, 2), + e3 % 4 * 64 + e4 + ) + end + + if padding == 2 then + local e1, e2 = byte(str, len - 3, len - 2) + e1 = lookup[e1] + e2 = lookup[e2] + out = out .. char(e1 * 4 + rshift(e2, 4)) + elseif padding == 1 then + local e1, e2, e3 = byte(str, len - 3, len - 1) + e1 = lookup[e1] + e2 = lookup[e2] + e3 = lookup[e3] + out = out .. char( + e1 * 4 + rshift(e2, 4), + e2 % 16 * 16 + rshift(e3, 2) + ) + end + + return out +end + +return { encode = encode, decode = decode } diff --git a/projects/core/src/test/resources/test-rom/spec/modules/cc/base64_spec.lua b/projects/core/src/test/resources/test-rom/spec/modules/cc/base64_spec.lua new file mode 100644 index 000000000..f129813a9 --- /dev/null +++ b/projects/core/src/test/resources/test-rom/spec/modules/cc/base64_spec.lua @@ -0,0 +1,66 @@ +-- SPDX-FileCopyrightText: 2026 The CC: Tweaked Developers +-- +-- SPDX-License-Identifier: MPL-2.0 + +describe("cc.base64", function() + local base64 = require "cc.base64" + + it("random strings roundtrip", function() + for _ = 1, 1000 do + local len = math.random(1000) + local str = "" + for _ = 1, len do str = str .. string.char(math.random(0, 255)) end + + expect(base64.decode(base64.encode(str))):eq(str) + end + end) + + describe("encode", function() + it("validates arguments", function() + expect.error(base64.encode, 2):eq("bad argument #1 (string expected, got number)") + expect.error(base64.encode, "", 2):eq("bad argument #2 (string expected, got number)") + expect.error(base64.encode, "", ""):eq("alt_chars must be exactly two characters") + end) + + it("encodes as expected", function() + expect(base64.encode("")):eq("") + expect(base64.encode("light w")):eq("bGlnaHQgdw==") + expect(base64.encode("light wo")):eq("bGlnaHQgd28=") + expect(base64.encode("light wor")):eq("bGlnaHQgd29y") + expect(base64.encode("Many hands make light work.")):eq("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu") + end) + + it("encodes using alternative alphabet", function() + expect(base64.encode("Test: \255\230")):eq("VGVzdDog/+Y=") + expect(base64.encode("Test: \255\230", "-_")):eq("VGVzdDog_-Y=") + end) + end) + + describe("decode", function() + it("validates arguments", function() + expect.error(base64.decode, 2):eq("bad argument #1 (string expected, got number)") + expect.error(base64.decode, "", 2):eq("bad argument #2 (string expected, got number)") + expect.error(base64.decode, "", ""):eq("alt_chars must be exactly two characters") + end) + + it("decodes as expected", function() + expect(base64.decode("")):eq("") + expect(base64.decode("bGlnaHQgdw==")):eq("light w") + expect(base64.decode("bGlnaHQgd28=")):eq("light wo") + expect(base64.decode("bGlnaHQgd29y")):eq("light wor") + expect(base64.decode("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu")):eq("Many hands make light work.") + end) + + it("decodes using alternative alphabet", function() + expect(base64.decode("VGVzdDog/+Y=")):eq("Test: \255\230") + expect(base64.decode("VGVzdDog_-Y=", "-_")):eq("Test: \255\230") + end) + + it("validates the input string", function() + expect { base64.decode("VGVzdDog/+Y") }:same { nil, "input is not valid base64" } + expect { base64.decode("VGVzdDog/+Y==") }:same { nil, "input is not valid base64" } + expect { base64.decode("VGVzdDog/=Y=") }:same { nil, "input is not valid base64" } + expect { base64.decode("VGVzdDog/===") }:same { nil, "input is not valid base64" } + end) + end) +end) From 87916cf34221839e4d60d42eb7151eababadbdca Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 16 May 2026 14:37:21 +0100 Subject: [PATCH 11/13] Prohibit specifying interface in IPv6 addresses --- .../core/apis/http/NetworkUtils.java | 5 +++++ .../core/apis/http/NetworkUtilsTest.java | 21 +++++++++++++++++++ .../test-rom/spec/apis/http_spec.lua | 5 +++++ 3 files changed, 31 insertions(+) create mode 100644 projects/core/src/test/java/dan200/computercraft/core/apis/http/NetworkUtilsTest.java diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java index ed3137c82..ebd89288a 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java @@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; +import java.net.Inet6Address; import java.net.InetSocketAddress; import java.net.URI; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -123,6 +124,10 @@ public final class NetworkUtils { if (port < 0) port = ssl ? 443 : 80; var socketAddress = new InetSocketAddress(host, port); if (socketAddress.isUnresolved()) throw new HTTPRequestException("Unknown host"); + if (socketAddress.getAddress() instanceof Inet6Address inet6 && (inet6.getScopedInterface() != null || inet6.getScopeId() != 0)) { + throw new HTTPRequestException("Scoped address not permitted"); + } + return socketAddress; } diff --git a/projects/core/src/test/java/dan200/computercraft/core/apis/http/NetworkUtilsTest.java b/projects/core/src/test/java/dan200/computercraft/core/apis/http/NetworkUtilsTest.java new file mode 100644 index 000000000..68a93fa06 --- /dev/null +++ b/projects/core/src/test/java/dan200/computercraft/core/apis/http/NetworkUtilsTest.java @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2026 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.core.apis.http; + +import dan200.computercraft.test.core.ReplaceUnderscoresDisplayNameGenerator; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@DisplayNameGeneration(ReplaceUnderscoresDisplayNameGenerator.class) +class NetworkUtilsTest { + @Test + public void test_getAddress_with_scoped_address() { + var err = assertThrows(HTTPRequestException.class, () -> NetworkUtils.getAddress("[::1%1]", 80, false)); + assertEquals("Scoped address not permitted", err.getMessage()); + } +} diff --git a/projects/core/src/test/resources/test-rom/spec/apis/http_spec.lua b/projects/core/src/test/resources/test-rom/spec/apis/http_spec.lua index 39a1506ac..4ccc99c8b 100644 --- a/projects/core/src/test/resources/test-rom/spec/apis/http_spec.lua +++ b/projects/core/src/test/resources/test-rom/spec/apis/http_spec.lua @@ -22,6 +22,11 @@ describe("The http library", function() -- to ensure the general control flow works. expect({ http.checkURL("http://localhost") }):same({ false, "Domain not permitted" }) expect({ http.checkURL("http://127.0.0.1") }):same({ false, "Domain not permitted" }) + expect({ http.checkURL("http://[::1]") }):same({ false, "Domain not permitted" }) + end) + + expect("rejects scoped addresses", function() + expect({ http.checkURL("http://[::1%1]") }):same({ false, "Scoped address not permitted" }) end) end) From 585b6d4d39b08f11d1ff4037791f88a4576b1e99 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 16 May 2026 14:41:37 +0100 Subject: [PATCH 12/13] Block NAT64 addresses --- .../core/apis/http/options/AddressPredicate.java | 13 ++++++++++++- .../core/apis/http/options/AddressRuleTest.java | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java index 46bce96be..80de5e392 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java +++ b/projects/core/src/main/java/dan200/computercraft/core/apis/http/options/AddressPredicate.java @@ -72,6 +72,10 @@ interface AddressPredicate { )); } + return parse(address, prefixSize); + } + + public static HostRange parse(InetAddress address, int prefixSize) { // Mask the bytes of the IP address. byte[] minBytes = address.getAddress(), maxBytes = address.getAddress(); var size = prefixSize; @@ -126,6 +130,7 @@ interface AddressPredicate { || socketAddress.isMulticastAddress() // 224.0.0.0/4, ff00::/8 || isUniqueLocalAddress(socketAddress) // fd00::/8 || isCarrierGradeNatAddress(socketAddress) // 100.64.0.0/10 + || NAT64_RANGE.matches(socketAddress) // 64:ff9b::/96 || additionalAddresses.contains(socketAddress); } @@ -154,6 +159,12 @@ interface AddressPredicate { var bytes = address.getAddress(); return bytes[0] == 100 && ((bytes[1] & 0xFF) >= 64 && (bytes[1] & 0xFF) <= 127); } - } + /** + * The NAT64 address range (64:ff9b::/96). + * + * @see NAT64 on Wikipedia + */ + private static final HostRange NAT64_RANGE = HostRange.parse(InetAddresses.forString("64:ff9b::"), 96); + } } diff --git a/projects/core/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java b/projects/core/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java index ddf009c82..c23d6c60b 100644 --- a/projects/core/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java +++ b/projects/core/src/test/java/dan200/computercraft/core/apis/http/options/AddressRuleTest.java @@ -36,6 +36,8 @@ public class AddressRuleTest { "224.0.0.1", "ff02::1", // CGNAT "100.64.0.0", "100.127.255.255", + // NAT64 + "64:ff9b::c0a8:0101", // Cloud metadata providers "100.100.100.200", // Alibaba "192.0.0.192", // Oracle From 432214f70ac95b1274e6a7aba0f00a30facf0220 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 16 May 2026 14:57:17 +0100 Subject: [PATCH 13/13] Bump CC:T to 1.119.0 --- gradle.properties | 2 +- .../data/computercraft/lua/rom/help/changelog.md | 14 ++++++++++++++ .../data/computercraft/lua/rom/help/whatsnew.md | 16 ++++++++++------ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/gradle.properties b/gradle.properties index 2a10bbeaf..464050e95 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error # Mod properties isUnstable=false -modVersion=1.118.0 +modVersion=1.119.0 # Minecraft properties: We want to configure this here so we can read it in settings.gradle mcVersion=1.20.1 diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md b/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md index 332ade1c5..d5fc63bb6 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/help/changelog.md @@ -1,3 +1,17 @@ +# New features in CC: Tweaked 1.119.0 + +* Add `commands.getDimension()`. +* Add `cc.base64` module. +* Update Cobalt to 0.9.9, bringing in several Lua 5.5 changes: + * Floats are now printed with enough digits to round trip correctly. + * Add `table.create`. + * `utf8.offset` now returns the final position of the codepoint. + +Several bug fixes: +* Fix handling of integer indexes in `LuaTable`. +* Correct `min` and `sec` defaults in `os.time`. (sircfenner) +* Make HTTP IP filtering stricter. + # New features in CC: Tweaked 1.118.0 * Add timeout argument to `rednet.lookup`. diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md b/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md index 51ffbced1..2250571b3 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md @@ -1,11 +1,15 @@ -New features in CC: Tweaked 1.118.0 +New features in CC: Tweaked 1.119.0 -* Add timeout argument to `rednet.lookup`. -* Add `commands.getEntity`. +* Add `commands.getDimension()`. +* Add `cc.base64` module. +* Update Cobalt to 0.9.9, bringing in several Lua 5.5 changes: + * Floats are now printed with enough digits to round trip correctly. + * Add `table.create`. + * `utf8.offset` now returns the final position of the codepoint. Several bug fixes: -* Documentation fixes (ItsNotVingtdeux). -* Fix handling of shebangs outside the root directory (graypinkfurball). -* Fix `edit` not clearing menu after terminal resize (Wojbie). +* Fix handling of integer indexes in `LuaTable`. +* Correct `min` and `sec` defaults in `os.time`. (sircfenner) +* Make HTTP IP filtering stricter. Type "help changelog" to see the full version history.