diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index 5d1cf8a92..18ab88e49 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -58,13 +58,13 @@ jobs: find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \; - name: 📤 Upload Jar - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: CC-Tweaked path: ./jars - name: 📤 Upload coverage - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 build-core: strategy: @@ -81,24 +81,28 @@ jobs: runs-on: ${{ matrix.uses }} steps: - - name: Clone repository + - name: 📥 Clone repository uses: actions/checkout@v4 - - name: Set up Java + - name: 📥 Set up Java uses: actions/setup-java@v4 with: java-version: 17 distribution: 'temurin' - - name: Setup Gradle + - name: 📥 Setup Gradle uses: gradle/actions/setup-gradle@v3 with: cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }} - - name: Run tests + - name: ⚒️ Build + run: | + ./gradlew --configure-on-demand :core:assemble :web:assemble + + - name: 🧪 Run tests run: | ./gradlew --configure-on-demand :core:test - - name: Parse test reports + - name: 🧪 Parse test reports run: python3 ./tools/parse-reports.py if: ${{ failure() }} diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 2940034a6..000000000 --- a/.gitpod.yml +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -# -# SPDX-License-Identifier: MPL-2.0 - -image: - file: config/gitpod/Dockerfile - -ports: - - port: 25565 - onOpen: notify - -vscode: - extensions: - - eamodio.gitlens - - github.vscode-pull-request-github - - ms-azuretools.vscode-docker - - redhat.java - - richardwillis.vscode-gradle - - vscjava.vscode-java-debug - - vscode.github - -tasks: - - name: Setup pre-commit hool - init: pre-commit install --allow-missing-config - - name: Install npm packages - init: npm ci diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d2aa3128..5fc93c314 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,9 +29,9 @@ automatically with GitHub, so please don't submit PRs adding/changing translatio In order to develop CC: Tweaked, you'll need to download the source code and then run it. - Make sure you've got the following software installed: - - Java Development Kit (JDK) installed. This can be downloaded from [Adoptium]. + - Java Development Kit (JDK). This can be downloaded from [Adoptium]. - [Git](https://git-scm.com/). - - If you want to work on documentation, [NodeJS][node]. + - [NodeJS][node]. - Download CC: Tweaked's source code: ``` diff --git a/build.gradle.kts b/build.gradle.kts index 2baba69d6..d56e6968f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,9 +5,8 @@ import cc.tweaked.gradle.JUnitExt import net.fabricmc.loom.api.LoomGradleExtensionAPI import net.fabricmc.loom.util.gradle.SourceSetHelper -import org.jetbrains.gradle.ext.compiler -import org.jetbrains.gradle.ext.runConfigurations -import org.jetbrains.gradle.ext.settings +import org.jetbrains.gradle.ext.* +import org.jetbrains.gradle.ext.Application plugins { publishing @@ -86,6 +85,19 @@ idea.project.settings.runConfigurations { moduleName = "${idea.project.name}.forge.test" packageName = "" } + + register("Standalone") { + moduleName = "${idea.project.name}.standalone.main" + mainClass = "cc.tweaked.standalone.Main" + programParameters = "--resources=projects/core/src/main/resources --term=80x30 --allow-local-domains" + } +} + +// Build with the IntelliJ, rather than through Gradle. This may require setting the "Compiler Output" option in +// "Project Structure". +idea.project.settings.delegateActions { + delegateBuildRunToGradle = false + testRunner = ActionDelegationConfig.TestRunner.PLATFORM } idea.project.settings.compiler.javac { diff --git a/buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt b/buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt index ddbe11dfa..a1da1e862 100644 --- a/buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt +++ b/buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt @@ -46,7 +46,7 @@ abstract class NpmInstall : DefaultTask() { @TaskAction fun install() { project.exec { - commandLine("npm", "ci") + commandLine(ProcessHelpers.getExecutable("npm"), "ci") workingDir = projectRoot.get().asFile } } @@ -59,6 +59,6 @@ abstract class NpmInstall : DefaultTask() { abstract class NpxExecToDir : ExecToDir() { init { dependsOn(NpmInstall.TASK_NAME) - executable = "npx" + executable = ProcessHelpers.getExecutable("npx") } } diff --git a/buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt b/buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt index 4f3fb2463..c16b55441 100644 --- a/buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt +++ b/buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt @@ -9,6 +9,7 @@ import org.gradle.api.GradleException import java.io.BufferedReader import java.io.File import java.io.InputStreamReader +import java.nio.charset.StandardCharsets internal object ProcessHelpers { fun startProcess(vararg command: String): Process { @@ -34,7 +35,7 @@ internal object ProcessHelpers { val process = startProcess(*command) process.outputStream.close() - val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader -> + val out = BufferedReader(InputStreamReader(process.inputStream, StandardCharsets.UTF_8)).use { reader -> reader.lines().filter { it.isNotEmpty() }.toList() } ProcessGroovyMethods.closeStreams(process) @@ -46,6 +47,28 @@ internal object ProcessHelpers { val path = System.getenv("PATH") ?: return false return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() } } + + /** + * Search for an executable on the `PATH` if required. + * + * [Process]/[ProcessBuilder] does not handle all executable file extensions on Windows (such as `.com). When on + * Windows, this function searches `PATH` and `PATHEXT` for an executable matching [name]. + */ + fun getExecutable(name: String): String { + if (!System.getProperty("os.name").lowercase().contains("windows")) return name + + val path = (System.getenv("PATH") ?: return name).split(File.pathSeparator) + val pathExt = (System.getenv("PATHEXT") ?: return name).split(File.pathSeparator) + + for (pathEntry in path) { + for (ext in pathExt) { + val resolved = File(pathEntry, name + ext) + if (resolved.exists()) return resolved.getAbsolutePath() + } + } + + return name + } } internal fun Process.waitForOrThrow(message: String) { diff --git a/config/gitpod/Dockerfile b/config/gitpod/Dockerfile deleted file mode 100644 index 78e922ea4..000000000 --- a/config/gitpod/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -# -# SPDX-License-Identifier: MPL-2.0 - -FROM gitpod/workspace-base - -USER gitpod - -RUN sudo apt-get -q update \ - && sudo apt-get install -yq openjdk-16-jdk python3-pip npm \ - && sudo pip3 install pre-commit \ - && sudo update-java-alternatives --set java-1.16.0-openjdk-amd64 diff --git a/doc/guides/speaker_audio.md b/doc/guides/speaker_audio.md index 2a09c06e7..d29cfc32f 100644 --- a/doc/guides/speaker_audio.md +++ b/doc/guides/speaker_audio.md @@ -131,7 +131,7 @@ different. First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker. -As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each +As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPWM uses a single bit for each sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use [`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and [`fs.ReadHandle.read`] if you prefer. diff --git a/gradle.properties b/gradle.properties index c336d9522..59600d400 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: MPL-2.0 -org.gradle.jvmargs=-Xmx3G +org.gradle.jvmargs=-Xmx3G -Dfile.encoding=UTF-8 org.gradle.parallel=true kotlin.stdlib.default.dependency=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d83b9c767..7642546ed 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ slf4j = "2.0.7" asm = "9.6" autoService = "1.1.1" checkerFramework = "3.42.0" -cobalt = "0.9.2" +cobalt = "0.9.3" commonsCli = "1.6.0" jetbrainsAnnotations = "24.1.0" jsr305 = "3.0.2" @@ -59,7 +59,7 @@ checkstyle = "10.14.1" curseForgeGradle = "1.1.18" errorProne-core = "2.23.0" errorProne-plugin = "3.1.0" -fabric-loom = "1.5.7" +fabric-loom = "1.6.7" githubRelease = "2.5.2" gradleVersions = "0.50.0" ideaExt = "1.1.7" @@ -70,8 +70,8 @@ neoGradle = "7.0.100" nullAway = "0.10.25" spotless = "6.23.3" taskTree = "2.1.1" -teavm = "0.10.0-SQUID.3" -vanillaExtract = "0.1.2" +teavm = "0.10.0-SQUID.4" +vanillaExtract = "0.1.3" versionCatalogUpdate = "0.8.1" [libraries] @@ -152,6 +152,7 @@ neoGradle-userdev = { module = "net.neoforged.gradle:userdev", version.ref = "ne nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" } spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" } +teavm-core = { module = "org.teavm:teavm-core", version.ref = "teavm" } teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" } teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" } teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917..e6441136f 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22ce5..b82aa23a4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java index 9071ebe39..4c1beb0e3 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/blocks/AbstractComputerBlock.java @@ -8,6 +8,7 @@ import dan200.computercraft.annotations.ForgeOverride; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.shared.common.IBundledRedstoneBlock; import dan200.computercraft.shared.computer.items.IComputerItem; +import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.platform.RegistryEntry; import dan200.computercraft.shared.util.BlockEntityHelpers; import net.minecraft.core.BlockPos; @@ -160,8 +161,19 @@ public abstract class AbstractComputerBlock - ? ((AbstractComputerBlock) getBlockState().getBlock()).getItem(this) - : ItemStack.EMPTY; - new ComputerContainerData(computer, stack).open(player, this); - } - return InteractionResult.sidedSuccess(getLevel().isClientSide); - } - return InteractionResult.PASS; - } - protected void serverTick() { if (getLevel().isClientSide) return; if (computerID < 0 && !startOn) return; // Don't tick if we don't need a computer! @@ -343,7 +313,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements if (getLevel().isClientSide || computerID == id) return; computerID = id; - setChanged(); + BlockEntityHelpers.updateBlock(this); } @Override @@ -353,7 +323,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements this.label = label; var computer = getServerComputer(); if (computer != null) computer.setLabel(label); - setChanged(); + BlockEntityHelpers.updateBlock(this); } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java index b187e41dd..93836f8f5 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlock.java @@ -21,10 +21,13 @@ import dan200.computercraft.shared.util.WaterloggableHelpers; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.Containers; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.AbstractHurtingProjectile; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Explosion; @@ -41,6 +44,7 @@ import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; @@ -168,6 +172,22 @@ public class TurtleBlock extends AbstractComputerBlock implem } } + @Override + @Deprecated + public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + var currentItem = player.getItemInHand(hand); + if (currentItem.getItem() == Items.NAME_TAG && currentItem.hasCustomHoverName() && level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer) { + // Label to rename computer + if (!level.isClientSide) { + computer.setLabel(currentItem.getHoverName().getString()); + currentItem.shrink(1); + } + return InteractionResult.sidedSuccess(level.isClientSide); + } + + return super.use(state, level, pos, player, hand, hit); + } + @ForgeOverride public float getExplosionResistance(BlockState state, BlockGetter world, BlockPos pos, Explosion explosion) { var exploder = explosion.getDirectSourceEntity(); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java index 92a1e2785..c8527ddf4 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java @@ -87,11 +87,6 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba if (!hasMoved()) super.unload(); } - @Override - protected boolean canNameWithTag(Player player) { - return true; - } - @Override protected double getInteractRange() { return 12.0; diff --git a/projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json b/projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json index 6d0c4899b..8051fa6e4 100644 --- a/projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json +++ b/projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json @@ -136,7 +136,7 @@ "gui.computercraft.config.term_sizes": "Velikosti terminálu", "gui.computercraft.config.term_sizes.computer": "Počítač", "gui.computercraft.config.term_sizes.computer.height": "Výška terminálu", - "gui.computercraft.config.term_sizes.computer.height.tooltip": "Rozsash: 1 ~ 255", + "gui.computercraft.config.term_sizes.computer.height.tooltip": "Rozsah: 1 ~ 255", "gui.computercraft.config.term_sizes.computer.tooltip": "Velikost počítačového terminálu.", "gui.computercraft.config.term_sizes.computer.width": "Šířka terminálu", "gui.computercraft.config.term_sizes.computer.width.tooltip": "Rozsah: 1 ~ 255", @@ -177,7 +177,7 @@ "gui.computercraft.tooltip.turn_off.key": "Zmáčkni Ctrl+S", "gui.computercraft.tooltip.turn_on": "Zapnout teno počítač", "gui.computercraft.upload.failed": "Nahrávání se nezdařilo", - "gui.computercraft.upload.failed.computer_off": "Před nahráním souborů musíš vypnout počítač.", + "gui.computercraft.upload.failed.computer_off": "Před nahráním souborů musíš zapnout počítač.", "gui.computercraft.upload.failed.corrupted": "Soubory byly porušeny při nahrávání. Prosím zkus to znovu.", "gui.computercraft.upload.failed.generic": "Nahrávání souborů se nezdařilo (%s)", "gui.computercraft.upload.failed.name_too_long": "Jména souborů pro nahrání jsou příliš dlouhá.", @@ -216,5 +216,11 @@ "upgrade.minecraft.diamond_hoe.adjective": "Farmářský", "upgrade.minecraft.diamond_pickaxe.adjective": "Těžební", "upgrade.minecraft.diamond_shovel.adjective": "Kopací", - "upgrade.minecraft.diamond_sword.adjective": "Bojový" + "upgrade.minecraft.diamond_sword.adjective": "Bojový", + "tag.item.computercraft.computer": "Počítače", + "tag.item.computercraft.monitor": "Monitory", + "tag.item.computercraft.turtle": "Roboti", + "tag.item.computercraft.wired_modem": "Drátové modemy", + "gui.computercraft.config.http.proxy": "Proxy", + "gui.computercraft.config.upload_max_size.tooltip": "Limit velikosti nahrávaného souboru v bytech. Musí být v rozmezí od 1 KiB do 16 MiB.\nMěj na paměti, že nahrávání souborů je zpracováváno v jednom ticku - velké soubory nebo\nšpatný výkon sítě mohou zpomalit síťové vlákno. A nezapomeň na místo na disku!\nRozsah: 1024 ~ 16777216" } diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt index 517580964..a4ebf599e 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Computer_Test.kt @@ -17,7 +17,6 @@ import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.gametest.framework.GameTest import net.minecraft.gametest.framework.GameTestHelper -import net.minecraft.world.InteractionHand import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items import net.minecraft.world.level.block.Blocks @@ -140,8 +139,7 @@ class Computer_Test { // Teleport the player to the computer and then open it. thenExecute { context.positionAt(BlockPos(2, 2, 1)) - val computer = context.getBlockEntity(BlockPos(2, 2, 2), ModRegistry.BlockEntities.COMPUTER_ADVANCED.get()) - computer.use(context.level.randomPlayer!!, InteractionHand.MAIN_HAND) + context.useBlock(BlockPos(2, 2, 2), context.level.randomPlayer!!) } // Assert the terminal is synced to the client. thenIdle(2) diff --git a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/settings.lua b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/settings.lua index 91af31c11..0d6ead5a2 100644 --- a/projects/core/src/main/resources/data/computercraft/lua/rom/apis/settings.lua +++ b/projects/core/src/main/resources/data/computercraft/lua/rom/apis/settings.lua @@ -183,7 +183,7 @@ function unset(name) end --- Resets the value of all settings. Equivalent to calling [`settings.unset`] ---- on every setting. +-- on every setting. -- -- @see settings.unset function clear() @@ -213,16 +213,16 @@ end -- Existing settings will be merged with any pre-existing ones. Conflicting -- entries will be overwritten, but any others will be preserved. -- --- @tparam[opt] string sPath The file to load from, defaulting to `.settings`. +-- @tparam[opt=".settings"] string path The file to load from. -- @treturn boolean Whether settings were successfully read from this -- file. Reasons for failure may include the file not existing or being -- corrupted. -- -- @see settings.save --- @changed 1.87.0 `sPath` is now optional. -function load(sPath) - expect(1, sPath, "string", "nil") - local file = fs.open(sPath or ".settings", "r") +-- @changed 1.87.0 `path` is now optional. +function load(path) + expect(1, path, "string", "nil") + local file = fs.open(path or ".settings", "r") if not file then return false end @@ -255,14 +255,14 @@ end -- This will entirely overwrite the pre-existing file. Settings defined in the -- file, but not currently loaded will be removed. -- --- @tparam[opt] string sPath The path to save settings to, defaulting to `.settings`. +-- @tparam[opt=".settings"] string path The path to save settings to. -- @treturn boolean If the settings were successfully saved. -- -- @see settings.load --- @changed 1.87.0 `sPath` is now optional. -function save(sPath) - expect(1, sPath, "string", "nil") - local file = fs.open(sPath or ".settings", "w") +-- @changed 1.87.0 `path` is now optional. +function save(path) + expect(1, path, "string", "nil") + local file = fs.open(path or ".settings", "w") if not file then return false end diff --git a/projects/web/build.gradle.kts b/projects/web/build.gradle.kts index 25f0838b6..19d83ceb4 100644 --- a/projects/web/build.gradle.kts +++ b/projects/web/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { implementation(libs.guava) implementation(libs.netty.http) implementation(libs.slf4j) + runtimeOnly(libs.teavm.core) // Contains the TeaVM runtime "builderCompileOnly"(libs.bundles.annotations) "builderImplementation"(libs.bundles.teavm.tooling) diff --git a/projects/web/src/builder/java/cc/tweaked/web/builder/TransformingClassLoader.java b/projects/web/src/builder/java/cc/tweaked/web/builder/TransformingClassLoader.java index fe2a0a00f..23c50616c 100644 --- a/projects/web/src/builder/java/cc/tweaked/web/builder/TransformingClassLoader.java +++ b/projects/web/src/builder/java/cc/tweaked/web/builder/TransformingClassLoader.java @@ -68,6 +68,10 @@ public class TransformingClassLoader extends ClassLoader { } private @Nullable Path findUnmappedFile(String name) { + // For some odd reason, we try to resolve the generated lambda classes. This includes classes called + // things like lambda, which is an invalid path on Windows. Detect those, and abort early. + if (name.indexOf("<") >= 0) return null; + return classpath.stream().map(x -> x.resolve(name)).filter(Files::exists).findFirst().orElse(null); } @@ -95,13 +99,18 @@ public class TransformingClassLoader extends ClassLoader { @Override public @Nullable InputStream getResourceAsStream(String name) { - if (!name.endsWith(".class")) return super.getResourceAsStream(name); + if (!name.endsWith(".class") || name.startsWith("java/") || name.startsWith("javax/")) { + return super.getResourceAsStream(name); + } var lastFile = this.lastFile; if (lastFile != null && lastFile.name().equals(name)) return new ByteArrayInputStream(lastFile.contents()); var path = findFile(name); - if (path == null) return null; + if (path == null) { + System.out.printf("Cannot find %s. Falling back to system class loader.\n", name); + return super.getResourceAsStream(name); + } ClassReader reader; try (var stream = Files.newInputStream(path)) { diff --git a/projects/web/src/frontend/emu/computer.ts b/projects/web/src/frontend/emu/computer.ts index 2e93fa362..dc14b0e91 100644 --- a/projects/web/src/frontend/emu/computer.ts +++ b/projects/web/src/frontend/emu/computer.ts @@ -72,6 +72,10 @@ class EmulatedComputer implements ComputerDisplay, ComputerActionable { for (const callback of this.callbacks) callback(computer); }) .catch(e => { + console.error(e); + + if (this.terminal.sizeX === 0 || this.terminal.sizeY === 0) this.terminal.resize(51, 19); + const width = this.terminal.sizeX; const fg = "0".repeat(width); const bg = "e".repeat(width); @@ -83,6 +87,8 @@ class EmulatedComputer implements ComputerDisplay, ComputerActionable { this.terminal.fore[y] = fg; this.terminal.back[y] = bg; } + + this.flushTerminal(); }); }