mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-11-04 07:32:59 +00:00 
			
		
		
		
	Compare commits
	
		
			21 Commits
		
	
	
		
			v1.21.1-1.
			...
			v1.21.1-1.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					5f87984bac | ||
| 
						 | 
					18f3426f1d | ||
| 
						 | 
					0ec46fe38e | ||
| 
						 | 
					569de7fafb | ||
| 
						 | 
					157ce4fa55 | ||
| 
						 | 
					b9ed66983d | ||
| 
						 | 
					d0fbec6c6b | ||
| 
						 | 
					a683697e8c | ||
| 
						 | 
					9e233a916f | ||
| 
						 | 
					5a9e21ccc3 | ||
| 
						 | 
					5f16909d4b | ||
| 
						 | 
					00475b9bb0 | ||
| 
						 | 
					9cf0f85fcb | ||
| 
						 | 
					018ce7c8a5 | ||
| 
						 | 
					44726827b4 | ||
| 
						 | 
					2bf0aba455 | ||
| 
						 | 
					3cf914cb4c | ||
| 
						 | 
					180156ff1c | ||
| 
						 | 
					c6ba753568 | ||
| 
						 | 
					9f45c91925 | ||
| 
						 | 
					5fa724ed24 | 
							
								
								
									
										3
									
								
								.github/ISSUE_TEMPLATE/bug_report.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/ISSUE_TEMPLATE/bug_report.yaml
									
									
									
									
										vendored
									
									
								
							@@ -11,7 +11,8 @@ body:
 | 
			
		||||
        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.x
 | 
			
		||||
      - 1.21.1
 | 
			
		||||
      - 1.21.7
 | 
			
		||||
  validations:
 | 
			
		||||
    required: true
 | 
			
		||||
- type: input
 | 
			
		||||
 
 | 
			
		||||
@@ -101,7 +101,7 @@ about how you can build on that until you've covered everything!
 | 
			
		||||
 | 
			
		||||
[new-issue]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose "Create a new issue"
 | 
			
		||||
[community]: README.md#community "Get in touch with the community."
 | 
			
		||||
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
 | 
			
		||||
[Adoptium]: https://adoptium.net/temurin/releases?version=21 "Download OpenJDK 21"
 | 
			
		||||
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
 | 
			
		||||
[docs]: https://tweaked.cc/ "CC: Tweaked documentation"
 | 
			
		||||
[ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator."
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,11 @@
 | 
			
		||||
 | 
			
		||||
import cc.tweaked.gradle.JUnitExt
 | 
			
		||||
import net.fabricmc.loom.api.LoomGradleExtensionAPI
 | 
			
		||||
import net.fabricmc.loom.util.gradle.SourceSetHelper
 | 
			
		||||
import org.jetbrains.gradle.ext.*
 | 
			
		||||
import org.jetbrains.gradle.ext.Application
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    publishing
 | 
			
		||||
    alias(libs.plugins.taskTree)
 | 
			
		||||
    alias(libs.plugins.githubRelease)
 | 
			
		||||
    alias(libs.plugins.gradleVersions)
 | 
			
		||||
    alias(libs.plugins.versionCatalogUpdate)
 | 
			
		||||
@@ -70,7 +68,7 @@ idea.project.settings.runConfigurations {
 | 
			
		||||
        val fabricProject = evaluationDependsOn(":fabric")
 | 
			
		||||
        val classPathGroup = fabricProject.extensions.getByType<LoomGradleExtensionAPI>().mods
 | 
			
		||||
            .joinToString(File.pathSeparator + File.pathSeparator) { modSettings ->
 | 
			
		||||
                SourceSetHelper.getClasspath(modSettings, project).joinToString(File.pathSeparator) { it.absolutePath }
 | 
			
		||||
                modSettings.modFiles.joinToString(File.pathSeparator) { it.absolutePath }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        vmParameters = "-ea -Dfabric.classPathGroups=$classPathGroup"
 | 
			
		||||
@@ -115,8 +113,12 @@ idea.project.settings.compiler.javac {
 | 
			
		||||
        .toMap()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
repositories() {
 | 
			
		||||
    mavenCentral()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
versionCatalogUpdate {
 | 
			
		||||
    sortByKey = false
 | 
			
		||||
    pin { versions.addAll("fastutil", "guava", "netty", "slf4j") }
 | 
			
		||||
    keep { keepUnusedLibraries = true }
 | 
			
		||||
    keep { keepUnusedVersions = true }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ repositories {
 | 
			
		||||
        name = "Fabric"
 | 
			
		||||
        content {
 | 
			
		||||
            includeGroup("net.fabricmc")
 | 
			
		||||
            includeGroup("net.fabricmc.unpick")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -69,6 +70,6 @@ gradlePlugin {
 | 
			
		||||
 | 
			
		||||
versionCatalogUpdate {
 | 
			
		||||
    sortByKey = false
 | 
			
		||||
    keep { keepUnusedLibraries = true }
 | 
			
		||||
    keep { keepUnusedVersions = true }
 | 
			
		||||
    catalogFile = file("../gradle/libs.versions.toml")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,7 @@
 | 
			
		||||
 | 
			
		||||
/** Default configuration for Fabric projects. */
 | 
			
		||||
 | 
			
		||||
import cc.tweaked.gradle.CCTweakedExtension
 | 
			
		||||
import cc.tweaked.gradle.CCTweakedPlugin
 | 
			
		||||
import cc.tweaked.gradle.IdeaRunConfigurations
 | 
			
		||||
import cc.tweaked.gradle.MinecraftConfigurations
 | 
			
		||||
import cc.tweaked.gradle.*
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    `java-library`
 | 
			
		||||
@@ -67,3 +64,19 @@ dependencies {
 | 
			
		||||
tasks.ideaSyncTask {
 | 
			
		||||
    doLast { IdeaRunConfigurations(project).patch() }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tasks.named("checkDependencyConsistency", DependencyCheck::class.java) {
 | 
			
		||||
    val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
 | 
			
		||||
    // Minecraft depends on lwjgl, but Fabric forces it to a more recent version
 | 
			
		||||
    for (lwjgl in listOf(
 | 
			
		||||
        "lwjgl",
 | 
			
		||||
        "lwjgl-glfw",
 | 
			
		||||
        "lwjgl-jemalloc",
 | 
			
		||||
        "lwjgl-openal",
 | 
			
		||||
        "lwjgl-opengl",
 | 
			
		||||
        "lwjgl-stb",
 | 
			
		||||
        "lwjgl-tinyfd",
 | 
			
		||||
    )) {
 | 
			
		||||
        override("org.lwjgl", lwjgl, "3.3.2")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,8 @@ plugins {
 | 
			
		||||
    checkstyle
 | 
			
		||||
    id("com.diffplug.spotless")
 | 
			
		||||
    id("net.ltgt.errorprone")
 | 
			
		||||
    // Required for cross-project dependencies in Fabric
 | 
			
		||||
    id("net.fabricmc.fabric-loom-companion")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val modVersion: String by extra
 | 
			
		||||
@@ -28,9 +30,9 @@ version = modVersion
 | 
			
		||||
base.archivesName.convention("cc-tweaked-$mcVersion-${project.name}")
 | 
			
		||||
 | 
			
		||||
java {
 | 
			
		||||
    toolchain {
 | 
			
		||||
        languageVersion = CCTweakedPlugin.JAVA_VERSION
 | 
			
		||||
    }
 | 
			
		||||
    toolchain { languageVersion = CCTweakedPlugin.JDK_VERSION }
 | 
			
		||||
    sourceCompatibility = CCTweakedPlugin.JAVA_VERSION
 | 
			
		||||
    targetCompatibility = CCTweakedPlugin.JAVA_VERSION
 | 
			
		||||
 | 
			
		||||
    withSourcesJar()
 | 
			
		||||
}
 | 
			
		||||
@@ -78,6 +80,8 @@ dependencies {
 | 
			
		||||
// Configure default JavaCompile tasks with our arguments.
 | 
			
		||||
sourceSets.all {
 | 
			
		||||
    tasks.named(compileJavaTaskName, JavaCompile::class.java) {
 | 
			
		||||
        // Explicitly set release, as that limits the APIs we can use to the right version of Java.
 | 
			
		||||
        options.release = CCTweakedPlugin.JAVA_TARGET.asInt()
 | 
			
		||||
 | 
			
		||||
        options.compilerArgs.addAll(
 | 
			
		||||
            listOf(
 | 
			
		||||
@@ -100,6 +104,7 @@ sourceSets.all {
 | 
			
		||||
            check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
 | 
			
		||||
            check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
 | 
			
		||||
            check("InvalidInlineTag", CheckSeverity.OFF) // Triggered by @snippet. Can be removed on Java 21.
 | 
			
		||||
            option("UnusedMethod:ExemptingMethodAnnotations", "dan200.computercraft.api.lua.LuaFunction")
 | 
			
		||||
 | 
			
		||||
            check("NullAway", CheckSeverity.ERROR)
 | 
			
		||||
            option(
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
 * See notes in [cc.tweaked.gradle.MinecraftConfigurations] for the general design behind these cursed ideas.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import cc.tweaked.gradle.CCTweakedPlugin
 | 
			
		||||
import cc.tweaked.gradle.MinecraftConfigurations
 | 
			
		||||
import cc.tweaked.gradle.clientClasses
 | 
			
		||||
import cc.tweaked.gradle.commonClasses
 | 
			
		||||
@@ -52,3 +53,5 @@ dependencies {
 | 
			
		||||
 | 
			
		||||
    testImplementation(testFixtures(project))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
kotlin.compilerOptions.jvmTarget = CCTweakedPlugin.KOTLIN_TARGET
 | 
			
		||||
 
 | 
			
		||||
@@ -223,7 +223,7 @@ abstract class CCTweakedExtension(private val project: Project) {
 | 
			
		||||
        ).resolve().single()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun <T> gitProvider(default: T, command: List<String>, process: (String) -> T): Provider<T> {
 | 
			
		||||
    private fun <T: Any> gitProvider(default: T, command: List<String>, process: (String) -> T): Provider<T> {
 | 
			
		||||
        val baseResult = project.providers.exec {
 | 
			
		||||
            commandLine = listOf("git", "-C", project.rootDir.absolutePath) + command
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
 | 
			
		||||
package cc.tweaked.gradle
 | 
			
		||||
 | 
			
		||||
import org.gradle.api.JavaVersion
 | 
			
		||||
import org.gradle.api.Plugin
 | 
			
		||||
import org.gradle.api.Project
 | 
			
		||||
import org.gradle.api.plugins.JavaPlugin
 | 
			
		||||
@@ -13,6 +14,7 @@ import org.gradle.plugins.ide.idea.model.IdeaModel
 | 
			
		||||
import org.jetbrains.gradle.ext.IdeaExtPlugin
 | 
			
		||||
import org.jetbrains.gradle.ext.runConfigurations
 | 
			
		||||
import org.jetbrains.gradle.ext.settings
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Configures projects to match a shared configuration.
 | 
			
		||||
@@ -42,6 +44,17 @@ class CCTweakedPlugin : Plugin<Project> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        val JAVA_VERSION = JavaLanguageVersion.of(21)
 | 
			
		||||
        /**
 | 
			
		||||
         * The version we run with. We use Java 21 here, as our Gradle build requires that.
 | 
			
		||||
         */
 | 
			
		||||
        val JDK_VERSION = JavaLanguageVersion.of(21)
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * The Java version we target. Should be the same as what Minecraft uses.
 | 
			
		||||
         */
 | 
			
		||||
        val JAVA_TARGET = JavaLanguageVersion.of(21)
 | 
			
		||||
 | 
			
		||||
        val JAVA_VERSION = JavaVersion.toVersion(JAVA_TARGET.asInt())
 | 
			
		||||
        val KOTLIN_TARGET = JvmTarget.fromTarget(JAVA_TARGET.toString())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,10 @@ abstract class DependencyCheck : DefaultTask() {
 | 
			
		||||
        overrides.putAll(project.provider { mutableMapOf(module.get().module.toString() to version) })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun override(group: String, module: String, version: String) {
 | 
			
		||||
        overrides.put("$group:$module", version)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add a configuration to check.
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -140,7 +140,7 @@ class CloseScope : AutoCloseable {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Proxy method to avoid overload ambiguity. */
 | 
			
		||||
fun <T> Property<T>.setProvider(provider: Provider<out T>) = set(provider)
 | 
			
		||||
fun <T: Any> Property<T >.setProvider(provider: Provider<out T>) = set(provider)
 | 
			
		||||
 | 
			
		||||
/** Short-cut method to get the absolute path of a [FileSystemLocation] provider. */
 | 
			
		||||
fun Provider<out FileSystemLocation>.getAbsolutePath(): String = get().asFile.absolutePath
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,6 @@ class MinecraftConfigurations private constructor(private val project: Project)
 | 
			
		||||
 | 
			
		||||
        // Set up an API configuration for clients (to ensure it's consistent with the main source set).
 | 
			
		||||
        val clientApi = configurations.maybeCreate(client.apiConfigurationName).apply {
 | 
			
		||||
            isVisible = false
 | 
			
		||||
            isCanBeConsumed = false
 | 
			
		||||
            isCanBeResolved = false
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ files:
 | 
			
		||||
        de: de_de    # German
 | 
			
		||||
        es-ES: es_es # Spanish
 | 
			
		||||
        fr: fr_fr    # French
 | 
			
		||||
        hu: hu_hu    # Hungarian
 | 
			
		||||
        it: it_it    # Italian
 | 
			
		||||
        ja: ja_jp    # Japanese
 | 
			
		||||
        ko: ko_kr    # Korean
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ neogradle.subsystems.conventions.runs.enabled=false
 | 
			
		||||
 | 
			
		||||
# Mod properties
 | 
			
		||||
isUnstable=true
 | 
			
		||||
modVersion=1.116.0
 | 
			
		||||
modVersion=1.116.2
 | 
			
		||||
 | 
			
		||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
 | 
			
		||||
mcVersion=1.21.1
 | 
			
		||||
 
 | 
			
		||||
@@ -23,17 +23,17 @@ netty = "4.1.97.Final"
 | 
			
		||||
slf4j = "2.0.9"
 | 
			
		||||
 | 
			
		||||
# Core dependencies (independent of Minecraft)
 | 
			
		||||
asm = "9.6"
 | 
			
		||||
asm = "9.9"
 | 
			
		||||
autoService = "1.1.1"
 | 
			
		||||
checkerFramework = "3.42.0"
 | 
			
		||||
cobalt = { strictly = "0.9.6" }
 | 
			
		||||
commonsCli = "1.6.0"
 | 
			
		||||
jetbrainsAnnotations = "24.1.0"
 | 
			
		||||
checkerFramework = "3.51.1"
 | 
			
		||||
cobalt = { strictly = "0.9.7" }
 | 
			
		||||
commonsCli = "1.10.0"
 | 
			
		||||
jetbrainsAnnotations = "26.0.2-1"
 | 
			
		||||
jspecify = "1.0.0"
 | 
			
		||||
jzlib = "1.1.3"
 | 
			
		||||
kotlin = "2.1.10"
 | 
			
		||||
kotlin-coroutines = "1.10.1"
 | 
			
		||||
nightConfig = "3.8.1"
 | 
			
		||||
kotlin = "2.2.21"
 | 
			
		||||
kotlin-coroutines = "1.10.2"
 | 
			
		||||
nightConfig = "3.8.3"
 | 
			
		||||
 | 
			
		||||
# Minecraft mods
 | 
			
		||||
emi = "1.1.7+1.21"
 | 
			
		||||
@@ -48,35 +48,34 @@ sodium-fabric = "mc1.21-0.6.0-beta.1-fabric"
 | 
			
		||||
sodium-forge = "mc1.21-0.6.0-beta.1-neoforge"
 | 
			
		||||
mixinExtra = "0.3.5"
 | 
			
		||||
create-forge = "6.0.0-6"
 | 
			
		||||
create-fabric = "0.5.1-f-build.1467+mc1.20.1"
 | 
			
		||||
create-fabric = "6.0.7.0+mc1.20.1-build.1716"
 | 
			
		||||
 | 
			
		||||
# Testing
 | 
			
		||||
hamcrest = "2.2"
 | 
			
		||||
jqwik = "1.8.2"
 | 
			
		||||
junit = "5.11.4"
 | 
			
		||||
junitPlatform = "1.11.4"
 | 
			
		||||
hamcrest = "3.0"
 | 
			
		||||
jqwik = "1.9.3"
 | 
			
		||||
junit = "6.0.1"
 | 
			
		||||
junitPlatform = "6.0.1"
 | 
			
		||||
jmh = "1.37"
 | 
			
		||||
 | 
			
		||||
# Build tools
 | 
			
		||||
cctJavadoc = "1.8.4"
 | 
			
		||||
checkstyle = "10.23.1"
 | 
			
		||||
errorProne-core = "2.38.0"
 | 
			
		||||
errorProne-plugin = "4.1.0"
 | 
			
		||||
fabric-loom = "1.10.4"
 | 
			
		||||
checkstyle = "12.1.1"
 | 
			
		||||
errorProne-core = "2.43.0"
 | 
			
		||||
errorProne-plugin = "4.3.0"
 | 
			
		||||
fabric-loom = "1.12.3"
 | 
			
		||||
githubRelease = "2.5.2"
 | 
			
		||||
gradleVersions = "0.50.0"
 | 
			
		||||
ideaExt = "1.1.7"
 | 
			
		||||
gradleVersions = "0.53.0"
 | 
			
		||||
ideaExt = "1.3"
 | 
			
		||||
illuaminate = "0.1.0-83-g1131f68"
 | 
			
		||||
lwjgl = "3.3.3"
 | 
			
		||||
lwjgl = "3.3.6"
 | 
			
		||||
minotaur = "2.8.7"
 | 
			
		||||
modDevGradle = "2.0.95"
 | 
			
		||||
nullAway = "0.12.7"
 | 
			
		||||
shadow = "8.3.1"
 | 
			
		||||
spotless = "7.0.2"
 | 
			
		||||
taskTree = "2.1.1"
 | 
			
		||||
teavm = "0.11.0-SQUID.1"
 | 
			
		||||
modDevGradle = "2.0.116"
 | 
			
		||||
nullAway = "0.12.11"
 | 
			
		||||
shadow = "9.2.2"
 | 
			
		||||
spotless = "8.0.0"
 | 
			
		||||
teavm = "0.13.0-SQUID.2"
 | 
			
		||||
vanillaExtract = "0.2.1"
 | 
			
		||||
versionCatalogUpdate = "0.8.1"
 | 
			
		||||
versionCatalogUpdate = "1.0.1"
 | 
			
		||||
 | 
			
		||||
[libraries]
 | 
			
		||||
# Normal dependencies
 | 
			
		||||
@@ -176,7 +175,6 @@ githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "g
 | 
			
		||||
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" }
 | 
			
		||||
 | 
			
		||||
[bundles]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
distributionBase=GRADLE_USER_HOME
 | 
			
		||||
distributionPath=wrapper/dists
 | 
			
		||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
 | 
			
		||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
 | 
			
		||||
networkTimeout=10000
 | 
			
		||||
validateDistributionUrl=true
 | 
			
		||||
zipStoreBase=GRADLE_USER_HOME
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										765
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										765
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -13,7 +13,7 @@
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@rollup/plugin-node-resolve": "^16.0.0",
 | 
			
		||||
    "@rollup/plugin-typescript": "^12.0.0 && <12.1.3",
 | 
			
		||||
    "@rollup/plugin-typescript": "^12.0.0",
 | 
			
		||||
    "@rollup/plugin-url": "^8.0.1",
 | 
			
		||||
    "@swc/core": "^1.3.92",
 | 
			
		||||
    "@types/node": "^24.0.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -83,7 +83,7 @@ val luaJavadoc by tasks.registering(Javadoc::class) {
 | 
			
		||||
    options.addStringOption("project-root", rootProject.file(".").absolutePath)
 | 
			
		||||
    options.noTimestamp(false)
 | 
			
		||||
 | 
			
		||||
    javadocTool = javaToolchains.javadocToolFor { languageVersion = CCTweakedPlugin.JAVA_VERSION }
 | 
			
		||||
    javadocTool = javaToolchains.javadocToolFor { languageVersion = CCTweakedPlugin.JDK_VERSION }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val lintLua by tasks.registering(IlluaminateExec::class) {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.impl.network.wired;
 | 
			
		||||
 | 
			
		||||
import org.jetbrains.annotations.Contract;
 | 
			
		||||
import org.jspecify.annotations.Nullable;
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
@@ -61,7 +60,6 @@ final class InvariantChecker {
 | 
			
		||||
        return okay;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Contract("")
 | 
			
		||||
    private static <T> @Nullable T makeNullable(T object) {
 | 
			
		||||
        return object;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -135,7 +135,7 @@ import java.util.function.UnaryOperator;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Registers ComputerCraft's registry entries and additional objects, such as {@link CauldronInteraction}s and
 | 
			
		||||
 * {@link DetailProvider}s
 | 
			
		||||
 * {@link DetailProvider}s.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * The functions in this class should be called from a loader-specific class.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,12 @@ public enum UserLevel implements Predicate<CommandSourceStack> {
 | 
			
		||||
 | 
			
		||||
    public static boolean isOwner(CommandSourceStack source) {
 | 
			
		||||
        var server = source.getServer();
 | 
			
		||||
 | 
			
		||||
        // While CommandSourceStack.getServer is non-nullable, that's a lie for permission checks. When loading
 | 
			
		||||
        // .mcfunction files, ServerFunctionLibrary constructs an instance with an empty server. In that case, return
 | 
			
		||||
        // false — we don't want to treat functions as an owner!
 | 
			
		||||
        if (server == null) return false;
 | 
			
		||||
 | 
			
		||||
        var player = source.getPlayer();
 | 
			
		||||
        return server.isDedicatedServer()
 | 
			
		||||
            ? source.getEntity() == null && source.hasPermission(4) && source.getTextName().equals("Server")
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Stops a sound on the client
 | 
			
		||||
 * Stops a sound on the client.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Called when a speaker is broken.
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
@@ -305,7 +305,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
 | 
			
		||||
        return wrappers == null ? null : wrappers.get(remoteName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class RemotePeripheralWrapper implements IComputerAccess, GuardedLuaContext.Guard {
 | 
			
		||||
    private static final class RemotePeripheralWrapper implements IComputerAccess, GuardedLuaContext.Guard {
 | 
			
		||||
        private final WiredModemElement element;
 | 
			
		||||
        private final IPeripheral peripheral;
 | 
			
		||||
        private final IComputerAccess computer;
 | 
			
		||||
@@ -331,13 +331,13 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
 | 
			
		||||
            methodMap = methods;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void attach() {
 | 
			
		||||
        private void attach() {
 | 
			
		||||
            attached = true;
 | 
			
		||||
            peripheral.attach(this);
 | 
			
		||||
            computer.queueEvent("peripheral", getAttachmentName());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void detach() {
 | 
			
		||||
        private void detach() {
 | 
			
		||||
            peripheral.detach(this);
 | 
			
		||||
            computer.queueEvent("peripheral_detach", getAttachmentName());
 | 
			
		||||
            attached = false;
 | 
			
		||||
@@ -352,19 +352,19 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements Wi
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public String getType() {
 | 
			
		||||
        private String getType() {
 | 
			
		||||
            return type;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Set<String> getAdditionalTypes() {
 | 
			
		||||
        private Set<String> getAdditionalTypes() {
 | 
			
		||||
            return additionalTypes;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Collection<String> getMethodNames() {
 | 
			
		||||
        private Collection<String> getMethodNames() {
 | 
			
		||||
            return methodMap.keySet();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public MethodResult callMethod(ILuaContext context, String methodName, IArguments arguments) throws LuaException {
 | 
			
		||||
        private MethodResult callMethod(ILuaContext context, String methodName, IArguments arguments) throws LuaException {
 | 
			
		||||
            var method = methodMap.get(methodName);
 | 
			
		||||
            if (method == null) throw new LuaException("No such method " + methodName);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import dan200.computercraft.core.util.Nullability;
 | 
			
		||||
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
 | 
			
		||||
import dan200.computercraft.shared.network.server.ServerNetworking;
 | 
			
		||||
import net.minecraft.core.BlockPos;
 | 
			
		||||
import net.minecraft.server.level.ServerLevel;
 | 
			
		||||
import net.minecraft.world.level.block.entity.BlockEntity;
 | 
			
		||||
import net.minecraft.world.level.block.entity.BlockEntityType;
 | 
			
		||||
import net.minecraft.world.level.block.state.BlockState;
 | 
			
		||||
@@ -47,7 +48,12 @@ public class SpeakerBlockEntity extends BlockEntity {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public SpeakerPosition getPosition() {
 | 
			
		||||
        protected ServerLevel getLevel() {
 | 
			
		||||
            return (ServerLevel) speaker.getLevel();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected SpeakerPosition getPosition() {
 | 
			
		||||
            return SpeakerPosition.of(speaker.getLevel(), Vec3.atCenterOf(speaker.getBlockPos()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,10 @@ import net.minecraft.util.Mth;
 | 
			
		||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
 | 
			
		||||
import org.jspecify.annotations.Nullable;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import static dan200.computercraft.api.lua.LuaValues.checkFinite;
 | 
			
		||||
 | 
			
		||||
@@ -155,7 +158,9 @@ public abstract class SpeakerPeripheral implements IPeripheral {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public abstract SpeakerPosition getPosition();
 | 
			
		||||
    protected abstract ServerLevel getLevel();
 | 
			
		||||
 | 
			
		||||
    protected abstract SpeakerPosition getPosition();
 | 
			
		||||
 | 
			
		||||
    public UUID getSource() {
 | 
			
		||||
        return source;
 | 
			
		||||
@@ -255,8 +260,8 @@ public abstract class SpeakerPeripheral implements IPeripheral {
 | 
			
		||||
        // Prevent playing music discs.
 | 
			
		||||
        var soundEvent = BuiltInRegistries.SOUND_EVENT.get(identifier);
 | 
			
		||||
        // TODO: Build a set of sound events at server startup, and cache this.
 | 
			
		||||
        var level = Objects.requireNonNull(getPosition().level());
 | 
			
		||||
        if (soundEvent != null && level.registryAccess().registry(Registries.JUKEBOX_SONG).orElseThrow().stream().anyMatch(x -> x.soundEvent().value() == soundEvent)) {
 | 
			
		||||
        var level = getLevel();
 | 
			
		||||
        if (soundEvent != null && level.registryAccess().registryOrThrow(Registries.JUKEBOX_SONG).stream().anyMatch(x -> x.soundEvent().value() == soundEvent)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,10 +20,8 @@ public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral {
 | 
			
		||||
        super.detach(computer);
 | 
			
		||||
 | 
			
		||||
        // We could be in the process of shutting down the server, so we can't send packets in this case.
 | 
			
		||||
        var level = getPosition().level();
 | 
			
		||||
        if (level == null) return;
 | 
			
		||||
        var server = level.getServer();
 | 
			
		||||
        if (server == null || server.isStopped()) return;
 | 
			
		||||
        var server = getLevel().getServer();
 | 
			
		||||
        if (server.isStopped()) return;
 | 
			
		||||
 | 
			
		||||
        ServerNetworking.sendToAllPlayers(new SpeakerStopClientMessage(getSource()), server);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,17 +8,23 @@ import dan200.computercraft.api.peripheral.IPeripheral;
 | 
			
		||||
import dan200.computercraft.api.pocket.IPocketAccess;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
 | 
			
		||||
import net.minecraft.server.level.ServerLevel;
 | 
			
		||||
import org.jspecify.annotations.Nullable;
 | 
			
		||||
 | 
			
		||||
public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
 | 
			
		||||
final class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
 | 
			
		||||
    private final IPocketAccess access;
 | 
			
		||||
 | 
			
		||||
    public PocketSpeakerPeripheral(IPocketAccess access) {
 | 
			
		||||
    PocketSpeakerPeripheral(IPocketAccess access) {
 | 
			
		||||
        this.access = access;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public SpeakerPosition getPosition() {
 | 
			
		||||
    protected ServerLevel getLevel() {
 | 
			
		||||
        return access.getLevel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected SpeakerPosition getPosition() {
 | 
			
		||||
        var entity = access.getEntity();
 | 
			
		||||
        return entity == null ? SpeakerPosition.of(access.getLevel(), access.getPosition()) : SpeakerPosition.of(entity);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import dan200.computercraft.api.upgrades.UpgradeType;
 | 
			
		||||
import dan200.computercraft.shared.ModRegistry;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
 | 
			
		||||
import net.minecraft.server.level.ServerLevel;
 | 
			
		||||
import net.minecraft.world.item.ItemStack;
 | 
			
		||||
import net.minecraft.world.phys.Vec3;
 | 
			
		||||
import org.jspecify.annotations.Nullable;
 | 
			
		||||
@@ -26,7 +27,12 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public SpeakerPosition getPosition() {
 | 
			
		||||
        protected ServerLevel getLevel() {
 | 
			
		||||
            return (ServerLevel) turtle.getLevel();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected SpeakerPosition getPosition() {
 | 
			
		||||
            return SpeakerPosition.of(turtle.getLevel(), Vec3.atCenterOf(turtle.getPosition()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,7 @@ public final class InventoryUtil {
 | 
			
		||||
     */
 | 
			
		||||
    public static int getInventorySlotFromCompartment(Player player, int slot, ItemStack stack) {
 | 
			
		||||
        if (stack.isEmpty()) throw new IllegalArgumentException("Cannot search for empty stack");
 | 
			
		||||
        if (player.getInventory().getItem(slot) == stack) return slot;
 | 
			
		||||
        if (slot >= 0 && slot < Inventory.INVENTORY_SIZE && player.getInventory().getItem(slot) == stack) return slot;
 | 
			
		||||
        if (player.getInventory().getItem(Inventory.SLOT_OFFHAND) == stack) return Inventory.SLOT_OFFHAND;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,224 @@
 | 
			
		||||
{
 | 
			
		||||
    "argument.computercraft.argument_expected": "Argumentum szükséges",
 | 
			
		||||
    "argument.computercraft.computer.distance": "Távolság az entitástól",
 | 
			
		||||
    "argument.computercraft.computer.family": "Számítógép típus",
 | 
			
		||||
    "argument.computercraft.computer.id": "Számítógép ID",
 | 
			
		||||
    "argument.computercraft.computer.instance": "Egyedi példányazonosító",
 | 
			
		||||
    "argument.computercraft.computer.label": "Számítógép címke",
 | 
			
		||||
    "argument.computercraft.computer.many_matching": "Több számítógép egyezik '%s' (példányok %s)",
 | 
			
		||||
    "argument.computercraft.computer.no_matching": "Nincs egyező számítógép '%s'",
 | 
			
		||||
    "argument.computercraft.tracking_field.no_field": "Ismeretlen mező '%s'",
 | 
			
		||||
    "argument.computercraft.unknown_computer_family": "Ismeretlen számítógéptípus '%s'",
 | 
			
		||||
    "block.computercraft.cable": "Hálózati kábel",
 | 
			
		||||
    "block.computercraft.computer_advanced": "Fejlett számítógép",
 | 
			
		||||
    "block.computercraft.computer_command": "Parancsszámítógép",
 | 
			
		||||
    "block.computercraft.computer_normal": "Számítógép",
 | 
			
		||||
    "block.computercraft.disk_drive": "Lemezmeghajtó",
 | 
			
		||||
    "block.computercraft.monitor_advanced": "Fejlett monitor",
 | 
			
		||||
    "block.computercraft.printer": "Nyomtató",
 | 
			
		||||
    "block.computercraft.redstone_relay": "Redstone jelfogó",
 | 
			
		||||
    "block.computercraft.speaker": "Hangszóró",
 | 
			
		||||
    "block.computercraft.turtle_advanced": "Fejlett teknős",
 | 
			
		||||
    "block.computercraft.turtle_advanced.upgraded": "Fejlett %s teknős",
 | 
			
		||||
    "block.computercraft.turtle_advanced.upgraded_twice": "Fejlett %s %s teknős",
 | 
			
		||||
    "block.computercraft.turtle_normal": "Teknős",
 | 
			
		||||
    "block.computercraft.turtle_normal.upgraded": "%s teknős",
 | 
			
		||||
    "block.computercraft.turtle_normal.upgraded_twice": "%s %s teknős",
 | 
			
		||||
    "block.computercraft.wired_modem": "Vezetékes modem",
 | 
			
		||||
    "block.computercraft.wired_modem_full": "Vezetékes modem",
 | 
			
		||||
    "block.computercraft.wireless_modem_advanced": "Ender modem",
 | 
			
		||||
    "block.computercraft.wireless_modem_normal": "Vezeték nélküli modem",
 | 
			
		||||
    "chat.computercraft.wired_modem.peripheral_connected": "Periféria \"%s\" csatlakoztatva a hálózathoz",
 | 
			
		||||
    "chat.computercraft.wired_modem.peripheral_disconnected": "Periféria \"%s\" lecsatlakoztatva a hálózatról",
 | 
			
		||||
    "commands.computercraft.desc": "A /computercraft parancs különböző hibakereső és adminisztrációs eszközöket biztosít a számítógépek vezérléséhez és kezeléséhez.",
 | 
			
		||||
    "commands.computercraft.dump.action": "További információ a számítógépről",
 | 
			
		||||
    "commands.computercraft.dump.desc": "Az összes számítógép állapotát, vagy egy konkrét számítógépről szóló információkat jeleníti meg. Megadhatod a számítógép példányazonosítóját (pl. 123), számítógép azonosítóját (pl. #123) vagy címkéjét (pl. \"@Saját Számítógép\").",
 | 
			
		||||
    "commands.computercraft.dump.open_path": "Nézd meg ennek a számítógépnek a fájljait",
 | 
			
		||||
    "commands.computercraft.dump.synopsis": "Számítógépek állapotának megjelenítése.",
 | 
			
		||||
    "commands.computercraft.generic.additional_rows": "%d további sor…",
 | 
			
		||||
    "commands.computercraft.generic.exception": "Kezelhetetlen kivétel (%s)",
 | 
			
		||||
    "commands.computercraft.generic.yes": "I",
 | 
			
		||||
    "commands.computercraft.help.desc": "Megjeleníti ezt a súgóüzenetet",
 | 
			
		||||
    "commands.computercraft.help.no_children": "%s nem rendelkezik alparancsokkal",
 | 
			
		||||
    "commands.computercraft.help.no_command": "Nincs ilyen parancs '%s'",
 | 
			
		||||
    "commands.computercraft.help.synopsis": "Súgó biztosítása egy adott parancshoz",
 | 
			
		||||
    "commands.computercraft.queue.desc": "Számítógép_parancs eseményt küld egy parancsszámítógépnek, további argumentumok továbbításával. Ez főleg térképkészítők számára készült, mint egy számítógépbarátabb változata a /trigger parancsnak. Bármelyik játékos futtathatja a parancsot, amit valószínűleg egy szövegelem kattintási eseményén keresztül végeznek.",
 | 
			
		||||
    "commands.computercraft.queue.synopsis": "Számítógép_parancs esemény küldése egy parancsszámítógépnek",
 | 
			
		||||
    "commands.computercraft.shutdown.desc": "Leállítja a megadott számítógépeket, vagy mindet, ha nincs megadva. Megadhatod a számítógép példányazonosítóját (pl. 123), számítógép azonosítóját (pl. #123) vagy címkéjét (pl. \"@Saját Számítógép\").",
 | 
			
		||||
    "commands.computercraft.shutdown.done": "%s/%s számítógép leállítva",
 | 
			
		||||
    "commands.computercraft.shutdown.synopsis": "Számítógépek távoli leállítása.",
 | 
			
		||||
    "commands.computercraft.synopsis": "Különféle parancsok számítógépek vezérlésére.",
 | 
			
		||||
    "commands.computercraft.tp.action": "Teleportálj ehhez a számítógéphez",
 | 
			
		||||
    "commands.computercraft.tp.desc": "Teleportálj egy számítógép helyére. Megadhatod a számítógép példányazonosítóját (pl. 123) vagy számítógép azonosítóját (pl. #123).",
 | 
			
		||||
    "commands.computercraft.tp.synopsis": "Teleportálás egy adott számítógéphez.",
 | 
			
		||||
    "commands.computercraft.track.desc": "Követi, mennyi ideig futnak a számítógépek, valamint hány eseményt kezelnek. Ez hasonló információkat nyújt, mint a /forge track, és hasznos lehet a késés diagnosztizálásában.",
 | 
			
		||||
    "commands.computercraft.track.dump.computer": "Számítógép",
 | 
			
		||||
    "commands.computercraft.track.dump.desc": "A számítógépes követés legfrissebb eredményeinek megjelenítése.",
 | 
			
		||||
    "commands.computercraft.track.dump.no_timings": "Nincsenek elérhető időzítések",
 | 
			
		||||
    "commands.computercraft.track.dump.synopsis": "A legfrissebb követési eredmények megjelenítése",
 | 
			
		||||
    "commands.computercraft.track.start.desc": "Minden számítógép végrehajtási idejének és eseményszámának követését indítja. Ezzel az előző futások eredményei elvesznek.",
 | 
			
		||||
    "commands.computercraft.track.start.stop": "Futtasd a %s parancsot a követés leállításához és az eredmények megtekintéséhez",
 | 
			
		||||
    "commands.computercraft.track.start.synopsis": "Minden számítógép követésének indítása",
 | 
			
		||||
    "commands.computercraft.track.stop.action": "Kattints a követés leállításához",
 | 
			
		||||
    "commands.computercraft.track.stop.desc": "Minden számítógépes esemény és végrehajtási idő követésének leállítása",
 | 
			
		||||
    "commands.computercraft.track.stop.not_enabled": "A számítógépek jelenleg nem követhetők",
 | 
			
		||||
    "commands.computercraft.track.stop.synopsis": "Minden számítógép követésének leállítása",
 | 
			
		||||
    "commands.computercraft.track.synopsis": "Számítógépek végrehajtási idejének követése.",
 | 
			
		||||
    "commands.computercraft.turn_on.desc": "Kapcsold be a megadott számítógépeket. Megadhatod a számítógép példányazonosítóját (pl. 123), számítógép azonosítóját (pl. #123) vagy címkéjét (pl. \"@Saját Számítógép\").",
 | 
			
		||||
    "commands.computercraft.turn_on.done": "%s/%s számítógép bekapcsolva",
 | 
			
		||||
    "commands.computercraft.turn_on.synopsis": "Számítógépek távoli bekapcsolása.",
 | 
			
		||||
    "commands.computercraft.view.action": "Nézd meg ezt a számítógépet",
 | 
			
		||||
    "commands.computercraft.view.desc": "Nyisd meg egy számítógép terminálját, amely lehetővé teszi a távoli vezérlést. Ez nem biztosít hozzáférést a teknősök leltárához. Megadhatod a számítógép példányazonosítóját (pl. 123) vagy számítógép azonosítóját (pl. #123).",
 | 
			
		||||
    "commands.computercraft.view.not_player": "Nem lehet terminált nyitni nem-játékos entitáshoz",
 | 
			
		||||
    "commands.computercraft.view.synopsis": "Számítógép termináljának megtekintése.",
 | 
			
		||||
    "gui.computercraft.config.command_require_creative": "Parancsszámítógépekhez kreatív mód szükséges",
 | 
			
		||||
    "gui.computercraft.config.command_require_creative.tooltip": "Szükséges, hogy a játékosok kreatív módban legyenek és rendelkezzenek adminisztrátori jogosultsággal a parancsszámítógépek használatához. Ez az alapértelmezett viselkedés a Minecraft parancsblokkjaihoz.",
 | 
			
		||||
    "gui.computercraft.config.computer_space_limit": "Számítógép tárhelykorlát (byte-ban)",
 | 
			
		||||
    "gui.computercraft.config.computer_space_limit.tooltip": "A számítógépek és teknősök lemezterületének korlátja, byte-ban.",
 | 
			
		||||
    "gui.computercraft.config.default_computer_settings": "Alapértelmezett számítógép-beállítások",
 | 
			
		||||
    "gui.computercraft.config.default_computer_settings.tooltip": "Alapértelmezett rendszerbeállítások vesszővel elválasztott listája, amelyek az új számítógépekre vonatkoznak.\nPélda: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\" letiltja az automatikus kiegészítést.",
 | 
			
		||||
    "gui.computercraft.config.disabled_generic_methods": "Letiltott általános metódusok",
 | 
			
		||||
    "gui.computercraft.config.disabled_generic_methods.tooltip": "Általános metódusok vagy metódusforrások listája, amelyek le vannak tiltva. Az általános metódusokat akkor adják hozzá egy blokkhoz vagy blokk entitáshoz, ha nincs kifejezetten hozzárendelt periféria-szolgáltató. Ez magában foglalja a leltár metódusokat (pl. inventory.getItemDetail, inventory.pushItems), valamint (Forge esetén) a fluid_storage és energy_storage metódusokat is.\nA listában lévő metódus lehet egy teljes metóduscsoport (computercraft:inventory), vagy egyetlen metódus (computercraft:inventory#pushItems).",
 | 
			
		||||
    "gui.computercraft.config.execution": "Végrehajtás",
 | 
			
		||||
    "gui.computercraft.config.execution.computer_threads": "Számítógép szálak",
 | 
			
		||||
    "gui.computercraft.config.execution.computer_threads.tooltip": "Az egyidejűleg futó számítógépek számát szabályozza. A magasabb szám több számítógép egyidejű futását teszi lehetővé, de lassulást okozhat. Vegye figyelembe, hogy néhány mod nem működik, ha a szálak száma meghaladja az 1-et. Óvatosan használja.\nTartomány: > 1",
 | 
			
		||||
    "gui.computercraft.config.execution.max_main_computer_time": "Szerver tick számítógépidő-korlát",
 | 
			
		||||
    "gui.computercraft.config.execution.max_main_computer_time.tooltip": "Az ideális maximális idő, ameddig egy számítógép egy tick-ben futtathat, milliszekundumban. Megjegyzés: Lehetséges, hogy túllépjük ezt a határt, mivel nincs mód az idő pontos előrejelzésére - ez az átlagos felső határ.",
 | 
			
		||||
    "gui.computercraft.config.execution.max_main_global_time": "Szerver tick globális időkorlát",
 | 
			
		||||
    "gui.computercraft.config.execution.max_main_global_time.tooltip": "Az egy tick-ben feladatok végrehajtására fordított maximális idő, milliszekundumban.",
 | 
			
		||||
    "gui.computercraft.config.execution.tooltip": "A számítógépek végrehajtási viselkedését szabályozza. Ez elsősorban szerverek finomhangolására szolgál, és általában nem szükséges megváltoztatni.",
 | 
			
		||||
    "gui.computercraft.config.floppy_space_limit": "Floppy lemez tárhelykorlát (byte-ban)",
 | 
			
		||||
    "gui.computercraft.config.floppy_space_limit.tooltip": "A floppy lemezek lemezterületének korlátja, byte-ban.",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth": "Sávszélesség",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.global_download": "Globális letöltési korlát",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.global_download.tooltip": "Az egy másodperc alatt letölthető byte-ok száma, amelyet minden számítógép oszt meg. (byte/s).\nTartomány: > 1",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.global_upload": "Globális feltöltési korlát",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.global_upload.tooltip": "Az egy másodperc alatt feltölthető byte-ok száma, amelyet minden számítógép oszt meg. (byte/s).\nTartomány: > 1",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.tooltip": "A számítógépek által használt sávszélességet korlátozza.",
 | 
			
		||||
    "gui.computercraft.config.http.enabled": "HTTP API engedélyezése",
 | 
			
		||||
    "gui.computercraft.config.http.enabled.tooltip": "Az \"http\" API engedélyezése a számítógépeken. Ha letiltjuk, akkor az \"http\", \"pastebin\" és \"wget\" programok is le lesznek tiltva, amelyeket sok felhasználó használ. Ajánlott engedélyezve hagyni, és a \"rules\" beállítás használatával finomhangolni a szabályozást.",
 | 
			
		||||
    "gui.computercraft.config.http.max_requests": "Maximális egyidejű kérések",
 | 
			
		||||
    "gui.computercraft.config.http.max_requests.tooltip": "Az egyszerre elküldhető http kérések száma egy számítógépen. A további kérések sorba állnak, és akkor kerülnek elküldésre, amikor a futó kérések befejeződtek. 0-ra állítva nincs korlátozás.\nTartomány: > 0",
 | 
			
		||||
    "gui.computercraft.config.http.max_websockets": "Maximális egyidejű websockets",
 | 
			
		||||
    "gui.computercraft.config.http.max_websockets.tooltip": "Az egyszerre megnyitható websockets száma egy számítógépen.\nTartomány: > 1",
 | 
			
		||||
    "gui.computercraft.config.http.proxy.host": "Hosztnév",
 | 
			
		||||
    "gui.computercraft.config.http.proxy.host.tooltip": "A proxy szerver hosztneve vagy IP-címe.",
 | 
			
		||||
    "gui.computercraft.config.http.proxy.port.tooltip": "A proxy szerver portja.\nTartomány: 1 ~ 65536",
 | 
			
		||||
    "gui.computercraft.config.http.proxy.tooltip": "Az HTTP és websocket kérések proxy szerveren keresztüli alagútba helyezése. Csak az \"use_proxy\" igazra állított HTTP szabályok esetén hatékony (alapértelmezésben kikapcsolva).",
 | 
			
		||||
    "gui.computercraft.config.http.proxy.type": "Proxy típus",
 | 
			
		||||
    "gui.computercraft.config.http.proxy.type.tooltip": "A használni kívánt proxy típus.\nMegengedett értékek: HTTP, HTTPS, SOCKS4, SOCKS5",
 | 
			
		||||
    "gui.computercraft.config.http.rules": "Engedélyezés/tiltás szabályok",
 | 
			
		||||
    "gui.computercraft.config.http.rules.tooltip": "Szabályok listája, amelyek szabályozzák az \"http\" API viselkedését adott domainek vagy IP-k számára. Minden szabály egy hosztnévre és egy opcionális portra vonatkozik, majd több tulajdonságot állít be a kéréshez. A szabályok sorrendben kerülnek értékelésre, így az előbbi szabályok felülírják a későbbieket.\n\nÉrvényes tulajdonságok:\n - \"host\" (kötelező): Az a domain vagy IP-cím, amelyre ez a szabály vonatkozik. Ez lehet domain név (\"pastebin.com\"), helyettesítő karakter (\"*.pastebin.com\") vagy CIDR jelölés (\"127.0.0.0/8\").\n - \"port\" (opcionális): Csak adott porttal rendelkező kérésekre vonatkozik, mint például 80 vagy 443.\n\n - \"action\" (opcionális): Azt határozza meg, hogy engedélyezett vagy tiltott legyen-e a kérés.\n - \"max_download\" (opcionális): A maximális méret (byte-ban), amit egy számítógép letölthet ezen a kérésen keresztül.\n - \"max_upload\" (opcionális): A maximális méret (byte-ban), amit egy számítógép feltölthet ezen a kérésen keresztül.\n - \"max_websocket_message\" (opcionális): A maximális méret (byte-ban), amit egy számítógép egy websocket csomagon keresztül küldhet vagy fogadhat.\n - \"use_proxy\" (opcionális): Proxy vagy HTTP/SOCKS proxy használata, ha be van állítva.",
 | 
			
		||||
    "gui.computercraft.config.http.tooltip": "A HTTP API szabályozása",
 | 
			
		||||
    "gui.computercraft.config.http.websocket_enabled": "Websockets engedélyezése",
 | 
			
		||||
    "gui.computercraft.config.http.websocket_enabled.tooltip": "A websockets használatának engedélyezése az http segítségével. Ehhez az \"http_enable\" opciónak is igaznak kell lennie.",
 | 
			
		||||
    "gui.computercraft.config.log_computer_errors": "Számítógép hibáinak naplózása",
 | 
			
		||||
    "gui.computercraft.config.log_computer_errors.tooltip": "A perifériák és más Lua objektumok által kiváltott kivételek naplózása. Ez segíthet a mod készítőknek a hibák megoldásában, de túl sok naplózott hiba esetén log-túltelítettséget eredményezhet.",
 | 
			
		||||
    "gui.computercraft.config.maximum_open_files": "Maximálisan megnyitható fájlok száma számítógépenként",
 | 
			
		||||
    "gui.computercraft.config.maximum_open_files.tooltip": "Beállítja, hogy egy számítógép hány fájlt nyithat meg egyszerre. 0-ra állítva nincs korlátozás.\nTartomány: > 0",
 | 
			
		||||
    "gui.computercraft.config.monitor_distance": "Monitor távolság",
 | 
			
		||||
    "gui.computercraft.config.monitor_distance.tooltip": "A monitorok maximális megjelenítési távolsága. Alapértelmezés szerint a szabványos blokk entitás korlátot használja, de ha nagyobb monitorokat szeretne építeni, akkor ezt növelheti.\nTartomány: 16 ~ 1024",
 | 
			
		||||
    "gui.computercraft.config.monitor_renderer": "Monitor renderelő",
 | 
			
		||||
    "gui.computercraft.config.monitor_renderer.tooltip": "A monitorok renderelőjének beállítása. Általában érdemes \"legjobb\" értéken hagyni - ha a monitorok teljesítményproblémákat okoznak, kipróbálhat más renderelő opciókat.\nMegengedett értékek: BEST, TBO, VBO",
 | 
			
		||||
    "gui.computercraft.config.peripheral": "Perifériák",
 | 
			
		||||
    "gui.computercraft.config.peripheral.command_block_enabled": "Parancsblokk periféria engedélyezése",
 | 
			
		||||
    "gui.computercraft.config.peripheral.command_block_enabled.tooltip": "Parancsblokk periféria támogatásának engedélyezése",
 | 
			
		||||
    "gui.computercraft.config.peripheral.max_notes_per_tick": "Egy tick alatt játszható maximális hangjegyek száma",
 | 
			
		||||
    "gui.computercraft.config.peripheral.max_notes_per_tick.tooltip": "A hangszóró által egy tick alatt lejátszható maximális hangjegyek száma.\nTartomány: > 1",
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_high_altitude_range": "Modem hatótávolság (magas magasság)",
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_high_altitude_range.tooltip": "A vezeték nélküli modemek hatótávolsága maximális magasságban, tiszta időben, méterben.\nTartomány: 0 ~ 100000",
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm": "Modem hatótávolság (magas magasság, viharos idő)",
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm.tooltip": "A vezeték nélküli modemek hatótávolsága maximális magasságban viharos időben, méterben.\nTartomány: 0 ~ 100000",
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_range": "Modem hatótávolság (alapértelmezett)",
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_range.tooltip": "A vezeték nélküli modemek hatótávolsága alacsony magasságban, tiszta időben, méterben.\nTartomány: 0 ~ 100000",
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_range_during_storm": "Modem hatótávolság (viharos idő)",
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_range_during_storm.tooltip": "A vezeték nélküli modemek hatótávolsága alacsony magasságban, viharos időben, méterben.\nTartomány: 0 ~ 100000",
 | 
			
		||||
    "gui.computercraft.config.peripheral.monitor_bandwidth": "Monitor sávszélesség",
 | 
			
		||||
    "gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "A monitoronként elküldhető adatkorlát *tick-enként*. Megjegyzés:\n - A sávszélességet a tömörítés előtt mérjük, így az ügyfélhez elküldött adat kisebb.\n - Nem veszi figyelembe a játékosok számát, akiknek egy csomagot küldünk. Egy monitor frissítése egy játékos számára ugyanolyan sávszélesség-korlátot fogyaszt, mint 20 játékosnak küldve.\n - Egy teljes méretű monitor ~25kb adatot küld. Az alapértelmezett érték (1MB) ~40 monitor frissítését teszi lehetővé egyetlen tick-ben.\n0-ra állítva letiltja.\nTartomány: > 0",
 | 
			
		||||
    "gui.computercraft.config.peripheral.tooltip": "Különböző beállítások a perifériákhoz.",
 | 
			
		||||
    "gui.computercraft.config.term_sizes": "Terminál méretek",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.computer": "Számítógép",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.computer.height": "Terminál magasság",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.computer.height.tooltip": "Tartomány: 1 ~ 255",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.computer.tooltip": "Számítógépek terminálmérete.",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.computer.width": "Terminál szélesség",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.computer.width.tooltip": "Tartomány: 1 ~ 255",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.height": "Maximális monitor magasság",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.height.tooltip": "Tartomány: 1 ~ 32",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.tooltip": "A monitorok maximális mérete (blokkokban).",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.width": "Maximális monitor szélesség",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.width.tooltip": "Tartomány: 1 ~ 32",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer": "Zseb számítógép",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer.height": "Terminál magasság",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "Tartomány: 1 ~ 255",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer.tooltip": "Zseb számítógépek terminálmérete.",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer.width": "Terminál szélesség",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "Tartomány: 1 ~ 255",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.tooltip": "Különböző számítógépek terminálméretének konfigurálása.\nA nagyobb terminálok több sávszélességet igényelnek, ezért óvatosan használjuk.",
 | 
			
		||||
    "gui.computercraft.config.turtle": "Teknősök",
 | 
			
		||||
    "gui.computercraft.config.turtle.advanced_fuel_limit": "Fejlett teknős üzemanyag korlát",
 | 
			
		||||
    "gui.computercraft.config.turtle.advanced_fuel_limit.tooltip": "A fejlett teknősök üzemanyag korlátja.\nTartomány: > 0",
 | 
			
		||||
    "gui.computercraft.config.turtle.can_push": "A teknősök tolják az entitásokat",
 | 
			
		||||
    "gui.computercraft.config.turtle.can_push.tooltip": "Ha be van állítva igazra, a teknősök eltolják az entitásokat az útból, ha van elég hely, ahelyett, hogy megállnának.",
 | 
			
		||||
    "gui.computercraft.config.turtle.need_fuel": "Üzemanyag szükséges",
 | 
			
		||||
    "gui.computercraft.config.turtle.need_fuel.tooltip": "Beállítja, hogy a teknősöknek szükségük van-e üzemanyagra a mozgáshoz.",
 | 
			
		||||
    "gui.computercraft.config.turtle.normal_fuel_limit": "Teknős üzemanyag korlát",
 | 
			
		||||
    "gui.computercraft.config.turtle.normal_fuel_limit.tooltip": "A teknősök üzemanyag korlátja.\nTartomány: > 0",
 | 
			
		||||
    "gui.computercraft.config.turtle.tooltip": "Különböző beállítások a teknősökhöz.",
 | 
			
		||||
    "gui.computercraft.config.upload_max_size": "Feltöltési fájlméret korlát (byte-ban)",
 | 
			
		||||
    "gui.computercraft.config.upload_max_size.tooltip": "A feltöltési fájlméret korlátja, byte-ban. A tartománynak 1 KiB és 16 MiB között kell lennie.\nVegye figyelembe, hogy a feltöltések egyetlen tick alatt kerülnek feldolgozásra - a nagy fájlok vagy a gyenge hálózati teljesítmény leállíthatják a hálózati szálat. És figyeljünk a lemezterületre!\nTartomány: 1024 ~ 16777216",
 | 
			
		||||
    "gui.computercraft.config.upload_nag_delay": "Feltöltési figyelmeztetési késleltetés",
 | 
			
		||||
    "gui.computercraft.config.upload_nag_delay.tooltip": "Az a késleltetés másodpercben, ami után figyelmeztetést kapunk a feldolgozatlan importokról. 0-ra állítva letiltja.\nTartomány: 0 ~ 60",
 | 
			
		||||
    "gui.computercraft.pocket_computer_overlay": "Zseb számítógép megnyitva. Nyomd meg az ESC-t a bezáráshoz.",
 | 
			
		||||
    "gui.computercraft.terminal": "Számítógép terminál",
 | 
			
		||||
    "gui.computercraft.tooltip.computer_id": "Számítógép ID: %s",
 | 
			
		||||
    "gui.computercraft.tooltip.copy": "Másolás vágólapra",
 | 
			
		||||
    "gui.computercraft.tooltip.disk_id": "Lemez ID: %s",
 | 
			
		||||
    "gui.computercraft.tooltip.terminate": "A futó kód leállítása",
 | 
			
		||||
    "gui.computercraft.tooltip.terminate.key": "Tartsd lenyomva a Ctrl+T billentyűket",
 | 
			
		||||
    "gui.computercraft.tooltip.turn_off": "Kapcsold ki ezt a számítógépet",
 | 
			
		||||
    "gui.computercraft.tooltip.turn_off.key": "Tartsd lenyomva a Ctrl+S billentyűket",
 | 
			
		||||
    "gui.computercraft.tooltip.turn_on": "Kapcsold be ezt a számítógépet",
 | 
			
		||||
    "gui.computercraft.upload.failed": "Feltöltés sikertelen",
 | 
			
		||||
    "gui.computercraft.upload.failed.computer_off": "Be kell kapcsolnod a számítógépet a fájlok feltöltéséhez.",
 | 
			
		||||
    "gui.computercraft.upload.failed.corrupted": "Fájlok megsérültek a feltöltés során. Kérlek próbáld újra.",
 | 
			
		||||
    "gui.computercraft.upload.failed.generic": "Feltöltés sikertelen (%s)",
 | 
			
		||||
    "gui.computercraft.upload.failed.name_too_long": "A fájlnevek túl hosszúak a feltöltéshez.",
 | 
			
		||||
    "gui.computercraft.upload.failed.too_many_files": "Nem lehet ilyen sok fájlt feltölteni.",
 | 
			
		||||
    "gui.computercraft.upload.failed.too_much": "A fájlok túl nagyok a feltöltéshez.",
 | 
			
		||||
    "gui.computercraft.upload.no_response": "Fájlok átvitele",
 | 
			
		||||
    "gui.computercraft.upload.no_response.msg": "A számítógéped nem használta a továbbított fájlokat. Lehet, hogy futtatnod kell a %s programot, majd újra próbálkozni.",
 | 
			
		||||
    "item.computercraft.disk": "Floppy lemez",
 | 
			
		||||
    "item.computercraft.pocket_computer_advanced": "Fejlett zseb számítógép",
 | 
			
		||||
    "item.computercraft.pocket_computer_advanced.upgraded": "Fejlett %s zseb számítógép",
 | 
			
		||||
    "item.computercraft.pocket_computer_normal": "Zseb számítógép",
 | 
			
		||||
    "item.computercraft.pocket_computer_normal.upgraded": "%s zseb számítógép",
 | 
			
		||||
    "item.computercraft.printed_book": "Nyomtatott könyv",
 | 
			
		||||
    "item.computercraft.printed_page": "Nyomtatott oldal",
 | 
			
		||||
    "item.computercraft.printed_pages": "Nyomtatott oldalak",
 | 
			
		||||
    "item.computercraft.treasure_disk": "Floppy lemez",
 | 
			
		||||
    "tag.item.computercraft.computer": "Számítógépek",
 | 
			
		||||
    "tag.item.computercraft.monitor": "Monitorok",
 | 
			
		||||
    "tag.item.computercraft.turtle": "Teknősök",
 | 
			
		||||
    "tag.item.computercraft.wired_modem": "Vezetékes modemek",
 | 
			
		||||
    "tracking_field.computercraft.avg": "%s (átlag)",
 | 
			
		||||
    "tracking_field.computercraft.computer_tasks.name": "Feladatok",
 | 
			
		||||
    "tracking_field.computercraft.count": "%s (szám)",
 | 
			
		||||
    "tracking_field.computercraft.fs.name": "Fájlrendszer műveletek",
 | 
			
		||||
    "tracking_field.computercraft.http_download.name": "HTTP letöltés",
 | 
			
		||||
    "tracking_field.computercraft.http_requests.name": "HTTP kérések",
 | 
			
		||||
    "tracking_field.computercraft.http_upload.name": "HTTP feltöltés",
 | 
			
		||||
    "tracking_field.computercraft.peripheral.name": "Periféria hívások",
 | 
			
		||||
    "tracking_field.computercraft.server_tasks.name": "Szerver feladatok",
 | 
			
		||||
    "tracking_field.computercraft.turtle_ops.name": "Teknős műveletek",
 | 
			
		||||
    "tracking_field.computercraft.websocket_incoming.name": "Websocket bejövő",
 | 
			
		||||
    "tracking_field.computercraft.websocket_outgoing.name": "Websocket kimenő",
 | 
			
		||||
    "upgrade.computercraft.speaker.adjective": "Hangos",
 | 
			
		||||
    "upgrade.computercraft.wireless_modem_normal.adjective": "Vezeték nélküli",
 | 
			
		||||
    "upgrade.minecraft.crafting_table.adjective": "Kézműves",
 | 
			
		||||
    "upgrade.minecraft.diamond_axe.adjective": "Fanyeső",
 | 
			
		||||
    "upgrade.minecraft.diamond_hoe.adjective": "Gazdálkodó",
 | 
			
		||||
    "upgrade.minecraft.diamond_pickaxe.adjective": "Bányász",
 | 
			
		||||
    "upgrade.minecraft.diamond_shovel.adjective": "Ásó",
 | 
			
		||||
    "upgrade.minecraft.diamond_sword.adjective": "Közelharci"
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +1,19 @@
 | 
			
		||||
{
 | 
			
		||||
    "argument.computercraft.argument_expected": "预期自变量",
 | 
			
		||||
    "argument.computercraft.computer.distance": "实体距离",
 | 
			
		||||
    "argument.computercraft.computer.family": "电脑类别",
 | 
			
		||||
    "argument.computercraft.computer.id": "电脑ID",
 | 
			
		||||
    "argument.computercraft.computer.family": "计算机类别",
 | 
			
		||||
    "argument.computercraft.computer.id": "计算机ID",
 | 
			
		||||
    "argument.computercraft.computer.instance": "唯一实例ID",
 | 
			
		||||
    "argument.computercraft.computer.label": "电脑标签",
 | 
			
		||||
    "argument.computercraft.computer.label": "计算机标签",
 | 
			
		||||
    "argument.computercraft.computer.many_matching": "多台计算机匹配'%s' (实例%s)",
 | 
			
		||||
    "argument.computercraft.computer.no_matching": "没有计算机匹配'%s'",
 | 
			
		||||
    "argument.computercraft.tracking_field.no_field": "未知字段'%s'",
 | 
			
		||||
    "argument.computercraft.unknown_computer_family": "未知电脑类别 '%s'",
 | 
			
		||||
    "block.computercraft.cable": "网络电缆",
 | 
			
		||||
    "argument.computercraft.unknown_computer_family": "未知计算机类别“%s”",
 | 
			
		||||
    "block.computercraft.cable": "网络线缆",
 | 
			
		||||
    "block.computercraft.computer_advanced": "高级计算机",
 | 
			
		||||
    "block.computercraft.computer_command": "命令电脑",
 | 
			
		||||
    "block.computercraft.computer_command": "命令计算机",
 | 
			
		||||
    "block.computercraft.computer_normal": "计算机",
 | 
			
		||||
    "block.computercraft.disk_drive": "磁盘驱动器",
 | 
			
		||||
    "block.computercraft.disk_drive": "软盘驱动器",
 | 
			
		||||
    "block.computercraft.monitor_advanced": "高级显示器",
 | 
			
		||||
    "block.computercraft.monitor_normal": "显示器",
 | 
			
		||||
    "block.computercraft.printer": "打印机",
 | 
			
		||||
@@ -29,36 +29,36 @@
 | 
			
		||||
    "block.computercraft.wired_modem_full": "有线调制解调器",
 | 
			
		||||
    "block.computercraft.wireless_modem_advanced": "末影调制解调器",
 | 
			
		||||
    "block.computercraft.wireless_modem_normal": "无线调制解调器",
 | 
			
		||||
    "chat.computercraft.wired_modem.peripheral_connected": "外部设备\"%s\"连接到网络",
 | 
			
		||||
    "chat.computercraft.wired_modem.peripheral_disconnected": "外部设备\"%s\"与网络断开连接",
 | 
			
		||||
    "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.generic.additional_rows": "%d额外的行…",
 | 
			
		||||
    "commands.computercraft.generic.exception": "未处理的异常(%s)",
 | 
			
		||||
    "commands.computercraft.generic.no": "N",
 | 
			
		||||
    "commands.computercraft.generic.yes": "Y",
 | 
			
		||||
    "commands.computercraft.dump.open_path": "查看此计算机的文件",
 | 
			
		||||
    "commands.computercraft.dump.synopsis": "显示计算机的状态。",
 | 
			
		||||
    "commands.computercraft.generic.additional_rows": "%d额外的行……",
 | 
			
		||||
    "commands.computercraft.generic.exception": "未处理的异常(%s)",
 | 
			
		||||
    "commands.computercraft.generic.no": "否",
 | 
			
		||||
    "commands.computercraft.generic.yes": "是",
 | 
			
		||||
    "commands.computercraft.help.desc": "显示该帮助信息",
 | 
			
		||||
    "commands.computercraft.help.no_children": "%s没有子命令",
 | 
			
		||||
    "commands.computercraft.help.no_command": "没有这样的命令'%s'",
 | 
			
		||||
    "commands.computercraft.help.no_command": "没有这样的命令“%s”",
 | 
			
		||||
    "commands.computercraft.help.synopsis": "为特定的命令提供帮助",
 | 
			
		||||
    "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.tp.action": "传送到这台电脑",
 | 
			
		||||
    "commands.computercraft.tp.desc": "传送到计算机的位置. 你可以指定计算机的实例id (例如. 123)或计算机id (例如. #123).",
 | 
			
		||||
    "commands.computercraft.tp.synopsis": "传送到特定的计算机.",
 | 
			
		||||
    "commands.computercraft.track.desc": "跟踪计算机执行的时间以及它们处理的事件数. 这以/forge track类似的方式呈现信息,可用于诊断滞后.",
 | 
			
		||||
    "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 track类似的方式呈现信息,可能有助于诊断卡顿与滞后。",
 | 
			
		||||
    "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.stop.action": "点击停止跟踪",
 | 
			
		||||
@@ -67,16 +67,16 @@
 | 
			
		||||
    "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.done": "已打开%s/%s台计算机",
 | 
			
		||||
    "commands.computercraft.turn_on.synopsis": "远程打开计算机.",
 | 
			
		||||
    "commands.computercraft.view.action": "查看此计算机",
 | 
			
		||||
    "commands.computercraft.view.desc": "打开计算机的终端,允许远程控制计算机. 这不提供对海龟库存的访问. 你可以指定计算机的实例id (例如. 123)或计算机id (例如. #123).",
 | 
			
		||||
    "commands.computercraft.view.not_player": "无法为非玩家打开终端",
 | 
			
		||||
    "commands.computercraft.view.synopsis": "查看计算机的终端.",
 | 
			
		||||
    "gui.computercraft.config.command_require_creative": "命令电脑需要创造模式",
 | 
			
		||||
    "gui.computercraft.config.command_require_creative": "命令计算机需要创造模式",
 | 
			
		||||
    "gui.computercraft.config.command_require_creative.tooltip": "玩家需要处于创造模式并为管理员才能与命令计算机交互。\n这是原版命令方块的默认行为。",
 | 
			
		||||
    "gui.computercraft.config.computer_space_limit": "计算机空间限制(字节)",
 | 
			
		||||
    "gui.computercraft.config.computer_space_limit.tooltip": "计算机和海龟的磁盘空间限制,以字节为单位。",
 | 
			
		||||
    "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": "禁用的通用方法",
 | 
			
		||||
@@ -89,15 +89,15 @@
 | 
			
		||||
    "gui.computercraft.config.execution.max_main_global_time": "服务器全局tick时间限制",
 | 
			
		||||
    "gui.computercraft.config.execution.max_main_global_time.tooltip": "在1刻内执行任务所花费的最大时间,以毫秒为单位。\n请注意,我们很可能会超出此限制,因为无法确定需要多长时间——这旨在成为平均时间的上限。",
 | 
			
		||||
    "gui.computercraft.config.execution.tooltip": "控制计算机的执行行为。这主要用于微调服务器,一般不需要触碰。",
 | 
			
		||||
    "gui.computercraft.config.floppy_space_limit": "软盘空间限制(字节)",
 | 
			
		||||
    "gui.computercraft.config.floppy_space_limit.tooltip": "软盘的磁盘空间限制,以字节为单位。",
 | 
			
		||||
    "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": "每秒钟可以下载的字节数. 所有电脑共享该设置 (bytes/s).",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.global_download.tooltip": "每秒钟内可下载的字节数。此值在所有计算机之间共享。(字节/秒)",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.global_upload": "全局上传限速",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.global_upload.tooltip": "每秒钟可以上传的字节数. 所有电脑共享该设置 (bytes/s).",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.tooltip": "限制电脑可以使用的带宽.",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.global_upload.tooltip": "每秒钟内可上传的字节数。此值在所有计算机之间共享。(字节/秒)",
 | 
			
		||||
    "gui.computercraft.config.http.bandwidth.tooltip": "限制计算机可以使用的带宽。",
 | 
			
		||||
    "gui.computercraft.config.http.enabled": "启用HTTP API",
 | 
			
		||||
    "gui.computercraft.config.http.enabled.tooltip": "在计算机上启用“http”API。禁用此功能还会禁用许多用户依赖的“pastebin”和“wget”程序。建议保持此功能开启并使用“规则”配置选项来实施更精细的控制。",
 | 
			
		||||
    "gui.computercraft.config.http.max_requests": "最大并发请求数",
 | 
			
		||||
@@ -112,7 +112,7 @@
 | 
			
		||||
    "gui.computercraft.config.http.proxy.tooltip": "通过代理服务器传输HTTP和WebSocket请求。仅影响将“use_proxy”设为true(默认关闭)的HTTP规则。\n如果代理需要认证,请在与“computercraft-server.toml”相同的目录中创建一个“computercraft-proxy.pw”文件,其中包含以冒号分隔的用户名和密码,例如“myuser:mypassword”。对于SOCKS4代理,只需要用户名。",
 | 
			
		||||
    "gui.computercraft.config.http.proxy.type": "代理类型",
 | 
			
		||||
    "gui.computercraft.config.http.proxy.type.tooltip": "代理使用的协议.",
 | 
			
		||||
    "gui.computercraft.config.http.rules": "允许/阻止规则",
 | 
			
		||||
    "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”(可选):计算机在一个WebSocket数据包中可以发送或接收的最大大小(以字节为单位)。\n —“use_proxy”(可选):如果已配置,则启用HTTP/SOCKS代理。",
 | 
			
		||||
    "gui.computercraft.config.http.tooltip": "控制HTTP API",
 | 
			
		||||
    "gui.computercraft.config.http.websocket_enabled": "启用websockets",
 | 
			
		||||
@@ -121,11 +121,11 @@
 | 
			
		||||
    "gui.computercraft.config.log_computer_errors.tooltip": "记录外设和其他Lua对象抛出的异常。这可让Mod作者更轻松地调试问题,但如果人们使用有缺陷的方法,则可能导致日志垃圾。",
 | 
			
		||||
    "gui.computercraft.config.maximum_open_files": "每台计算机打开的最大文件数",
 | 
			
		||||
    "gui.computercraft.config.maximum_open_files.tooltip": "设置计算机可同时打开的文件数。设为0表示无限制。",
 | 
			
		||||
    "gui.computercraft.config.monitor_distance": "监视器距离",
 | 
			
		||||
    "gui.computercraft.config.monitor_distance.tooltip": "监视器渲染的最大距离。默认为标准图块实体限制,但如果你希望建造更大的监视器,则可以扩展。",
 | 
			
		||||
    "gui.computercraft.config.monitor_renderer": "监视器渲染器",
 | 
			
		||||
    "gui.computercraft.config.monitor_renderer.tooltip": "用于监视器的渲染器。通常,应将其保持在“最佳”状态——如果监视器存在性能问题,你可以尝试其他渲染器。",
 | 
			
		||||
    "gui.computercraft.config.peripheral": "外围设备",
 | 
			
		||||
    "gui.computercraft.config.monitor_distance": "显示器距离",
 | 
			
		||||
    "gui.computercraft.config.monitor_distance.tooltip": "显示器渲染的最大距离。默认为标准图块实体限制,但如果你希望建造更大的显示器,则可以调高此值。",
 | 
			
		||||
    "gui.computercraft.config.monitor_renderer": "显示器渲染器",
 | 
			
		||||
    "gui.computercraft.config.monitor_renderer.tooltip": "用于显示器的渲染器。通常,应将其保持在“最佳”状态——如果显示器存在性能问题,你可以尝试其他渲染器。",
 | 
			
		||||
    "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": "计算机一次可以播放的最大音符数量",
 | 
			
		||||
@@ -138,8 +138,8 @@
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_range.tooltip": "晴朗天气下低海拔无线调制解调器的范围,以米为单位。",
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_range_during_storm": "调制解调器范围(恶劣天气)",
 | 
			
		||||
    "gui.computercraft.config.peripheral.modem_range_during_storm.tooltip": "暴风雨天气下低海拔无线调制解调器的范围,以米为单位。",
 | 
			
		||||
    "gui.computercraft.config.peripheral.monitor_bandwidth": "监视器带宽",
 | 
			
		||||
    "gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "*每刻*可以发送多少监视器数据的限制。请注意:\n —带宽是在压缩之前测量的,因此发送到客户端的数据较少。\n —这忽略了数据包发送到的玩家数量。为一个玩家更新监视器所消耗的带宽限制与发送到20个玩家的带宽限制相同。\n —全尺寸监视器发送约25kb的数据。因此默认值(1MB)允许在1刻内更新约40个监视器。\n设为0以禁用。",
 | 
			
		||||
    "gui.computercraft.config.peripheral.monitor_bandwidth": "显示器带宽",
 | 
			
		||||
    "gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "*每刻*可以发送多少显示器数据的限制。请注意:\n - 带宽是在压缩之前测量的,因此发送到客户端的数据较少。\n - 这忽略了数据包发送到的玩家数量。为一个玩家更新显示器所消耗的带宽限制与发送到20个玩家的带宽限制相同。\n - 全尺寸显示器发送约25kb的数据。因此默认值(1MB)允许在1刻内更新约40个显示器。\n设为0以禁用。",
 | 
			
		||||
    "gui.computercraft.config.peripheral.tooltip": "与外设相关的各种选项。",
 | 
			
		||||
    "gui.computercraft.config.term_sizes": "终端尺寸",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.computer": "计算机",
 | 
			
		||||
@@ -148,18 +148,18 @@
 | 
			
		||||
    "gui.computercraft.config.term_sizes.computer.tooltip": "计算机的终端尺寸。",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.computer.width": "终端宽度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.computer.width.tooltip": "计算机终端宽度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor": "监视器",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.height": "最大监视器高度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.height.tooltip": "监视器的最大高度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.tooltip": "监视器的最大尺寸(以方块为单位)。",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.width": "最大监视器宽度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.width.tooltip": "监视器的最大宽度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer": "手提计算机",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor": "显示器",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.height": "最大显示器高度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.height.tooltip": "显示器的最大高度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.tooltip": "显示器的最大尺寸(以方块为单位)。",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.width": "最大显示器宽度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.monitor.width.tooltip": "显示器的最大宽度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer": "便携式计算机",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer.height": "终端高度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "手提计算机终端高度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer.tooltip": "手提计算机的终端尺寸。",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer.height.tooltip": "便携式计算机终端高度",
 | 
			
		||||
    "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": "手提计算机终端宽度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.pocket_computer.width.tooltip": "便携式计算机终端宽度",
 | 
			
		||||
    "gui.computercraft.config.term_sizes.tooltip": "配置各种计算机终端的尺寸。\n终端越大,需要的带宽越多,请谨慎使用。",
 | 
			
		||||
    "gui.computercraft.config.turtle": "海龟",
 | 
			
		||||
    "gui.computercraft.config.turtle.advanced_fuel_limit": "高级海龟燃料限制",
 | 
			
		||||
@@ -172,14 +172,14 @@
 | 
			
		||||
    "gui.computercraft.config.turtle.normal_fuel_limit.tooltip": "海龟的燃料限制。",
 | 
			
		||||
    "gui.computercraft.config.turtle.tooltip": "与海龟相关的各种选项。",
 | 
			
		||||
    "gui.computercraft.config.upload_max_size": "文件上传大小限制(字节)",
 | 
			
		||||
    "gui.computercraft.config.upload_max_size.tooltip": "文件上传大小限制(以字节为单位)。必须在1KiB到16MiB之间。\n请记住,上传在1刻内处理——大文件或不佳的网络性能可能会使网络线程停滞。并且注意磁盘空间!",
 | 
			
		||||
    "gui.computercraft.config.upload_max_size.tooltip": "文件上传大小限制(以字节为单位)。必须在1KiB到16MiB之间。\n请记住,上传在1刻内处理——大文件或不佳的网络性能可能会使网络线程停滞。记得注意软盘空间!",
 | 
			
		||||
    "gui.computercraft.config.upload_nag_delay": "上传延迟",
 | 
			
		||||
    "gui.computercraft.config.upload_nag_delay.tooltip": "通知未处理的导入之前延迟的秒数。设为0以禁用。",
 | 
			
		||||
    "gui.computercraft.pocket_computer_overlay": "手提计算机已打开。按ESC键关闭。",
 | 
			
		||||
    "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.disk_id": "软盘ID:%s",
 | 
			
		||||
    "gui.computercraft.tooltip.terminate": "停止当前运行的代码",
 | 
			
		||||
    "gui.computercraft.tooltip.terminate.key": "按住Ctrl+T",
 | 
			
		||||
    "gui.computercraft.tooltip.turn_off": "关闭这台计算机",
 | 
			
		||||
@@ -195,19 +195,19 @@
 | 
			
		||||
    "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.printed_pages": "一摞打印纸",
 | 
			
		||||
    "item.computercraft.treasure_disk": "软盘",
 | 
			
		||||
    "itemGroup.computercraft": "ComputerCraft",
 | 
			
		||||
    "tag.item.computercraft.computer": "计算机",
 | 
			
		||||
    "tag.item.computercraft.disks": "磁盘",
 | 
			
		||||
    "tag.item.computercraft.disks": "软盘",
 | 
			
		||||
    "tag.item.computercraft.dyeable": "可染色物品",
 | 
			
		||||
    "tag.item.computercraft.monitor": "监视器",
 | 
			
		||||
    "tag.item.computercraft.monitor": "显示器",
 | 
			
		||||
    "tag.item.computercraft.pocket_computers": "便携式计算机",
 | 
			
		||||
    "tag.item.computercraft.turtle": "海龟",
 | 
			
		||||
    "tag.item.computercraft.turtle_can_place": "可放置海龟物品",
 | 
			
		||||
@@ -220,12 +220,12 @@
 | 
			
		||||
    "tracking_field.computercraft.http_requests.name": "HTTP请求",
 | 
			
		||||
    "tracking_field.computercraft.http_upload.name": "HTTP上传",
 | 
			
		||||
    "tracking_field.computercraft.max": "%s (最大)",
 | 
			
		||||
    "tracking_field.computercraft.peripheral.name": "外部设备呼叫",
 | 
			
		||||
    "tracking_field.computercraft.peripheral.name": "外部设备调用",
 | 
			
		||||
    "tracking_field.computercraft.server_tasks.name": "服务器任务",
 | 
			
		||||
    "tracking_field.computercraft.turtle_ops.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": "喧闹",
 | 
			
		||||
    "upgrade.computercraft.speaker.adjective": "嘈杂",
 | 
			
		||||
    "upgrade.computercraft.wireless_modem_advanced.adjective": "末影",
 | 
			
		||||
    "upgrade.computercraft.wireless_modem_normal.adjective": "无线",
 | 
			
		||||
    "upgrade.minecraft.crafting_table.adjective": "合成",
 | 
			
		||||
 
 | 
			
		||||
@@ -141,7 +141,7 @@ public class NetworkBenchmark {
 | 
			
		||||
        return networks;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static class Grid<T> {
 | 
			
		||||
    private static final class Grid<T> {
 | 
			
		||||
        private final int size;
 | 
			
		||||
        private final T[] box;
 | 
			
		||||
 | 
			
		||||
@@ -151,7 +151,7 @@ public class NetworkBenchmark {
 | 
			
		||||
            this.box = (T[]) new Object[size * size * size];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public T get(BlockPos pos) {
 | 
			
		||||
        private T get(BlockPos pos) {
 | 
			
		||||
            int x = pos.getX(), y = pos.getY(), z = pos.getZ();
 | 
			
		||||
 | 
			
		||||
            return x >= 0 && x < size && y >= 0 && y < size && z >= 0 && z < size
 | 
			
		||||
@@ -159,7 +159,7 @@ public class NetworkBenchmark {
 | 
			
		||||
                : null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void forEach(BiConsumer<T, BlockPos> transform) {
 | 
			
		||||
        private void forEach(BiConsumer<T, BlockPos> transform) {
 | 
			
		||||
            for (var x = 0; x < size; x++) {
 | 
			
		||||
                for (var y = 0; y < size; y++) {
 | 
			
		||||
                    for (var z = 0; z < size; z++) {
 | 
			
		||||
@@ -169,7 +169,7 @@ public class NetworkBenchmark {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void map(BiFunction<T, BlockPos, T> transform) {
 | 
			
		||||
        private void map(BiFunction<T, BlockPos, T> transform) {
 | 
			
		||||
            for (var x = 0; x < size; x++) {
 | 
			
		||||
                for (var y = 0; y < size; y++) {
 | 
			
		||||
                    for (var z = 0; z < size; z++) {
 | 
			
		||||
 
 | 
			
		||||
@@ -61,4 +61,14 @@ public class ObjectLuaTable implements LuaTable<Object, Object> {
 | 
			
		||||
    public Set<Entry<Object, Object>> entrySet() {
 | 
			
		||||
        return map.entrySet();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean equals(Object o) {
 | 
			
		||||
        return this == o || o instanceof Map<?, ?> otherMap && map.equals(otherMap);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int hashCode() {
 | 
			
		||||
        return map.hashCode();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
import cc.tweaked.gradle.CCTweakedPlugin
 | 
			
		||||
import cc.tweaked.gradle.getAbsolutePath
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
@@ -39,6 +40,8 @@ dependencies {
 | 
			
		||||
    testRuntimeOnly(libs.slf4j.simple)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
kotlin.compilerOptions.jvmTarget = CCTweakedPlugin.KOTLIN_TARGET
 | 
			
		||||
 | 
			
		||||
tasks.processResources {
 | 
			
		||||
    inputs.property("gitHash", cct.gitHash)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ import java.util.*;
 | 
			
		||||
 * @hidden
 | 
			
		||||
 */
 | 
			
		||||
public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChangeListener {
 | 
			
		||||
    private class PeripheralWrapper extends ComputerAccess implements GuardedLuaContext.Guard {
 | 
			
		||||
    private final class PeripheralWrapper extends ComputerAccess implements GuardedLuaContext.Guard {
 | 
			
		||||
        private final String side;
 | 
			
		||||
        private final IPeripheral peripheral;
 | 
			
		||||
 | 
			
		||||
@@ -49,32 +49,32 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
 | 
			
		||||
            methodMap = peripheralMethods.getSelfMethods(peripheral);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IPeripheral getPeripheral() {
 | 
			
		||||
        private IPeripheral getPeripheral() {
 | 
			
		||||
            return peripheral;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public String getType() {
 | 
			
		||||
        private String getType() {
 | 
			
		||||
            return type;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Set<String> getAdditionalTypes() {
 | 
			
		||||
        private Set<String> getAdditionalTypes() {
 | 
			
		||||
            return additionalTypes;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Collection<String> getMethods() {
 | 
			
		||||
        private Collection<String> getMethods() {
 | 
			
		||||
            return methodMap.keySet();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public synchronized boolean isAttached() {
 | 
			
		||||
        private synchronized boolean isAttached() {
 | 
			
		||||
            return attached;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public synchronized void attach() {
 | 
			
		||||
        private synchronized void attach() {
 | 
			
		||||
            attached = true;
 | 
			
		||||
            peripheral.attach(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void detach() {
 | 
			
		||||
        private void detach() {
 | 
			
		||||
            // Call detach
 | 
			
		||||
            peripheral.detach(this);
 | 
			
		||||
 | 
			
		||||
@@ -86,7 +86,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
 | 
			
		||||
            attached = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public MethodResult call(ILuaContext context, String methodName, IArguments arguments) throws LuaException {
 | 
			
		||||
        private MethodResult call(ILuaContext context, String methodName, IArguments arguments) throws LuaException {
 | 
			
		||||
            PeripheralMethod method;
 | 
			
		||||
            synchronized (this) {
 | 
			
		||||
                method = methodMap.get(methodName);
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ import java.net.URI;
 | 
			
		||||
import java.util.concurrent.Future;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Checks a URL using {@link NetworkUtils#getAddress(String, int, boolean)}}
 | 
			
		||||
 * Checks a URL using {@link NetworkUtils#getAddress(String, int, boolean)}.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * This requires a DNS lookup, and so needs to occur off-thread.
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,7 @@ public abstract class Resource<T extends Resource<T>> implements Closeable {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clean up any pending resources
 | 
			
		||||
     * Clean up any pending resources.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Note, this may be called multiple times, and so should be thread-safe and
 | 
			
		||||
     * avoid any major side effects.
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ final class Generator<T> {
 | 
			
		||||
    private static final MethodHandle ARG_GET_OBJECT, ARG_GET_ENUM, ARG_OPT_ENUM, ARG_GET_STRING_COERCED, ARG_GET_BYTES_COERCED;
 | 
			
		||||
 | 
			
		||||
    private record ArgMethods(MethodHandle get, MethodHandle opt) {
 | 
			
		||||
        public static ArgMethods of(Class<?> type, String name) throws ReflectiveOperationException {
 | 
			
		||||
        private static ArgMethods of(Class<?> type, String name) throws ReflectiveOperationException {
 | 
			
		||||
            return new ArgMethods(
 | 
			
		||||
                LOOKUP.findVirtual(IArguments.class, "get" + name, MethodType.methodType(type, int.class)),
 | 
			
		||||
                LOOKUP.findVirtual(IArguments.class, "opt" + name, MethodType.methodType(Optional.class, int.class))
 | 
			
		||||
@@ -73,6 +73,22 @@ final class Generator<T> {
 | 
			
		||||
            addArgType(argMethodMap, Map.class, "Table");
 | 
			
		||||
            addArgType(argMethodMap, String.class, "String");
 | 
			
		||||
            addArgType(argMethodMap, ByteBuffer.class, "Bytes");
 | 
			
		||||
            argMethodMap.put(LuaTable.class, new ArgMethods(
 | 
			
		||||
                // i -> new ObjectLuaTable(getTable(i))
 | 
			
		||||
                MethodHandles.filterReturnValue(
 | 
			
		||||
                    LOOKUP.findVirtual(IArguments.class, "getTable", MethodType.methodType(Map.class, int.class)),
 | 
			
		||||
                    LOOKUP.findConstructor(ObjectLuaTable.class, MethodType.methodType(void.class, Map.class))
 | 
			
		||||
                        .asType(MethodType.methodType(LuaTable.class, Map.class))
 | 
			
		||||
                ),
 | 
			
		||||
                // i -> optTable(i).map(ObjectLuaTable::new)
 | 
			
		||||
                MethodHandles.filterReturnValue(
 | 
			
		||||
                    LOOKUP.findVirtual(IArguments.class, "optTable", MethodType.methodType(Optional.class, int.class)),
 | 
			
		||||
                    MethodHandles.insertArguments(
 | 
			
		||||
                        LOOKUP.findVirtual(Optional.class, "map", MethodType.methodType(Optional.class, Function.class)),
 | 
			
		||||
                        1, (Function<Map<?, ?>, LuaTable<?, ?>>) ObjectLuaTable::new
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
            ));
 | 
			
		||||
            argMethods = Map.copyOf(argMethodMap);
 | 
			
		||||
 | 
			
		||||
            ARG_TABLE_UNSAFE = ArgMethods.of(LuaTable.class, "TableUnsafe");
 | 
			
		||||
@@ -335,10 +351,9 @@ final class Generator<T> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static @Nullable ArgMethods getArgMethods(Class<?> type, boolean unsafe) {
 | 
			
		||||
        var getter = argMethods.get(type);
 | 
			
		||||
        if (getter != null) return getter;
 | 
			
		||||
        if (type == LuaTable.class && unsafe) return ARG_TABLE_UNSAFE;
 | 
			
		||||
        return null;
 | 
			
		||||
 | 
			
		||||
        return argMethods.get(type);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -740,7 +740,7 @@ public final class ComputerThread implements ComputerScheduler {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final class ExecutorImpl implements Executor {
 | 
			
		||||
        public static final AtomicReferenceFieldUpdater<ExecutorImpl, ExecutorState> STATE = AtomicReferenceFieldUpdater.newUpdater(
 | 
			
		||||
        private static final AtomicReferenceFieldUpdater<ExecutorImpl, ExecutorState> STATE = AtomicReferenceFieldUpdater.newUpdater(
 | 
			
		||||
            ExecutorImpl.class, ExecutorState.class, "$state"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,13 +20,8 @@ import static org.squiddev.cobalt.Constants.NAME;
 | 
			
		||||
final class VarargArguments implements IArguments {
 | 
			
		||||
    private static final Logger LOG = LoggerFactory.getLogger(VarargArguments.class);
 | 
			
		||||
 | 
			
		||||
    private static final VarargArguments EMPTY = new VarargArguments(Constants.NONE);
 | 
			
		||||
    private static boolean reportedIllegalGet;
 | 
			
		||||
 | 
			
		||||
    static {
 | 
			
		||||
        EMPTY.escapes = EMPTY.closed = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final Varargs varargs;
 | 
			
		||||
 | 
			
		||||
    private volatile boolean closed;
 | 
			
		||||
@@ -51,7 +46,7 @@ final class VarargArguments implements IArguments {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static VarargArguments of(Varargs values) {
 | 
			
		||||
        return values == Constants.NONE ? EMPTY : new VarargArguments(values);
 | 
			
		||||
        return new VarargArguments(values);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    boolean isClosed() {
 | 
			
		||||
@@ -138,9 +133,7 @@ final class VarargArguments implements IArguments {
 | 
			
		||||
        if (count < 0) throw new IllegalStateException("count cannot be negative");
 | 
			
		||||
        if (count == 0) return this;
 | 
			
		||||
 | 
			
		||||
        var newArgs = varargs.subargs(count + 1);
 | 
			
		||||
        if (newArgs == Constants.NONE) return EMPTY;
 | 
			
		||||
        return new VarargArguments(newArgs, this, count);
 | 
			
		||||
        return new VarargArguments(varargs.subargs(count + 1), this, count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -616,7 +616,8 @@ do
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    local function parse_ident(str, pos)
 | 
			
		||||
        local _, last, val = find(str, '^([%a][%w_]*)', pos)
 | 
			
		||||
        local _, last, val = find(str, '^([%w_+.-]+)', pos)
 | 
			
		||||
        if not last then error_at(pos, "Expected object key") end
 | 
			
		||||
        return val, last + 1
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -864,7 +865,7 @@ values do not serialise cleanly into JSON.
 | 
			
		||||
   A consequence of this is that an empty table will always be serialised to an object,
 | 
			
		||||
   not an array. [`textutils.empty_json_array`] may be used to express an empty array.
 | 
			
		||||
 | 
			
		||||
 - Lua strings are an a sequence of raw bytes, and do not have any specific encoding.
 | 
			
		||||
 - Lua strings are a sequence of raw bytes, and do not have any specific encoding.
 | 
			
		||||
   However, JSON strings must be valid unicode. By default, non-ASCII characters in a
 | 
			
		||||
   string are serialised to their unicode code point (for instance, `"\xfe"` is
 | 
			
		||||
   converted to `"\u00fe"`). The `unicode_strings` option may be set to treat all input
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,19 @@
 | 
			
		||||
# New features in CC: Tweaked 1.116.2
 | 
			
		||||
 | 
			
		||||
Several bug fixes:
 | 
			
		||||
* Update Create compatibility to Create Fabric 6.0.
 | 
			
		||||
* Various documentation fixes (Zirunis).
 | 
			
		||||
* Fix crash with Inventorio.
 | 
			
		||||
* Various fixes to SNBT parsing.
 | 
			
		||||
* Fix Regex DDoS in string pattern matching.
 | 
			
		||||
 | 
			
		||||
# New features in CC: Tweaked 1.116.1
 | 
			
		||||
 | 
			
		||||
* Update translations.
 | 
			
		||||
 | 
			
		||||
One bug fix:
 | 
			
		||||
* Fix NPE when mcfunction files contain CC commands.
 | 
			
		||||
 | 
			
		||||
# New features in CC: Tweaked 1.116.0
 | 
			
		||||
 | 
			
		||||
* Add `turtle.getEquippedLeft()` and `turtle.getEquippedRight()`.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,10 @@
 | 
			
		||||
New features in CC: Tweaked 1.116.0
 | 
			
		||||
 | 
			
		||||
* Add `turtle.getEquippedLeft()` and `turtle.getEquippedRight()`.
 | 
			
		||||
* Add item tags for floppy disks and pocket computers.
 | 
			
		||||
* Support multi-line strings and comments in `edit`.
 | 
			
		||||
* Computer and pocket computer terminal sizes can be set with the `computercraft:terminal_size` component.
 | 
			
		||||
* Border and sidebar textures now use vanilla's nine-sliced format.
 | 
			
		||||
New features in CC: Tweaked 1.116.2
 | 
			
		||||
 | 
			
		||||
Several bug fixes:
 | 
			
		||||
* Ignore shader compilation errors when running with Pojav.
 | 
			
		||||
* Fix several issues with character input.
 | 
			
		||||
* Fix pocket computer dyes being lost when equipping/unequipping upgrades.
 | 
			
		||||
* Fix superflous warnings from allocation tracking.
 | 
			
		||||
* Fix `__lt`/`__le` not working on heterogeneous types.
 | 
			
		||||
* Many documentation fixes (Lemmmy, matematikaadit, McJack12).
 | 
			
		||||
* Fix `0` being treated as a valid colour in `window` and `colour.toBlit`.
 | 
			
		||||
* Fix out-of-bounds when pasting too lon text.
 | 
			
		||||
* Fix syntax highlighting of string escapes (LorneHyde).
 | 
			
		||||
* Fix sidebar texture of advanced computers being offset.
 | 
			
		||||
* Update Create compatibility to Create Fabric 6.0.
 | 
			
		||||
* Various documentation fixes (Zirunis).
 | 
			
		||||
* Fix crash with Inventorio.
 | 
			
		||||
* Various fixes to SNBT parsing.
 | 
			
		||||
* Fix Regex DDoS in string pattern matching.
 | 
			
		||||
 | 
			
		||||
Type "help changelog" to see the full version history.
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,8 @@ 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 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`].
 | 
			
		||||
Typically DFPWM audio is read from [the filesystem][`fs.ReadHandle`] or [a web request][`http.Response`] as a string,
 | 
			
		||||
and converted to a format suitable for [`speaker.playAudio`].
 | 
			
		||||
 | 
			
		||||
## Encoding and decoding files
 | 
			
		||||
This module exposes two key functions, [`make_decoder`] and [`make_encoder`], which construct a new decoder or encoder.
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import dan200.computercraft.core.computer.ComputerSide;
 | 
			
		||||
import dan200.computercraft.core.methods.LuaMethod;
 | 
			
		||||
import dan200.computercraft.core.methods.NamedMethod;
 | 
			
		||||
import org.hamcrest.Matcher;
 | 
			
		||||
import org.jspecify.annotations.Nullable;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.objectweb.asm.ClassReader;
 | 
			
		||||
import org.objectweb.asm.ClassVisitor;
 | 
			
		||||
@@ -123,6 +124,17 @@ public class GeneratorTest {
 | 
			
		||||
        assertThrows(LuaException.class, () -> apply(methods, new EnumMethods(), "getEnum", "not as side"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testLuaTable() throws LuaException {
 | 
			
		||||
        var methods = GENERATOR.getMethods(TableMethods.class);
 | 
			
		||||
        assertThat(methods, containsInAnyOrder(named("getTable"), named("optTable")));
 | 
			
		||||
 | 
			
		||||
        assertThat(apply(methods, new TableMethods(), "getTable", Map.of("x", "y")), one(is(Map.of("x", "y"))));
 | 
			
		||||
        assertThat(apply(methods, new TableMethods(), "optTable", Map.of("x", "y")), one(is(Map.of("x", "y"))));
 | 
			
		||||
        assertThat(apply(methods, new TableMethods(), "optTable"), one(nullValue()));
 | 
			
		||||
        assertThrows(LuaException.class, () -> apply(methods, new TableMethods(), "getTable", "not a table"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testMainThread() throws LuaException {
 | 
			
		||||
        var methods = GENERATOR.getMethods(MainThread.class);
 | 
			
		||||
@@ -277,6 +289,18 @@ public class GeneratorTest {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class TableMethods {
 | 
			
		||||
        @LuaFunction
 | 
			
		||||
        public final LuaTable<?, ?> getTable(LuaTable<?, ?> table) {
 | 
			
		||||
            return table;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @LuaFunction
 | 
			
		||||
        public final @Nullable LuaTable<?, ?> optTable(Optional<LuaTable<?, ?>> table) {
 | 
			
		||||
            return table.orElse(null);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class MainThread {
 | 
			
		||||
        @LuaFunction(mainThread = true)
 | 
			
		||||
        public final void go() {
 | 
			
		||||
@@ -288,10 +312,6 @@ public class GeneratorTest {
 | 
			
		||||
        public final void withUnsafe(LuaTable<?, ?> table) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @LuaFunction
 | 
			
		||||
        public final void withoutUnsafe(LuaTable<?, ?> table) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @LuaFunction(unsafe = true, mainThread = true)
 | 
			
		||||
        public final void invalid(LuaTable<?, ?> table) {
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -246,11 +246,25 @@ describe("The textutils library", function()
 | 
			
		||||
        describe("parses using NBT-style syntax", function()
 | 
			
		||||
            local function exp(x)
 | 
			
		||||
                local res, err = textutils.unserializeJSON(x, { nbt_style = true })
 | 
			
		||||
                if not res then error(err, 2) end
 | 
			
		||||
                if not res then fail(err) end
 | 
			
		||||
                return expect(res)
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            local function exp_err(x)
 | 
			
		||||
                local res, err = textutils.unserializeJSON(x, { nbt_style = true })
 | 
			
		||||
                if res ~= nil then
 | 
			
		||||
                    fail(("Expected %q not to parse, but returned %s"):format(x, textutils.serialise(res)))
 | 
			
		||||
                end
 | 
			
		||||
                return expect(err)
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            it("basic objects", function()
 | 
			
		||||
                exp([[{ a: 1, b:2 }]]):same { a = 1, b = 2 }
 | 
			
		||||
                exp("{ a: 1, b:2 }"):same { a = 1, b = 2 }
 | 
			
		||||
                exp("{0+_-.aA: 1}"):same { ["0+_-.aA"] = 1 }
 | 
			
		||||
                exp("{}"):same {}
 | 
			
		||||
 | 
			
		||||
                exp_err("{: 123}"):eq("Malformed JSON at position 2: Expected object key")
 | 
			
		||||
                exp_err("{#: 123}"):eq("Malformed JSON at position 2: Expected object key")
 | 
			
		||||
            end)
 | 
			
		||||
 | 
			
		||||
            it("suffixed numbers", function()
 | 
			
		||||
@@ -258,9 +272,21 @@ describe("The textutils library", function()
 | 
			
		||||
                exp("1.1d"):eq(1.1)
 | 
			
		||||
            end)
 | 
			
		||||
 | 
			
		||||
            it("strings", function()
 | 
			
		||||
                exp("'123'"):eq("123")
 | 
			
		||||
                exp("\"123\""):eq("123")
 | 
			
		||||
            describe("strings", function()
 | 
			
		||||
                it("empty quoted strings", function()
 | 
			
		||||
                    exp("''"):eq("")
 | 
			
		||||
                    exp("\"\""):eq("")
 | 
			
		||||
                end)
 | 
			
		||||
 | 
			
		||||
                pending("unquoted strings", function()
 | 
			
		||||
                    exp("hello"):eq("hello")
 | 
			
		||||
                    exp("0+_-.aA"):eq("0+_-.aA")
 | 
			
		||||
                end)
 | 
			
		||||
 | 
			
		||||
                it("quoted strings", function()
 | 
			
		||||
                    exp("'123'"):eq("123")
 | 
			
		||||
                    exp("\"123\""):eq("123")
 | 
			
		||||
                end)
 | 
			
		||||
            end)
 | 
			
		||||
 | 
			
		||||
            it("typed arrays", function()
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import org.junit.jupiter.api.DisplayNameGeneration;
 | 
			
		||||
import org.junit.jupiter.api.DisplayNameGenerator;
 | 
			
		||||
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A {@link DisplayNameGenerator} which replaces underscores with spaces. This is equivalent to
 | 
			
		||||
@@ -17,7 +18,7 @@ import java.lang.reflect.Method;
 | 
			
		||||
 */
 | 
			
		||||
public class ReplaceUnderscoresDisplayNameGenerator extends DisplayNameGenerator.ReplaceUnderscores {
 | 
			
		||||
    @Override
 | 
			
		||||
    public String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {
 | 
			
		||||
    public String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass, Method testMethod) {
 | 
			
		||||
        return testMethod.getName().replace('_', ' ');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,6 @@ configurations {
 | 
			
		||||
    val localImplementation by registering {
 | 
			
		||||
        isCanBeResolved = false
 | 
			
		||||
        isCanBeConsumed = false
 | 
			
		||||
        isVisible = false
 | 
			
		||||
    }
 | 
			
		||||
    compileClasspath { extendsFrom(localImplementation.get()) }
 | 
			
		||||
    runtimeClasspath { extendsFrom(localImplementation.get()) }
 | 
			
		||||
@@ -118,7 +117,6 @@ dependencies {
 | 
			
		||||
 | 
			
		||||
loom {
 | 
			
		||||
    accessWidenerPath = project(":common").file("src/main/resources/computercraft.accesswidener")
 | 
			
		||||
    mixin.useLegacyMixinAp = false
 | 
			
		||||
 | 
			
		||||
    mods {
 | 
			
		||||
        register("computercraft") {
 | 
			
		||||
 
 | 
			
		||||
@@ -39,11 +39,11 @@ public class FabricDataProviders implements DataGeneratorEntrypoint {
 | 
			
		||||
    private record PlatformGeneratorsImpl(
 | 
			
		||||
        FabricDataGenerator.Pack generator, CompletableFuture<HolderLookup.Provider> registries
 | 
			
		||||
    ) implements DataProviders.GeneratorSink {
 | 
			
		||||
        public <T extends DataProvider> T addWithFabricOutput(FabricDataGenerator.Pack.Factory<T> factory) {
 | 
			
		||||
        private <T extends DataProvider> T addWithFabricOutput(FabricDataGenerator.Pack.Factory<T> factory) {
 | 
			
		||||
            return generator.addProvider((FabricDataOutput p) -> new PrettyDataProvider<>(factory.create(p))).provider();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public <T extends DataProvider> T addWithRegistries(FabricDataGenerator.Pack.RegistryDependentFactory<T> factory) {
 | 
			
		||||
        private <T extends DataProvider> T addWithRegistries(FabricDataGenerator.Pack.RegistryDependentFactory<T> factory) {
 | 
			
		||||
            return generator.addProvider((r, p) -> new PrettyDataProvider<>(factory.create(r, p))).provider();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -125,12 +125,12 @@ public class ComputerCraft {
 | 
			
		||||
        ComputerCraftAPI.registerGenericSource(new InventoryMethods());
 | 
			
		||||
 | 
			
		||||
        Peripherals.addGenericLookup(InventoryMethods::extractContainer);
 | 
			
		||||
 | 
			
		||||
        if (FabricLoader.getInstance().isModLoaded(CreateIntegration.ID)) CreateIntegration.setup();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static <B extends FriendlyByteBuf, T extends CustomPacketPayload> void registerPayloadType(PayloadTypeRegistry<B> registry, CustomPacketPayload.TypeAndCodec<B, T> type) {
 | 
			
		||||
        registry.register(type.type(), type.codec());
 | 
			
		||||
 | 
			
		||||
        if (FabricLoader.getInstance().isModLoaded(CreateIntegration.ID)) CreateIntegration.setup();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private record ReloadListener(String name, PreparableReloadListener listener)
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,8 @@
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.integration;
 | 
			
		||||
 | 
			
		||||
import com.simibubi.create.content.contraptions.BlockMovementChecks;
 | 
			
		||||
import com.simibubi.create.content.contraptions.BlockMovementChecks.CheckResult;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -147,7 +147,6 @@ configurations {
 | 
			
		||||
    val localImplementation by registering {
 | 
			
		||||
        isCanBeResolved = false
 | 
			
		||||
        isCanBeConsumed = false
 | 
			
		||||
        isVisible = false
 | 
			
		||||
    }
 | 
			
		||||
    compileClasspath { extendsFrom(localImplementation.get()) }
 | 
			
		||||
    runtimeClasspath { extendsFrom(localImplementation.get()) }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,8 @@
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
import cc.tweaked.gradle.CCTweakedPlugin
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    kotlin("jvm")
 | 
			
		||||
    id("cc-tweaked.java-convention")
 | 
			
		||||
@@ -29,6 +31,8 @@ dependencies {
 | 
			
		||||
    testRuntimeOnly(libs.bundles.testRuntime)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
kotlin.compilerOptions.jvmTarget = CCTweakedPlugin.KOTLIN_TARGET
 | 
			
		||||
 | 
			
		||||
tasks.test {
 | 
			
		||||
    jvmArgs(
 | 
			
		||||
        "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ import dan200.computercraft.core.terminal.Terminal;
 | 
			
		||||
import dan200.computercraft.core.terminal.TextBuffer;
 | 
			
		||||
import dan200.computercraft.core.util.Colour;
 | 
			
		||||
import org.apache.commons.cli.*;
 | 
			
		||||
import org.apache.commons.cli.help.HelpFormatter;
 | 
			
		||||
import org.jetbrains.annotations.Contract;
 | 
			
		||||
import org.jspecify.annotations.Nullable;
 | 
			
		||||
import org.lwjgl.glfw.GLFW;
 | 
			
		||||
@@ -31,9 +32,8 @@ import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.io.UncheckedIOException;
 | 
			
		||||
import java.nio.ByteBuffer;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.nio.file.InvalidPathException;
 | 
			
		||||
import java.nio.file.Path;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
@@ -70,10 +70,10 @@ public class Main {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private record TermSize(int width, int height) {
 | 
			
		||||
        public static final TermSize DEFAULT = new TermSize(51, 19);
 | 
			
		||||
        public static final Pattern PATTERN = Pattern.compile("^(\\d+)x(\\d+)$");
 | 
			
		||||
        private static final TermSize DEFAULT = new TermSize(51, 19);
 | 
			
		||||
        private static final Pattern PATTERN = Pattern.compile("^(\\d+)x(\\d+)$");
 | 
			
		||||
 | 
			
		||||
        public static TermSize parse(String value) throws ParseException {
 | 
			
		||||
        private static TermSize parse(String value) throws ParseException {
 | 
			
		||||
            var matcher = TermSize.PATTERN.matcher(value);
 | 
			
		||||
            if (!matcher.matches()) throw new ParseException("'" + value + "' is not a valid terminal size.");
 | 
			
		||||
 | 
			
		||||
@@ -82,9 +82,9 @@ public class Main {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private record MountPaths(Path src, String dest) {
 | 
			
		||||
        public static final Pattern PATTERN = Pattern.compile("^([^:]+):([^:]+)$");
 | 
			
		||||
        private static final Pattern PATTERN = Pattern.compile("^([^:]+):([^:]+)$");
 | 
			
		||||
 | 
			
		||||
        public static MountPaths parse(String value) throws ParseException {
 | 
			
		||||
        private static MountPaths parse(String value) throws ParseException {
 | 
			
		||||
            var matcher = MountPaths.PATTERN.matcher(value);
 | 
			
		||||
            if (!matcher.matches()) throw new ParseException("'" + value + "' is not a mount spec.");
 | 
			
		||||
 | 
			
		||||
@@ -115,26 +115,26 @@ public class Main {
 | 
			
		||||
        Option resourceOpt, computerOpt, termSizeOpt, allowLocalDomainsOpt, helpOpt, mountOpt, mountRoOpt;
 | 
			
		||||
        options.addOption(resourceOpt = Option.builder("r").argName("PATH").longOpt("resources").hasArg()
 | 
			
		||||
            .desc("The path to the resources directory")
 | 
			
		||||
            .build());
 | 
			
		||||
            .get());
 | 
			
		||||
        options.addOption(computerOpt = Option.builder("c").argName("PATH").longOpt("computer").hasArg()
 | 
			
		||||
            .desc("The root directory of the computer. Defaults to a temporary directory.")
 | 
			
		||||
            .build());
 | 
			
		||||
            .get());
 | 
			
		||||
        options.addOption(termSizeOpt = Option.builder("t").argName("WIDTHxHEIGHT").longOpt("term-size").hasArg()
 | 
			
		||||
            .desc("The size of the terminal, defaults to 51x19.")
 | 
			
		||||
            .build());
 | 
			
		||||
            .get());
 | 
			
		||||
        options.addOption(allowLocalDomainsOpt = Option.builder("L").longOpt("allow-local-domains")
 | 
			
		||||
            .desc("Allow accessing local domains with the HTTP API.")
 | 
			
		||||
            .build());
 | 
			
		||||
            .get());
 | 
			
		||||
        options.addOption(mountOpt = Option.builder().longOpt("mount").hasArg().argName("SRC:DEST")
 | 
			
		||||
            .desc("Mount a folder SRC at directory DEST on the computer.")
 | 
			
		||||
            .build());
 | 
			
		||||
            .get());
 | 
			
		||||
        options.addOption(mountRoOpt = Option.builder().longOpt("mount-ro").hasArg().argName("SRC:DEST")
 | 
			
		||||
            .desc("Mount a read-only folder SRC at directory DEST on the computer.")
 | 
			
		||||
            .build());
 | 
			
		||||
            .get());
 | 
			
		||||
 | 
			
		||||
        options.addOption(helpOpt = Option.builder("h").longOpt("help")
 | 
			
		||||
            .desc("Print help message")
 | 
			
		||||
            .build());
 | 
			
		||||
            .get());
 | 
			
		||||
 | 
			
		||||
        Path resourcesDirectory;
 | 
			
		||||
        Path computerDirectory;
 | 
			
		||||
@@ -144,7 +144,11 @@ public class Main {
 | 
			
		||||
        try {
 | 
			
		||||
            var cli = new DefaultParser().parse(options, args);
 | 
			
		||||
            if (cli.hasOption(helpOpt)) {
 | 
			
		||||
                new HelpFormatter().printHelp("standalone.jar", options, true);
 | 
			
		||||
                try {
 | 
			
		||||
                    HelpFormatter.builder().get().printHelp("standalone.jar", "", options, "", true);
 | 
			
		||||
                } catch (IOException e) {
 | 
			
		||||
                    throw new UncheckedIOException(e);
 | 
			
		||||
                }
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (!cli.hasOption(resourceOpt)) throw new ParseException("--resources directory is required");
 | 
			
		||||
@@ -157,10 +161,7 @@ public class Main {
 | 
			
		||||
            readOnlyMounts = getParsedOptionValues(cli, mountRoOpt, MountPaths::parse);
 | 
			
		||||
        } catch (ParseException e) {
 | 
			
		||||
            System.err.println(e.getLocalizedMessage());
 | 
			
		||||
 | 
			
		||||
            var writer = new PrintWriter(System.err, false, StandardCharsets.UTF_8);
 | 
			
		||||
            new HelpFormatter().printUsage(writer, HelpFormatter.DEFAULT_WIDTH, "standalone.jar", options);
 | 
			
		||||
            writer.flush();
 | 
			
		||||
            System.err.println(HelpFormatter.builder().get().toSyntaxOptions(options));
 | 
			
		||||
 | 
			
		||||
            System.exit(1);
 | 
			
		||||
            return;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import org.jspecify.annotations.Nullable;
 | 
			
		||||
import org.objectweb.asm.ClassReader;
 | 
			
		||||
import org.objectweb.asm.ClassVisitor;
 | 
			
		||||
import org.objectweb.asm.ClassWriter;
 | 
			
		||||
import org.objectweb.asm.Opcodes;
 | 
			
		||||
import org.objectweb.asm.commons.ClassRemapper;
 | 
			
		||||
import org.objectweb.asm.commons.Remapper;
 | 
			
		||||
 | 
			
		||||
@@ -17,6 +18,8 @@ import java.io.InputStream;
 | 
			
		||||
import java.io.UncheckedIOException;
 | 
			
		||||
import java.net.MalformedURLException;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.net.URLConnection;
 | 
			
		||||
import java.net.URLStreamHandler;
 | 
			
		||||
import java.nio.file.Files;
 | 
			
		||||
import java.nio.file.Path;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
@@ -36,7 +39,7 @@ public class TransformingClassLoader extends ClassLoader {
 | 
			
		||||
    private final Map<String, Path> remappedResources = new HashMap<>();
 | 
			
		||||
    private final List<BiFunction<String, ClassVisitor, ClassVisitor>> transformers = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    private final Remapper remapper = new Remapper() {
 | 
			
		||||
    private final Remapper remapper = new Remapper(Opcodes.ASM9) {
 | 
			
		||||
        @Override
 | 
			
		||||
        public String map(String internalName) {
 | 
			
		||||
            return remappedClasses.getOrDefault(internalName, internalName);
 | 
			
		||||
@@ -112,11 +115,17 @@ public class TransformingClassLoader extends ClassLoader {
 | 
			
		||||
            return super.getResourceAsStream(name);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var bytes = getTransformed(path);
 | 
			
		||||
        this.lastFile = new TransformedClass(name, bytes);
 | 
			
		||||
        return new ByteArrayInputStream(bytes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private byte[] getTransformed(Path path) {
 | 
			
		||||
        ClassReader reader;
 | 
			
		||||
        try (var stream = Files.newInputStream(path)) {
 | 
			
		||||
            reader = new ClassReader(stream);
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            throw new UncheckedIOException("Failed reading " + name, e);
 | 
			
		||||
            throw new UncheckedIOException("Failed reading " + path, e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var writer = new ClassWriter(reader, 0);
 | 
			
		||||
@@ -125,9 +134,7 @@ public class TransformingClassLoader extends ClassLoader {
 | 
			
		||||
        for (var transformer : transformers) sink = transformer.apply(className, sink);
 | 
			
		||||
        reader.accept(sink, 0);
 | 
			
		||||
 | 
			
		||||
        var bytes = writer.toByteArray();
 | 
			
		||||
        this.lastFile = new TransformedClass(name, bytes);
 | 
			
		||||
        return new ByteArrayInputStream(bytes);
 | 
			
		||||
        return writer.toByteArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -142,7 +149,7 @@ public class TransformingClassLoader extends ClassLoader {
 | 
			
		||||
        return new IteratorEnumeration<>(
 | 
			
		||||
            (path == null ? classpath.stream().map(x -> x.resolve(name)) : Stream.of(path))
 | 
			
		||||
                .filter(Files::exists)
 | 
			
		||||
                .map(TransformingClassLoader::toURL)
 | 
			
		||||
                .map(this::toURL)
 | 
			
		||||
                .iterator()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@@ -160,9 +167,12 @@ public class TransformingClassLoader extends ClassLoader {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static URL toURL(Path path) {
 | 
			
		||||
    private URL toURL(Path path) {
 | 
			
		||||
        try {
 | 
			
		||||
            return path.toUri().toURL();
 | 
			
		||||
            var url = path.toUri().toURL();
 | 
			
		||||
 | 
			
		||||
            if (!path.toString().endsWith(".class")) return url;
 | 
			
		||||
            return new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile(), new TransformingHandler(path));
 | 
			
		||||
        } catch (MalformedURLException e) {
 | 
			
		||||
            throw new IllegalStateException("Cannot convert " + path + " to a URL", e);
 | 
			
		||||
        }
 | 
			
		||||
@@ -171,4 +181,31 @@ public class TransformingClassLoader extends ClassLoader {
 | 
			
		||||
    @SuppressWarnings("ArrayRecordComponent")
 | 
			
		||||
    private record TransformedClass(String name, byte[] contents) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final class TransformingHandler extends URLStreamHandler {
 | 
			
		||||
        private final Path path;
 | 
			
		||||
 | 
			
		||||
        private TransformingHandler(Path path) {
 | 
			
		||||
            this.path = path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected URLConnection openConnection(URL u) {
 | 
			
		||||
            return new URLConnection(u) {
 | 
			
		||||
                private @Nullable InputStream stream;
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void connect() {
 | 
			
		||||
                    if (stream == null) stream = new ByteArrayInputStream(getTransformed(path));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public InputStream getInputStream() {
 | 
			
		||||
                    connect();
 | 
			
		||||
                    if (stream == null) throw new NullPointerException("Stream cannot be null");
 | 
			
		||||
                    return stream;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -187,9 +187,9 @@ class EmulatedComputer implements ComputerEnvironment, ComputerHandle {
 | 
			
		||||
    public void addFile(String path, JSObject contents) {
 | 
			
		||||
        byte[] bytes;
 | 
			
		||||
        if (JavascriptConv.isArrayBuffer(contents)) {
 | 
			
		||||
            bytes = bytesOfBuffer(contents.cast());
 | 
			
		||||
            bytes = bytesOfBuffer((ArrayBuffer) contents);
 | 
			
		||||
        } else {
 | 
			
		||||
            JSString string = contents.cast();
 | 
			
		||||
            var string = (JSString) contents;
 | 
			
		||||
            bytes = string.stringValue().getBytes(StandardCharsets.UTF_8);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -117,7 +117,7 @@ public class THttpRequest extends Resource<THttpRequest> {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ArrayBuffer buffer = request.getResponse().cast();
 | 
			
		||||
        var buffer = (ArrayBuffer) request.getResponse();
 | 
			
		||||
        SeekableByteChannel contents = new ArrayByteChannel(JavascriptConv.asByteArray(buffer));
 | 
			
		||||
        var reader = new ReadHandle(contents, binary);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ pluginManagement {
 | 
			
		||||
            content {
 | 
			
		||||
                includeGroup("fabric-loom")
 | 
			
		||||
                includeGroup("net.fabricmc")
 | 
			
		||||
                includeGroup("net.fabricmc.unpick")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user