mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-11-04 07:32:59 +00:00 
			
		
		
		
	Compare commits
	
		
			12 Commits
		
	
	
		
			v1.20.1-1.
			...
			v1.20.1-1.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					39a5e40c92 | ||
| 
						 | 
					763ba51919 | ||
| 
						 | 
					cf6ec8c28f | ||
| 
						 | 
					95d3b646b2 | ||
| 
						 | 
					488f66eead | ||
| 
						 | 
					1f7d245876 | ||
| 
						 | 
					af12b3a0ea | ||
| 
						 | 
					eb3e8ba677 | ||
| 
						 | 
					2043939531 | ||
| 
						 | 
					84a799d27a | ||
| 
						 | 
					fe826f5c9c | ||
| 
						 | 
					f8b7422294 | 
@@ -42,7 +42,6 @@ repositories {
 | 
			
		||||
    url "https://squiddev.cc/maven/"
 | 
			
		||||
    content {
 | 
			
		||||
      includeGroup("cc.tweaked")
 | 
			
		||||
      includeModule("org.squiddev", "Cobalt")
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,9 +10,11 @@ import org.gradle.api.GradleException
 | 
			
		||||
import org.gradle.api.NamedDomainObjectProvider
 | 
			
		||||
import org.gradle.api.Project
 | 
			
		||||
import org.gradle.api.Task
 | 
			
		||||
import org.gradle.api.artifacts.Dependency
 | 
			
		||||
import org.gradle.api.attributes.TestSuiteType
 | 
			
		||||
import org.gradle.api.file.FileSystemOperations
 | 
			
		||||
import org.gradle.api.plugins.JavaPluginExtension
 | 
			
		||||
import org.gradle.api.provider.ListProperty
 | 
			
		||||
import org.gradle.api.provider.Provider
 | 
			
		||||
import org.gradle.api.provider.SetProperty
 | 
			
		||||
import org.gradle.api.reporting.ReportingExtension
 | 
			
		||||
@@ -73,11 +75,17 @@ abstract class CCTweakedExtension(
 | 
			
		||||
     */
 | 
			
		||||
    val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Dependencies excluded from published artifacts.
 | 
			
		||||
     */
 | 
			
		||||
    private val excludedDeps: ListProperty<Dependency> = project.objects.listProperty(Dependency::class.java)
 | 
			
		||||
 | 
			
		||||
    /** All source sets referenced by this project. */
 | 
			
		||||
    val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        sourceDirectories.finalizeValueOnRead()
 | 
			
		||||
        excludedDeps.finalizeValueOnRead()
 | 
			
		||||
        project.afterEvaluate { sourceDirectories.disallowChanges() }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -246,6 +254,20 @@ abstract class CCTweakedExtension(
 | 
			
		||||
        ).resolve().single()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Exclude a dependency from being publisehd in Maven.
 | 
			
		||||
     */
 | 
			
		||||
    fun exclude(dep: Dependency) {
 | 
			
		||||
        excludedDeps.add(dep)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Configure a [MavenDependencySpec].
 | 
			
		||||
     */
 | 
			
		||||
    fun configureExcludes(spec: MavenDependencySpec) {
 | 
			
		||||
        for (dep in excludedDeps.get()) spec.exclude(dep)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
 | 
			
		||||
        private val IGNORED_USERS = setOf(
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,8 @@ package cc.tweaked.gradle
 | 
			
		||||
 | 
			
		||||
import org.gradle.api.artifacts.Dependency
 | 
			
		||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
 | 
			
		||||
import org.gradle.api.artifacts.ProjectDependency
 | 
			
		||||
import org.gradle.api.plugins.BasePluginExtension
 | 
			
		||||
import org.gradle.api.publish.maven.MavenPublication
 | 
			
		||||
import org.gradle.api.specs.Spec
 | 
			
		||||
 | 
			
		||||
@@ -26,8 +28,13 @@ class MavenDependencySpec {
 | 
			
		||||
 | 
			
		||||
    fun exclude(dep: Dependency) {
 | 
			
		||||
        exclude {
 | 
			
		||||
            // We have to cheat a little for project dependencies, as the project name doesn't match the artifact group.
 | 
			
		||||
            val name = when (dep) {
 | 
			
		||||
                is ProjectDependency -> dep.dependencyProject.extensions.getByType(BasePluginExtension::class.java).archivesName.get()
 | 
			
		||||
                else -> dep.name
 | 
			
		||||
            }
 | 
			
		||||
            (dep.group.isNullOrEmpty() || dep.group == it.groupId) &&
 | 
			
		||||
                (dep.name.isNullOrEmpty() || dep.name == it.artifactId) &&
 | 
			
		||||
                (name.isNullOrEmpty() || name == it.artifactId) &&
 | 
			
		||||
                (dep.version.isNullOrEmpty() || dep.version == it.version)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ of the mod should run fine on later versions.
 | 
			
		||||
However, some changes to the underlying game, or CC: Tweaked's own internals may break some programs. This page serves
 | 
			
		||||
as documentation for breaking changes and "gotchas" one should look out for between versions.
 | 
			
		||||
 | 
			
		||||
## CC: Tweaked 1.109.0 {#cct-1.109}
 | 
			
		||||
## CC: Tweaked 1.109.0 to 1.109.1 {#cct-1.109}
 | 
			
		||||
 | 
			
		||||
 - Update to Lua 5.2:
 | 
			
		||||
   - Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
 | 
			
		||||
@@ -31,6 +31,7 @@ as documentation for breaking changes and "gotchas" one should look out for betw
 | 
			
		||||
   - `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
 | 
			
		||||
     environment.
 | 
			
		||||
   - Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
 | 
			
		||||
   - `math.random` now uses Lua 5.4's random number generator.
 | 
			
		||||
 | 
			
		||||
 - File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
 | 
			
		||||
 | 
			
		||||
# Mod properties
 | 
			
		||||
isUnstable=true
 | 
			
		||||
modVersion=1.109.0
 | 
			
		||||
modVersion=1.109.2
 | 
			
		||||
 | 
			
		||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
 | 
			
		||||
mcVersion=1.20.1
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,8 @@ parchmentMc = "1.20.1"
 | 
			
		||||
asm = "9.5"
 | 
			
		||||
autoService = "1.1.1"
 | 
			
		||||
checkerFramework = "3.32.0"
 | 
			
		||||
cobalt = "0.8.0"
 | 
			
		||||
cobalt-next = "0.8.1" # Not a real version, used to constrain the version we accept.
 | 
			
		||||
cobalt = "0.8.1"
 | 
			
		||||
cobalt-next = "0.8.2" # Not a real version, used to constrain the version we accept.
 | 
			
		||||
commonsCli = "1.3.1"
 | 
			
		||||
fastutil = "8.5.9"
 | 
			
		||||
guava = "31.1-jre"
 | 
			
		||||
@@ -51,7 +51,7 @@ jqwik = "1.7.4"
 | 
			
		||||
junit = "5.10.0"
 | 
			
		||||
 | 
			
		||||
# Build tools
 | 
			
		||||
cctJavadoc = "1.8.1"
 | 
			
		||||
cctJavadoc = "1.8.2"
 | 
			
		||||
checkstyle = "10.12.3"
 | 
			
		||||
curseForgeGradle = "1.0.14"
 | 
			
		||||
errorProne-core = "2.21.1"
 | 
			
		||||
@@ -68,7 +68,7 @@ mixinGradle = "0.7.+"
 | 
			
		||||
nullAway = "0.9.9"
 | 
			
		||||
spotless = "6.21.0"
 | 
			
		||||
taskTree = "2.1.1"
 | 
			
		||||
teavm = "0.10.0-SQUID.1"
 | 
			
		||||
teavm = "0.10.0-SQUID.2"
 | 
			
		||||
vanillaGradle = "0.2.1-SNAPSHOT"
 | 
			
		||||
vineflower = "1.11.0"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 | 
			
		||||
(sources
 | 
			
		||||
  /doc/
 | 
			
		||||
  /projects/forge/build/docs/luaJavadoc/
 | 
			
		||||
  /projects/common/build/docs/luaJavadoc/
 | 
			
		||||
  /projects/core/src/main/resources/data/computercraft/lua/bios.lua
 | 
			
		||||
  /projects/core/src/main/resources/data/computercraft/lua/rom/
 | 
			
		||||
  /projects/core/src/test/resources/test-rom
 | 
			
		||||
@@ -36,7 +36,7 @@
 | 
			
		||||
 | 
			
		||||
  (library-path
 | 
			
		||||
    /doc/stub/
 | 
			
		||||
    /projects/forge/build/docs/luaJavadoc/
 | 
			
		||||
    /projects/common/build/docs/luaJavadoc/
 | 
			
		||||
 | 
			
		||||
    /projects/core/src/main/resources/data/computercraft/lua/rom/apis/
 | 
			
		||||
    /projects/core/src/main/resources/data/computercraft/lua/rom/apis/command/
 | 
			
		||||
@@ -88,7 +88,7 @@
 | 
			
		||||
  (/doc/stub/
 | 
			
		||||
   /projects/core/src/main/resources/data/computercraft/lua/bios.lua
 | 
			
		||||
   /projects/core/src/main/resources/data/computercraft/lua/rom/apis/
 | 
			
		||||
   /projects/forge/build/docs/luaJavadoc/)
 | 
			
		||||
   /projects/common/build/docs/luaJavadoc/)
 | 
			
		||||
  (linters -var:unused-global)
 | 
			
		||||
  (lint (allow-toplevel-global true)))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,12 @@
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
import cc.tweaked.gradle.annotationProcessorEverywhere
 | 
			
		||||
import cc.tweaked.gradle.clientClasses
 | 
			
		||||
import cc.tweaked.gradle.commonClasses
 | 
			
		||||
import cc.tweaked.gradle.*
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    id("cc-tweaked.vanilla")
 | 
			
		||||
    id("cc-tweaked.gametest")
 | 
			
		||||
    id("cc-tweaked.illuaminate")
 | 
			
		||||
    id("cc-tweaked.publishing")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -19,6 +18,10 @@ minecraft {
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
configurations {
 | 
			
		||||
    register("cctJavadoc")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    // Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
 | 
			
		||||
    implementation(project(":core"))
 | 
			
		||||
@@ -41,4 +44,53 @@ dependencies {
 | 
			
		||||
    testModImplementation(libs.bundles.kotlin)
 | 
			
		||||
 | 
			
		||||
    testFixturesImplementation(testFixtures(project(":core")))
 | 
			
		||||
 | 
			
		||||
    "cctJavadoc"(libs.cctJavadoc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
illuaminate {
 | 
			
		||||
    version.set(libs.versions.illuaminate)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val luaJavadoc by tasks.registering(Javadoc::class) {
 | 
			
		||||
    description = "Generates documentation for Java-side Lua functions."
 | 
			
		||||
    group = JavaBasePlugin.DOCUMENTATION_GROUP
 | 
			
		||||
 | 
			
		||||
    val sourceSets = listOf(sourceSets.main.get(), project(":core").sourceSets.main.get())
 | 
			
		||||
    for (sourceSet in sourceSets) {
 | 
			
		||||
        source(sourceSet.java)
 | 
			
		||||
        classpath += sourceSet.compileClasspath
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    destinationDir = layout.buildDirectory.dir("docs/luaJavadoc").get().asFile
 | 
			
		||||
 | 
			
		||||
    val options = options as StandardJavadocDocletOptions
 | 
			
		||||
    options.docletpath = configurations["cctJavadoc"].files.toList()
 | 
			
		||||
    options.doclet = "cc.tweaked.javadoc.LuaDoclet"
 | 
			
		||||
    options.addStringOption("project-root", rootProject.file(".").absolutePath)
 | 
			
		||||
    options.noTimestamp(false)
 | 
			
		||||
 | 
			
		||||
    javadocTool.set(
 | 
			
		||||
        javaToolchains.javadocToolFor {
 | 
			
		||||
            languageVersion.set(cc.tweaked.gradle.CCTweakedPlugin.JAVA_VERSION)
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val lintLua by tasks.registering(IlluaminateExec::class) {
 | 
			
		||||
    group = JavaBasePlugin.VERIFICATION_GROUP
 | 
			
		||||
    description = "Lint Lua (and Lua docs) with illuaminate"
 | 
			
		||||
 | 
			
		||||
    // Config files
 | 
			
		||||
    inputs.file(rootProject.file("illuaminate.sexp")).withPropertyName("illuaminate.sexp")
 | 
			
		||||
    // Sources
 | 
			
		||||
    inputs.files(rootProject.fileTree("doc")).withPropertyName("docs")
 | 
			
		||||
    inputs.files(project(":core").fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
 | 
			
		||||
    inputs.files(luaJavadoc)
 | 
			
		||||
 | 
			
		||||
    args = listOf("lint")
 | 
			
		||||
    workingDir = rootProject.projectDir
 | 
			
		||||
 | 
			
		||||
    doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
 | 
			
		||||
    doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ import dan200.computercraft.shared.network.server.UploadFileMessage;
 | 
			
		||||
import net.minecraft.ChatFormatting;
 | 
			
		||||
import net.minecraft.Util;
 | 
			
		||||
import net.minecraft.client.gui.GuiGraphics;
 | 
			
		||||
import net.minecraft.client.gui.components.events.GuiEventListener;
 | 
			
		||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
 | 
			
		||||
import net.minecraft.network.chat.Component;
 | 
			
		||||
import net.minecraft.world.entity.player.Inventory;
 | 
			
		||||
@@ -144,6 +145,11 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
 | 
			
		||||
            || super.mouseDragged(x, y, button, deltaX, deltaY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setFocused(@Nullable GuiEventListener listener) {
 | 
			
		||||
        // Don't clear and re-focus if we're already focused.
 | 
			
		||||
        if (listener != getFocused()) super.setFocused(listener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
 | 
			
		||||
 
 | 
			
		||||
@@ -246,7 +246,7 @@ public class TerminalWidget extends AbstractWidget {
 | 
			
		||||
            keysDown.clear();
 | 
			
		||||
 | 
			
		||||
            // When blurring, we should make the last mouse button go up
 | 
			
		||||
            if (lastMouseButton > 0) {
 | 
			
		||||
            if (lastMouseButton >= 0) {
 | 
			
		||||
                computer.mouseUp(lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1);
 | 
			
		||||
                lastMouseButton = -1;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,10 @@ class TagProvider {
 | 
			
		||||
 | 
			
		||||
        tags.tag(ComputerCraftTags.Blocks.TURTLE_SWORD_BREAKABLE).addTag(BlockTags.WOOL).add(Blocks.COBWEB);
 | 
			
		||||
 | 
			
		||||
        tags.tag(ComputerCraftTags.Blocks.TURTLE_CAN_USE).addTag(BlockTags.CAULDRONS).addTag(BlockTags.BEEHIVES);
 | 
			
		||||
        tags.tag(ComputerCraftTags.Blocks.TURTLE_CAN_USE)
 | 
			
		||||
            .addTag(BlockTags.BEEHIVES)
 | 
			
		||||
            .addTag(BlockTags.CAULDRONS)
 | 
			
		||||
            .add(Blocks.COMPOSTER);
 | 
			
		||||
 | 
			
		||||
        // Make all blocks aside from command computer mineable.
 | 
			
		||||
        tags.tag(BlockTags.MINEABLE_WITH_PICKAXE).add(
 | 
			
		||||
 
 | 
			
		||||
@@ -46,12 +46,14 @@ public final class GlobalMetrics {
 | 
			
		||||
     * Add a new global metrics observer. This will receive metrics data for all computers.
 | 
			
		||||
     *
 | 
			
		||||
     * @param tracker The observer to add.
 | 
			
		||||
     * @return Whether the observer was added. {@code false} if the observer was already registered.
 | 
			
		||||
     */
 | 
			
		||||
    public void addObserver(ComputerMetricsObserver tracker) {
 | 
			
		||||
    public boolean addObserver(ComputerMetricsObserver tracker) {
 | 
			
		||||
        synchronized (lock) {
 | 
			
		||||
            if (trackers.contains(tracker)) return;
 | 
			
		||||
            if (trackers.contains(tracker)) return false;
 | 
			
		||||
            trackers.add(tracker);
 | 
			
		||||
            enabled = true;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -59,11 +61,13 @@ public final class GlobalMetrics {
 | 
			
		||||
     * Remove a previously-registered global metrics observer.
 | 
			
		||||
     *
 | 
			
		||||
     * @param tracker The observer to add.
 | 
			
		||||
     * @return Whether the observer was removed. {@code false} if the observer was not registered.
 | 
			
		||||
     */
 | 
			
		||||
    public void removeObserver(ComputerMetricsObserver tracker) {
 | 
			
		||||
    public boolean removeObserver(ComputerMetricsObserver tracker) {
 | 
			
		||||
        synchronized (lock) {
 | 
			
		||||
            trackers.remove(tracker);
 | 
			
		||||
            var changed = trackers.remove(tracker);
 | 
			
		||||
            enabled = !trackers.isEmpty();
 | 
			
		||||
            return changed;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import dan200.computercraft.shared.computer.core.ServerComputer;
 | 
			
		||||
import dan200.computercraft.shared.computer.metrics.ComputerMetricsObserver;
 | 
			
		||||
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.concurrent.GuardedBy;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
@@ -21,29 +22,31 @@ import java.util.Map;
 | 
			
		||||
 */
 | 
			
		||||
public class BasicComputerMetricsObserver implements ComputerMetricsObserver {
 | 
			
		||||
    private final GlobalMetrics owner;
 | 
			
		||||
    private boolean tracking = false;
 | 
			
		||||
 | 
			
		||||
    @GuardedBy("this")
 | 
			
		||||
    private final List<ComputerMetrics> timings = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    @GuardedBy("this")
 | 
			
		||||
    private final Map<ServerComputer, ComputerMetrics> timingLookup = new MapMaker().weakKeys().makeMap();
 | 
			
		||||
 | 
			
		||||
    public BasicComputerMetricsObserver(GlobalMetrics owner) {
 | 
			
		||||
        this.owner = owner;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public synchronized void start() {
 | 
			
		||||
        if (!tracking) owner.addObserver(this);
 | 
			
		||||
        tracking = true;
 | 
			
		||||
    public void start() {
 | 
			
		||||
        if (!owner.addObserver(this)) return;
 | 
			
		||||
 | 
			
		||||
        timings.clear();
 | 
			
		||||
        timingLookup.clear();
 | 
			
		||||
        synchronized (this) {
 | 
			
		||||
            timings.clear();
 | 
			
		||||
            timingLookup.clear();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public synchronized boolean stop() {
 | 
			
		||||
        if (!tracking) return false;
 | 
			
		||||
 | 
			
		||||
        owner.removeObserver(this);
 | 
			
		||||
        tracking = false;
 | 
			
		||||
        timingLookup.clear();
 | 
			
		||||
    public boolean stop() {
 | 
			
		||||
        if (!owner.removeObserver(this)) return false;
 | 
			
		||||
        synchronized (this) {
 | 
			
		||||
            timingLookup.clear();
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -57,6 +60,7 @@ public class BasicComputerMetricsObserver implements ComputerMetricsObserver {
 | 
			
		||||
        return new ArrayList<>(timings);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GuardedBy("this")
 | 
			
		||||
    private ComputerMetrics getMetrics(ServerComputer computer) {
 | 
			
		||||
        var existing = timingLookup.get(computer);
 | 
			
		||||
        if (existing != null) return existing;
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,53 @@
 | 
			
		||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.peripheral.generic.methods;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaFunction;
 | 
			
		||||
import dan200.computercraft.api.peripheral.GenericPeripheral;
 | 
			
		||||
import dan200.computercraft.api.peripheral.PeripheralType;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Methods for interacting with blocks which store energy.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * This works with energy storage blocks, as well as generators and machines which consume energy.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * > [!NOTE]
 | 
			
		||||
 * > Due to limitations with Forge's energy API, it is not possible to measure throughput (i.e. FE used/generated per
 | 
			
		||||
 * > tick).
 | 
			
		||||
 *
 | 
			
		||||
 * @param <T> The type for energy storage.
 | 
			
		||||
 * @cc.module energy_storage
 | 
			
		||||
 * @cc.since 1.94.0
 | 
			
		||||
 */
 | 
			
		||||
public abstract class AbstractEnergyMethods<T> implements GenericPeripheral {
 | 
			
		||||
    @Override
 | 
			
		||||
    public final PeripheralType getType() {
 | 
			
		||||
        return PeripheralType.ofAdditional("energy_storage");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public final String id() {
 | 
			
		||||
        return ComputerCraftAPI.MOD_ID + ":energy";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the energy of this block.
 | 
			
		||||
     *
 | 
			
		||||
     * @param energy The current energy storage.
 | 
			
		||||
     * @return The energy stored in this block, in FE.
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract int getEnergy(T energy);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the maximum amount of energy this block can store.
 | 
			
		||||
     *
 | 
			
		||||
     * @param energy The current energy storage.
 | 
			
		||||
     * @return The energy capacity of this block.
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract int getEnergyCapacity(T energy);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,92 @@
 | 
			
		||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: MPL-2.0
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.peripheral.generic.methods;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaException;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaFunction;
 | 
			
		||||
import dan200.computercraft.api.peripheral.GenericPeripheral;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IComputerAccess;
 | 
			
		||||
import dan200.computercraft.api.peripheral.PeripheralType;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Methods for interacting with tanks and other fluid storage blocks.
 | 
			
		||||
 *
 | 
			
		||||
 * @param <T> The type for fluid inventories.
 | 
			
		||||
 * @cc.module fluid_storage
 | 
			
		||||
 * @cc.since 1.94.0
 | 
			
		||||
 */
 | 
			
		||||
public abstract class AbstractFluidMethods<T> implements GenericPeripheral {
 | 
			
		||||
    @Override
 | 
			
		||||
    public final PeripheralType getType() {
 | 
			
		||||
        return PeripheralType.ofAdditional("fluid_storage");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public final String id() {
 | 
			
		||||
        return ComputerCraftAPI.MOD_ID + ":fluid";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all "tanks" in this fluid storage.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Each tank either contains some amount of fluid or is empty. Tanks with fluids inside will return some basic
 | 
			
		||||
     * information about the fluid, including its name and amount.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * The returned table is sparse, and so empty tanks will be `nil` - it is recommended to loop over using [`pairs`]
 | 
			
		||||
     * rather than [`ipairs`].
 | 
			
		||||
     *
 | 
			
		||||
     * @param fluids The current fluid handler.
 | 
			
		||||
     * @return All tanks.
 | 
			
		||||
     * @cc.treturn { (table|nil)... } All tanks in this fluid storage.
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract Map<Integer, Map<String, ?>> tanks(T fluids);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Move a fluid from one fluid container to another connected one.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This allows you to pull fluid in the current fluid container to another container <em>on the same wired
 | 
			
		||||
     * network</em>. Both containers must attached to wired modems which are connected via a cable.
 | 
			
		||||
     *
 | 
			
		||||
     * @param from      Container to move fluid from.
 | 
			
		||||
     * @param computer  The current computer.
 | 
			
		||||
     * @param toName    The name of the peripheral/container to push to. This is the string given to [`peripheral.wrap`],
 | 
			
		||||
     *                  and displayed by the wired modem.
 | 
			
		||||
     * @param limit     The maximum amount of fluid to move.
 | 
			
		||||
     * @param fluidName The fluid to move. If not given, an arbitrary fluid will be chosen.
 | 
			
		||||
     * @return The amount of moved fluid.
 | 
			
		||||
     * @throws LuaException If the peripheral to transfer to doesn't exist or isn't an fluid container.
 | 
			
		||||
     * @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract int pushFluid(
 | 
			
		||||
        T from, IComputerAccess computer, String toName, Optional<Integer> limit, Optional<String> fluidName
 | 
			
		||||
    ) throws LuaException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Move a fluid from a connected fluid container into this oneone.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This allows you to pull fluid in the current fluid container from another container <em>on the same wired
 | 
			
		||||
     * network</em>. Both containers must attached to wired modems which are connected via a cable.
 | 
			
		||||
     *
 | 
			
		||||
     * @param to        Container to move fluid to.
 | 
			
		||||
     * @param computer  The current computer.
 | 
			
		||||
     * @param fromName  The name of the peripheral/container to push to. This is the string given to [`peripheral.wrap`],
 | 
			
		||||
     *                  and displayed by the wired modem.
 | 
			
		||||
     * @param limit     The maximum amount of fluid to move.
 | 
			
		||||
     * @param fluidName The fluid to move. If not given, an arbitrary fluid will be chosen.
 | 
			
		||||
     * @return The amount of moved fluid.
 | 
			
		||||
     * @throws LuaException If the peripheral to transfer to doesn't exist or isn't an fluid container.
 | 
			
		||||
     * @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract int pullFluid(
 | 
			
		||||
        T to, IComputerAccess computer, String fromName, Optional<Integer> limit, Optional<String> fluidName
 | 
			
		||||
    ) throws LuaException;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,197 @@
 | 
			
		||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
 | 
			
		||||
//
 | 
			
		||||
// SPDX-License-Identifier: LicenseRef-CCPL
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.peripheral.generic.methods;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
import dan200.computercraft.api.lua.ILuaContext;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaException;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaFunction;
 | 
			
		||||
import dan200.computercraft.api.peripheral.GenericPeripheral;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IComputerAccess;
 | 
			
		||||
import dan200.computercraft.api.peripheral.PeripheralType;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Methods for interacting with inventories.
 | 
			
		||||
 *
 | 
			
		||||
 * @param <T> The type for inventories.
 | 
			
		||||
 * @cc.module inventory
 | 
			
		||||
 * @cc.since 1.94.0
 | 
			
		||||
 */
 | 
			
		||||
public abstract class AbstractInventoryMethods<T> implements GenericPeripheral {
 | 
			
		||||
    @Override
 | 
			
		||||
    public final PeripheralType getType() {
 | 
			
		||||
        return PeripheralType.ofAdditional("inventory");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public final String id() {
 | 
			
		||||
        return ComputerCraftAPI.MOD_ID + ":inventory";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the size of this inventory.
 | 
			
		||||
     *
 | 
			
		||||
     * @param inventory The current inventory.
 | 
			
		||||
     * @return The number of slots in this inventory.
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract int size(T inventory);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * List all items in this inventory. This returns a table, with an entry for each slot.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Each item in the inventory is represented by a table containing some basic information, much like
 | 
			
		||||
     * {@link dan200.computercraft.shared.turtle.apis.TurtleAPI#getItemDetail(ILuaContext, Optional, Optional)}
 | 
			
		||||
     * includes. More information can be fetched with {@link #getItemDetail}. The table contains the item `name`, the
 | 
			
		||||
     * `count` and an a (potentially nil) hash of the item's `nbt.` This NBT data doesn't contain anything useful, but
 | 
			
		||||
     * allows you to distinguish identical items.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * The returned table is sparse, and so empty slots will be `nil` - it is recommended to loop over using [`pairs`]
 | 
			
		||||
     * rather than [`ipairs`].
 | 
			
		||||
     *
 | 
			
		||||
     * @param inventory The current inventory.
 | 
			
		||||
     * @return All items in this inventory.
 | 
			
		||||
     * @cc.treturn { (table|nil)... } All items in this inventory.
 | 
			
		||||
     * @cc.usage Find an adjacent chest and print all items in it.
 | 
			
		||||
     *
 | 
			
		||||
     * <pre>{@code
 | 
			
		||||
     * local chest = peripheral.find("minecraft:chest")
 | 
			
		||||
     * for slot, item in pairs(chest.list()) do
 | 
			
		||||
     *   print(("%d x %s in slot %d"):format(item.count, item.name, slot))
 | 
			
		||||
     * end
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract Map<Integer, Map<String, ?>> list(T inventory);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get detailed information about an item.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * The returned information contains the same information as each item in
 | 
			
		||||
     * {@link #list}, as well as additional details like the display name
 | 
			
		||||
     * (`displayName`), and item and item durability (`damage`, `maxDamage`, `durability`).
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Some items include more information (such as enchantments) - it is
 | 
			
		||||
     * recommended to print it out using [`textutils.serialize`] or in the Lua
 | 
			
		||||
     * REPL, to explore what is available.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * > [Deprecated fields][!INFO]
 | 
			
		||||
     * > Older versions of CC: Tweaked exposed an {@code itemGroups} field, listing the
 | 
			
		||||
     * > creative tabs an item was available under. This information is no longer available on
 | 
			
		||||
     * > more recent versions of the game, and so this field will always be empty. Do not use this
 | 
			
		||||
     * > field in new code!
 | 
			
		||||
     *
 | 
			
		||||
     * @param inventory The current inventory.
 | 
			
		||||
     * @param slot      The slot to get information about.
 | 
			
		||||
     * @return Information about the item in this slot, or {@code nil} if not present.
 | 
			
		||||
     * @throws LuaException If the slot is out of range.
 | 
			
		||||
     * @cc.treturn table Information about the item in this slot, or {@code nil} if not present.
 | 
			
		||||
     * @cc.usage Print some information about the first in a chest.
 | 
			
		||||
     *
 | 
			
		||||
     * <pre>{@code
 | 
			
		||||
     * local chest = peripheral.find("minecraft:chest")
 | 
			
		||||
     * local item = chest.getItemDetail(1)
 | 
			
		||||
     * if not item then print("No item") return end
 | 
			
		||||
     *
 | 
			
		||||
     * print(("%s (%s)"):format(item.displayName, item.name))
 | 
			
		||||
     * print(("Count: %d/%d"):format(item.count, item.maxCount))
 | 
			
		||||
     *
 | 
			
		||||
     * if item.damage then
 | 
			
		||||
     *   print(("Damage: %d/%d"):format(item.damage, item.maxDamage))
 | 
			
		||||
     * end
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract Map<String, ?> getItemDetail(T inventory, int slot) throws LuaException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the maximum number of items which can be stored in this slot.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Typically this will be limited to 64 items. However, some inventories (such as barrels or caches) can store
 | 
			
		||||
     * hundreds or thousands of items in one slot.
 | 
			
		||||
     *
 | 
			
		||||
     * @param inventory Inventory to probe.
 | 
			
		||||
     * @param slot      The slot
 | 
			
		||||
     * @return The maximum number of items in this slot.
 | 
			
		||||
     * @throws LuaException If the slot is out of range.
 | 
			
		||||
     * @cc.usage Count the maximum number of items an adjacent chest can hold.
 | 
			
		||||
     * <pre>{@code
 | 
			
		||||
     * local chest = peripheral.find("minecraft:chest")
 | 
			
		||||
     * local total = 0
 | 
			
		||||
     * for i = 1, chest.size() do
 | 
			
		||||
     *   total = total + chest.getItemLimit(i)
 | 
			
		||||
     * end
 | 
			
		||||
     * print(total)
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     * @cc.since 1.96.0
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract long getItemLimit(T inventory, int slot) throws LuaException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Push items from one inventory to another connected one.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This allows you to push an item in an inventory to another inventory <em>on the same wired network</em>. Both
 | 
			
		||||
     * inventories must attached to wired modems which are connected via a cable.
 | 
			
		||||
     *
 | 
			
		||||
     * @param from     Inventory to move items from.
 | 
			
		||||
     * @param computer The current computer.
 | 
			
		||||
     * @param toName   The name of the peripheral/inventory to push to. This is the string given to [`peripheral.wrap`],
 | 
			
		||||
     *                 and displayed by the wired modem.
 | 
			
		||||
     * @param fromSlot The slot in the current inventory to move items to.
 | 
			
		||||
     * @param limit    The maximum number of items to move. Defaults to the current stack limit.
 | 
			
		||||
     * @param toSlot   The slot in the target inventory to move to. If not given, the item will be inserted into any slot.
 | 
			
		||||
     * @return The number of transferred items.
 | 
			
		||||
     * @throws LuaException If the peripheral to transfer to doesn't exist or isn't an inventory.
 | 
			
		||||
     * @throws LuaException If either source or destination slot is out of range.
 | 
			
		||||
     * @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
 | 
			
		||||
     * @cc.usage Wrap two chests, and push an item from one to another.
 | 
			
		||||
     * <pre>{@code
 | 
			
		||||
     * local chest_a = peripheral.wrap("minecraft:chest_0")
 | 
			
		||||
     * local chest_b = peripheral.wrap("minecraft:chest_1")
 | 
			
		||||
     *
 | 
			
		||||
     * chest_a.pushItems(peripheral.getName(chest_b), 1)
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract int pushItems(
 | 
			
		||||
        T from, IComputerAccess computer, String toName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
 | 
			
		||||
    ) throws LuaException;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pull items from a connected inventory into this one.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This allows you to transfer items between inventories <em>on the same wired network</em>. Both this and the source
 | 
			
		||||
     * inventory must attached to wired modems which are connected via a cable.
 | 
			
		||||
     *
 | 
			
		||||
     * @param to       Inventory to move items to.
 | 
			
		||||
     * @param computer The current computer.
 | 
			
		||||
     * @param fromName The name of the peripheral/inventory to pull from. This is the string given to [`peripheral.wrap`],
 | 
			
		||||
     *                 and displayed by the wired modem.
 | 
			
		||||
     * @param fromSlot The slot in the source inventory to move items from.
 | 
			
		||||
     * @param limit    The maximum number of items to move. Defaults to the current stack limit.
 | 
			
		||||
     * @param toSlot   The slot in current inventory to move to. If not given, the item will be inserted into any slot.
 | 
			
		||||
     * @return The number of transferred items.
 | 
			
		||||
     * @throws LuaException If the peripheral to transfer to doesn't exist or isn't an inventory.
 | 
			
		||||
     * @throws LuaException If either source or destination slot is out of range.
 | 
			
		||||
     * @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
 | 
			
		||||
     * @cc.usage Wrap two chests, and push an item from one to another.
 | 
			
		||||
     * <pre>{@code
 | 
			
		||||
     * local chest_a = peripheral.wrap("minecraft:chest_0")
 | 
			
		||||
     * local chest_b = peripheral.wrap("minecraft:chest_1")
 | 
			
		||||
     *
 | 
			
		||||
     * chest_a.pullItems(peripheral.getName(chest_b), 1)
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public abstract int pullItems(
 | 
			
		||||
        T to, IComputerAccess computer, String fromName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
 | 
			
		||||
    ) throws LuaException;
 | 
			
		||||
}
 | 
			
		||||
@@ -11,6 +11,7 @@ import dan200.computercraft.api.turtle.TurtleCommandResult;
 | 
			
		||||
import dan200.computercraft.api.turtle.TurtleSide;
 | 
			
		||||
import dan200.computercraft.core.apis.IAPIEnvironment;
 | 
			
		||||
import dan200.computercraft.core.metrics.Metrics;
 | 
			
		||||
import dan200.computercraft.shared.peripheral.generic.methods.AbstractInventoryMethods;
 | 
			
		||||
import dan200.computercraft.shared.turtle.core.*;
 | 
			
		||||
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
@@ -749,7 +750,7 @@ public class TurtleAPI implements ILuaAPI {
 | 
			
		||||
     * --  count = 13,
 | 
			
		||||
     * -- }
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     * @see dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods#getItemDetail Describes the information returned by a detailed query.
 | 
			
		||||
     * @see AbstractInventoryMethods#getItemDetail Describes the information returned by a detailed query.
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction
 | 
			
		||||
    public final MethodResult getItemDetail(ILuaContext context, Optional<Integer> slot, Optional<Boolean> detailed) throws LuaException {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,11 +11,10 @@ import dan200.computercraft.api.peripheral.IPeripheral;
 | 
			
		||||
 * A generic source of {@link LuaFunction} functions.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Unlike normal objects ({@link IDynamicLuaObject} or {@link IPeripheral}), methods do not target this object but
 | 
			
		||||
 * instead are defined as {@code static} and accept their target as the first parameter. This allows you to inject
 | 
			
		||||
 * methods onto objects you do not own, as well as declaring methods for a specific "trait" (for instance, a Forge
 | 
			
		||||
 * capability or Fabric block lookup interface).
 | 
			
		||||
 * accept their target as the first parameter. This allows you to inject methods onto objects you do not own, as well as
 | 
			
		||||
 * declaring methods for a specific "trait" (for instance, a Forge capability or Fabric block lookup interface).
 | 
			
		||||
 * <p>
 | 
			
		||||
 * Currently the "generic peripheral" system is incompatible with normal peripherals. Peripherals explicitly provided
 | 
			
		||||
 * Currently, the "generic peripheral" system is incompatible with normal peripherals. Peripherals explicitly provided
 | 
			
		||||
 * by capabilities/the block lookup API take priority. Block entities which use this system are given a peripheral name
 | 
			
		||||
 * determined by their id, rather than any peripheral provider, though additional types may be provided by overriding
 | 
			
		||||
 * {@link GenericPeripheral#getType()}.
 | 
			
		||||
@@ -25,7 +24,7 @@ import dan200.computercraft.api.peripheral.IPeripheral;
 | 
			
		||||
 * <pre>{@code
 | 
			
		||||
 * public class InventoryMethods implements GenericSource {
 | 
			
		||||
 *     \@LuaFunction( mainThread = true )
 | 
			
		||||
 *     public static int size(IItemHandler inventory) {
 | 
			
		||||
 *     public int size(IItemHandler inventory) {
 | 
			
		||||
 *         return inventory.getSlots();
 | 
			
		||||
 *     }
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
@@ -18,10 +18,7 @@ import javax.annotation.Nullable;
 | 
			
		||||
import java.lang.invoke.MethodHandle;
 | 
			
		||||
import java.lang.invoke.MethodHandles;
 | 
			
		||||
import java.lang.invoke.MethodType;
 | 
			
		||||
import java.lang.reflect.Member;
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.lang.reflect.Modifier;
 | 
			
		||||
import java.lang.reflect.Type;
 | 
			
		||||
import java.lang.reflect.*;
 | 
			
		||||
import java.nio.ByteBuffer;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
@@ -106,9 +103,14 @@ final class Generator<T> {
 | 
			
		||||
    private final Function<MethodHandle, T> factory;
 | 
			
		||||
    private final Function<T, T> wrap;
 | 
			
		||||
 | 
			
		||||
    private final LoadingCache<Method, Optional<T>> methodCache = CacheBuilder
 | 
			
		||||
    private final LoadingCache<Method, Optional<T>> instanceCache = CacheBuilder
 | 
			
		||||
        .newBuilder()
 | 
			
		||||
        .build(CacheLoader.from(catching(this::build, Optional.empty())));
 | 
			
		||||
        .build(CacheLoader.from(catching(this::buildInstanceMethod, Optional.empty())));
 | 
			
		||||
 | 
			
		||||
    private final LoadingCache<GenericMethod, Optional<T>> genericCache = CacheBuilder
 | 
			
		||||
        .newBuilder()
 | 
			
		||||
        .weakKeys()
 | 
			
		||||
        .build(CacheLoader.from(catching(this::buildGenericMethod, Optional.empty())));
 | 
			
		||||
 | 
			
		||||
    Generator(List<Class<?>> context, Function<MethodHandle, T> factory, Function<T, T> wrap) {
 | 
			
		||||
        this.context = context;
 | 
			
		||||
@@ -131,65 +133,94 @@ final class Generator<T> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Optional<T> getMethod(Method method) {
 | 
			
		||||
        return methodCache.getUnchecked(method);
 | 
			
		||||
    Optional<T> getInstanceMethod(Method method) {
 | 
			
		||||
        return instanceCache.getUnchecked(method);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Optional<T> build(Method method) {
 | 
			
		||||
        var name = method.getDeclaringClass().getName() + "." + method.getName();
 | 
			
		||||
        var modifiers = method.getModifiers();
 | 
			
		||||
    Optional<T> getGenericMethod(GenericMethod method) {
 | 
			
		||||
        return genericCache.getUnchecked(method);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        // Instance methods must be final - this prevents them being overridden and potentially exposed twice.
 | 
			
		||||
        if (!Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) {
 | 
			
		||||
            LOG.warn("Lua Method {} should be final.", name);
 | 
			
		||||
    /**
 | 
			
		||||
     * Check if a {@link LuaFunction}-annotated method can be used in this context.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method The method to check.
 | 
			
		||||
     * @return Whether the method is valid.
 | 
			
		||||
     */
 | 
			
		||||
    private boolean checkMethod(Method method) {
 | 
			
		||||
        if (method.isBridge()) {
 | 
			
		||||
            LOG.debug("Skipping bridge Lua Method {}.{}", method.getDeclaringClass().getName(), method.getName());
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!Modifier.isPublic(modifiers)) {
 | 
			
		||||
            LOG.error("Lua Method {} should be a public method.", name);
 | 
			
		||||
            return Optional.empty();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
 | 
			
		||||
            LOG.error("Lua Method {} should be on a public class.", name);
 | 
			
		||||
            return Optional.empty();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LOG.debug("Generating method wrapper for {}.", name);
 | 
			
		||||
 | 
			
		||||
        // Check we don't throw additional exceptions.
 | 
			
		||||
        var exceptions = method.getExceptionTypes();
 | 
			
		||||
        for (var exception : exceptions) {
 | 
			
		||||
            if (exception != LuaException.class) {
 | 
			
		||||
                LOG.error("Lua Method {} cannot throw {}.", name, exception.getName());
 | 
			
		||||
                return Optional.empty();
 | 
			
		||||
                LOG.error("Lua Method {}.{} cannot throw {}.", method.getDeclaringClass().getName(), method.getName(), exception.getName());
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // unsafe can only be used on the computer thread, so reject it for mainThread functions.
 | 
			
		||||
        var annotation = method.getAnnotation(LuaFunction.class);
 | 
			
		||||
        if (annotation.unsafe() && annotation.mainThread()) {
 | 
			
		||||
            LOG.error("Lua Method {} cannot use unsafe and mainThread", name);
 | 
			
		||||
            return Optional.empty();
 | 
			
		||||
            LOG.error("Lua Method {}.{} cannot use unsafe and mainThread.", method.getDeclaringClass().getName(), method.getName());
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            var originalHandle = LOOKUP.unreflect(method);
 | 
			
		||||
 | 
			
		||||
            List<Type> parameters;
 | 
			
		||||
            if (Modifier.isStatic(modifiers)) {
 | 
			
		||||
                var allParameters = method.getGenericParameterTypes();
 | 
			
		||||
                parameters = Arrays.asList(allParameters).subList(1, allParameters.length);
 | 
			
		||||
            } else {
 | 
			
		||||
                parameters = Arrays.asList(method.getGenericParameterTypes());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var handle = buildMethodHandle(method, originalHandle, parameters, annotation.unsafe());
 | 
			
		||||
            if (handle == null) return Optional.empty();
 | 
			
		||||
 | 
			
		||||
            var instance = factory.apply(handle);
 | 
			
		||||
            return Optional.of(annotation.mainThread() ? wrap.apply(instance) : instance);
 | 
			
		||||
        } catch (ReflectiveOperationException | RuntimeException e) {
 | 
			
		||||
            LOG.error("Error generating wrapper for {}.", name, e);
 | 
			
		||||
            return Optional.empty();
 | 
			
		||||
        // Instance methods must be final - this prevents them being overridden and potentially exposed twice.
 | 
			
		||||
        var modifiers = method.getModifiers();
 | 
			
		||||
        if (!Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers) && !Modifier.isFinal(method.getDeclaringClass().getModifiers())) {
 | 
			
		||||
            LOG.warn("Lua Method {}.{} should be final.", method.getDeclaringClass().getName(), method.getName());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Optional<T> buildInstanceMethod(Method method) {
 | 
			
		||||
        if (!checkMethod(method)) return Optional.empty();
 | 
			
		||||
 | 
			
		||||
        var handle = tryUnreflect(method);
 | 
			
		||||
        if (handle == null) return Optional.empty();
 | 
			
		||||
 | 
			
		||||
        return build(method, handle, Arrays.asList(method.getGenericParameterTypes()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Optional<T> buildGenericMethod(GenericMethod method) {
 | 
			
		||||
        if (!checkMethod(method.method)) return Optional.empty();
 | 
			
		||||
 | 
			
		||||
        var handle = tryUnreflect(method.method);
 | 
			
		||||
        if (handle == null) return Optional.empty();
 | 
			
		||||
 | 
			
		||||
        var parameters = Arrays.asList(method.method.getGenericParameterTypes());
 | 
			
		||||
        return build(
 | 
			
		||||
            method.method,
 | 
			
		||||
            Modifier.isStatic(method.method.getModifiers()) ? handle : handle.bindTo(method.source),
 | 
			
		||||
            parameters.subList(1, parameters.size()) // Drop the instance argument.
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Generate our {@link T} instance for a specific method.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This {@linkplain #buildMethodHandle(Member, MethodHandle, List, boolean)} builds the method handle, and then
 | 
			
		||||
     * wraps it with {@link #factory}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method     The original method, for reflection and error reporting.
 | 
			
		||||
     * @param handle     The method handle to execute.
 | 
			
		||||
     * @param parameters The generic parameters to this method handle.
 | 
			
		||||
     * @return The generated method, or {@link Optional#empty()} if an error occurred.
 | 
			
		||||
     */
 | 
			
		||||
    private Optional<T> build(Method method, MethodHandle handle, List<Type> parameters) {
 | 
			
		||||
        LOG.debug("Generating method wrapper for {}.{}.", method.getDeclaringClass().getName(), method.getName());
 | 
			
		||||
 | 
			
		||||
        var annotation = method.getAnnotation(LuaFunction.class);
 | 
			
		||||
        var wrappedHandle = buildMethodHandle(method, handle, parameters, annotation.unsafe());
 | 
			
		||||
        if (wrappedHandle == null) return Optional.empty();
 | 
			
		||||
 | 
			
		||||
        var instance = factory.apply(wrappedHandle);
 | 
			
		||||
        return Optional.of(annotation.mainThread() ? wrap.apply(instance) : instance);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -202,8 +233,7 @@ final class Generator<T> {
 | 
			
		||||
     * @param unsafe         Whether to allow unsafe argument getters.
 | 
			
		||||
     * @return The wrapped method handle.
 | 
			
		||||
     */
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private MethodHandle buildMethodHandle(Member method, MethodHandle handle, List<Type> parameterTypes, boolean unsafe) {
 | 
			
		||||
    private @Nullable MethodHandle buildMethodHandle(Member method, MethodHandle handle, List<Type> parameterTypes, boolean unsafe) {
 | 
			
		||||
        if (handle.type().parameterCount() != parameterTypes.size() + 1) {
 | 
			
		||||
            throw new IllegalArgumentException("Argument lists are mismatched");
 | 
			
		||||
        }
 | 
			
		||||
@@ -263,8 +293,7 @@ final class Generator<T> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private static MethodHandle loadArg(Member method, boolean unsafe, Class<?> argType, Type genericArg, int argIndex) {
 | 
			
		||||
    private static @Nullable MethodHandle loadArg(Member method, boolean unsafe, Class<?> argType, Type genericArg, int argIndex) {
 | 
			
		||||
        if (argType == Coerced.class) {
 | 
			
		||||
            var klass = Reflect.getRawType(method, TypeToken.of(genericArg).resolveType(Reflect.COERCED_IN).getType(), false);
 | 
			
		||||
            if (klass == null) return null;
 | 
			
		||||
@@ -312,6 +341,22 @@ final class Generator<T> {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A wrapper over {@link MethodHandles.Lookup#unreflect(Method)} which discards errors.
 | 
			
		||||
     *
 | 
			
		||||
     * @param method The method to unreflect.
 | 
			
		||||
     * @return The resulting handle, or {@code null} if it cannot be unreflected.
 | 
			
		||||
     */
 | 
			
		||||
    private static @Nullable MethodHandle tryUnreflect(Method method) {
 | 
			
		||||
        try {
 | 
			
		||||
            method.setAccessible(true);
 | 
			
		||||
            return LOOKUP.unreflect(method);
 | 
			
		||||
        } catch (SecurityException | InaccessibleObjectException | IllegalAccessException e) {
 | 
			
		||||
            LOG.error("Lua Method {}.{} is not accessible.", method.getDeclaringClass().getName(), method.getName());
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("Guava")
 | 
			
		||||
    static <T, U> com.google.common.base.Function<T, U> catching(Function<T, U> function, U def) {
 | 
			
		||||
        return x -> {
 | 
			
		||||
@@ -320,7 +365,7 @@ final class Generator<T> {
 | 
			
		||||
            } catch (Exception | LinkageError e) {
 | 
			
		||||
                // LinkageError due to possible codegen bugs and NoClassDefFoundError. The latter occurs when fetching
 | 
			
		||||
                // methods on a class which references non-existent (i.e. client-only) types.
 | 
			
		||||
                LOG.error("Error generating @LuaFunctions", e);
 | 
			
		||||
                LOG.error("Error generating @LuaFunction for {}", x, e);
 | 
			
		||||
                return def;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@ import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.lang.reflect.Modifier;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
@@ -56,16 +55,11 @@ public final class GenericMethod {
 | 
			
		||||
        Class<?> klass = source.getClass();
 | 
			
		||||
        var type = source instanceof GenericPeripheral generic ? generic.getType() : null;
 | 
			
		||||
 | 
			
		||||
        return Arrays.stream(klass.getDeclaredMethods())
 | 
			
		||||
        return Arrays.stream(klass.getMethods())
 | 
			
		||||
            .map(method -> {
 | 
			
		||||
                var annotation = method.getAnnotation(LuaFunction.class);
 | 
			
		||||
                if (annotation == null) return null;
 | 
			
		||||
 | 
			
		||||
                if (!Modifier.isStatic(method.getModifiers())) {
 | 
			
		||||
                    LOG.error("GenericSource method {}.{} should be static.", method.getDeclaringClass(), method.getName());
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var types = method.getGenericParameterTypes();
 | 
			
		||||
                if (types.length == 0) {
 | 
			
		||||
                    LOG.error("GenericSource method {}.{} has no parameters.", method.getDeclaringClass(), method.getName());
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,7 @@ final class MethodSupplierImpl<T> implements MethodSupplier<T> {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var instance = generator.getMethod(method).orElse(null);
 | 
			
		||||
            var instance = generator.getInstanceMethod(method).orElse(null);
 | 
			
		||||
            if (instance == null) continue;
 | 
			
		||||
 | 
			
		||||
            if (methods == null) methods = new ArrayList<>();
 | 
			
		||||
@@ -121,7 +121,7 @@ final class MethodSupplierImpl<T> implements MethodSupplier<T> {
 | 
			
		||||
        for (var method : genericMethods) {
 | 
			
		||||
            if (!method.target.isAssignableFrom(klass)) continue;
 | 
			
		||||
 | 
			
		||||
            var instance = generator.getMethod(method.method).orElse(null);
 | 
			
		||||
            var instance = generator.getGenericMethod(method).orElse(null);
 | 
			
		||||
            if (instance == null) continue;
 | 
			
		||||
 | 
			
		||||
            if (methods == null) methods = new ArrayList<>();
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,7 @@ local term = _ENV
 | 
			
		||||
-- @since 1.31
 | 
			
		||||
-- @usage
 | 
			
		||||
-- Redirect to a monitor on the right of the computer.
 | 
			
		||||
--
 | 
			
		||||
--     term.redirect(peripheral.wrap("right"))
 | 
			
		||||
term.redirect = function(target)
 | 
			
		||||
    expect(1, target, "table")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,18 @@
 | 
			
		||||
# New features in CC: Tweaked 1.109.2
 | 
			
		||||
 | 
			
		||||
* `math.random` now uses Lua 5.4's random number generator.
 | 
			
		||||
 | 
			
		||||
Several bug fixes:
 | 
			
		||||
* Fix errors involving `goto` statements having the wrong line number.
 | 
			
		||||
 | 
			
		||||
# New features in CC: Tweaked 1.109.1
 | 
			
		||||
 | 
			
		||||
Several bug fixes:
 | 
			
		||||
* Fix `mouse_drag` event not firing for right and middle mouse buttons.
 | 
			
		||||
* Fix crash when syntax errors involve `goto` or `::`.
 | 
			
		||||
* Fix deadlock occuring when adding/removing observers.
 | 
			
		||||
* Allow placing seeds into compostor barrels with `turtle.place()`.
 | 
			
		||||
 | 
			
		||||
# New features in CC: Tweaked 1.109.0
 | 
			
		||||
 | 
			
		||||
* Update to Lua 5.2
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,8 @@
 | 
			
		||||
New features in CC: Tweaked 1.109.0
 | 
			
		||||
New features in CC: Tweaked 1.109.2
 | 
			
		||||
 | 
			
		||||
* Update to Lua 5.2
 | 
			
		||||
  * `getfenv`/`setfenv` now only work on Lua functions.
 | 
			
		||||
  * Add support for `goto`.
 | 
			
		||||
  * Remove support for dumping and loading binary chunks.
 | 
			
		||||
* File handles, HTTP requests and websocket messages now use raw bytes rather than converting to UTF-8.
 | 
			
		||||
* Add `allow_repetitions` option to `textutils.serialiseJSON`.
 | 
			
		||||
* Track memory allocated by computers.
 | 
			
		||||
* `math.random` now uses Lua 5.4's random number generator.
 | 
			
		||||
 | 
			
		||||
Several bug fixes:
 | 
			
		||||
* Fix error when using position captures and backreferences in string patterns (e.g. `()(%1)`).
 | 
			
		||||
* Fix formatting non-real numbers with `%d`.
 | 
			
		||||
* Fix errors involving `goto` statements having the wrong line number.
 | 
			
		||||
 | 
			
		||||
Type "help changelog" to see the full version history.
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,7 @@ local token_names = setmetatable({
 | 
			
		||||
    [tokens.DO] = code("do"),
 | 
			
		||||
    [tokens.DOT] = code("."),
 | 
			
		||||
    [tokens.DOTS] = code("..."),
 | 
			
		||||
    [tokens.DOUBLE_COLON] = code("::"),
 | 
			
		||||
    [tokens.ELSE] = code("else"),
 | 
			
		||||
    [tokens.ELSEIF] = code("elseif"),
 | 
			
		||||
    [tokens.END] = code("end"),
 | 
			
		||||
@@ -67,6 +68,7 @@ local token_names = setmetatable({
 | 
			
		||||
    [tokens.FOR] = code("for"),
 | 
			
		||||
    [tokens.FUNCTION] = code("function"),
 | 
			
		||||
    [tokens.GE] = code(">="),
 | 
			
		||||
    [tokens.GOTO] = code("goto"),
 | 
			
		||||
    [tokens.GT] = code(">"),
 | 
			
		||||
    [tokens.IF] = code("if"),
 | 
			
		||||
    [tokens.IN] = code("in"),
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ import java.util.Collection;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
import static dan200.computercraft.test.core.ContramapMatcher.contramap;
 | 
			
		||||
import static org.hamcrest.MatcherAssert.assertThat;
 | 
			
		||||
@@ -30,7 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
 | 
			
		||||
 | 
			
		||||
public class GeneratorTest {
 | 
			
		||||
    private static final MethodSupplierImpl<LuaMethod> GENERATOR = (MethodSupplierImpl<LuaMethod>) LuaMethodSupplier.create(
 | 
			
		||||
        GenericMethod.getMethods(new StaticMethod()).toList()
 | 
			
		||||
        Stream.of(new StaticGeneric(), new InstanceGeneric()).flatMap(GenericMethod::getMethods).toList()
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@@ -65,8 +66,10 @@ public class GeneratorTest {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testNonPublicClass() {
 | 
			
		||||
        assertThat(GENERATOR.getMethods(NonPublic.class), is(empty()));
 | 
			
		||||
    public void testNonPublicClass() throws LuaException {
 | 
			
		||||
        var methods = GENERATOR.getMethods(NonPublic.class);
 | 
			
		||||
        assertThat(methods, contains(named("go")));
 | 
			
		||||
        assertThat(apply(methods, new NonPublic(), "go"), is(MethodResult.of()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@@ -75,10 +78,18 @@ public class GeneratorTest {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testStaticMethod() throws LuaException {
 | 
			
		||||
        var methods = GENERATOR.getMethods(StaticMethodTarget.class);
 | 
			
		||||
        assertThat(methods, contains(named("go")));
 | 
			
		||||
        assertThat(apply(methods, new StaticMethodTarget(), "go", "Hello", 123), is(MethodResult.of()));
 | 
			
		||||
    public void testStaticGenericMethod() throws LuaException {
 | 
			
		||||
        var methods = GENERATOR.getMethods(GenericMethodTarget.class);
 | 
			
		||||
        assertThat(methods, hasItem(named("goStatic")));
 | 
			
		||||
        assertThat(apply(methods, new GenericMethodTarget(), "goStatic", "Hello", 123), is(MethodResult.of()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testInstanceGenericrMethod() throws LuaException {
 | 
			
		||||
        var methods = GENERATOR.getMethods(GenericMethodTarget.class);
 | 
			
		||||
        assertThat(methods, hasItem(named("goInstance")));
 | 
			
		||||
        assertThat(apply(methods, new GenericMethodTarget(), "goInstance", "Hello", 123), is(MethodResult.of()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
@@ -181,17 +192,28 @@ public class GeneratorTest {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class StaticMethodTarget {
 | 
			
		||||
    public static class GenericMethodTarget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class StaticMethod implements GenericSource {
 | 
			
		||||
    public static class StaticGeneric implements GenericSource {
 | 
			
		||||
        @Override
 | 
			
		||||
        public String id() {
 | 
			
		||||
            return "source";
 | 
			
		||||
            return "static";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @LuaFunction
 | 
			
		||||
        public static void go(StaticMethodTarget target, String arg1, int arg2, ILuaContext context) {
 | 
			
		||||
        public static void goStatic(GenericMethodTarget target, String arg1, int arg2, ILuaContext context) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class InstanceGeneric implements GenericSource {
 | 
			
		||||
        @Override
 | 
			
		||||
        public String id() {
 | 
			
		||||
            return "instance";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @LuaFunction
 | 
			
		||||
        public void goInstance(GenericMethodTarget target, String arg1, int arg2, ILuaContext context) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ addRemappedConfiguration("testWithIris")
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    clientCompileOnly(variantOf(libs.emi) { classifier("api") })
 | 
			
		||||
    modImplementation(libs.bundles.externalMods.fabric)
 | 
			
		||||
    modImplementation(libs.bundles.externalMods.fabric) { cct.exclude(this) }
 | 
			
		||||
    modCompileOnly(libs.bundles.externalMods.fabric.compile) {
 | 
			
		||||
        exclude("net.fabricmc", "fabric-loader")
 | 
			
		||||
        exclude("net.fabricmc.fabric-api")
 | 
			
		||||
@@ -72,9 +72,9 @@ dependencies {
 | 
			
		||||
    include(libs.nightConfig.toml)
 | 
			
		||||
 | 
			
		||||
    // Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
 | 
			
		||||
    api(commonClasses(project(":fabric-api")))
 | 
			
		||||
    clientApi(clientClasses(project(":fabric-api")))
 | 
			
		||||
    implementation(project(":core"))
 | 
			
		||||
    api(commonClasses(project(":fabric-api"))) { cct.exclude(this) }
 | 
			
		||||
    clientApi(clientClasses(project(":fabric-api"))) { cct.exclude(this) }
 | 
			
		||||
    implementation(project(":core")) { cct.exclude(this) }
 | 
			
		||||
    // These are transitive deps of :core, so we don't need these deps. However, we want them to appear as runtime deps
 | 
			
		||||
    // in our POM, and this is the easiest way.
 | 
			
		||||
    runtimeOnly(libs.cobalt)
 | 
			
		||||
@@ -168,7 +168,11 @@ loom {
 | 
			
		||||
            configureForGameTest(this)
 | 
			
		||||
 | 
			
		||||
            property("fabric-api.gametest")
 | 
			
		||||
            property("fabric-api.gametest.report-file", layout.buildDirectory.dir("test-results/runGametest.xml").getAbsolutePath())
 | 
			
		||||
            property(
 | 
			
		||||
                "fabric-api.gametest.report-file",
 | 
			
		||||
                layout.buildDirectory.dir("test-results/runGametest.xml")
 | 
			
		||||
                    .getAbsolutePath(),
 | 
			
		||||
            )
 | 
			
		||||
            runDir("run/gametest")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -258,9 +262,7 @@ publishing {
 | 
			
		||||
    publications {
 | 
			
		||||
        named("maven", MavenPublication::class) {
 | 
			
		||||
            mavenDependencies {
 | 
			
		||||
                exclude(dependencies.create("cc.tweaked:"))
 | 
			
		||||
                exclude(libs.jei.fabric.get())
 | 
			
		||||
                exclude(libs.modmenu.get())
 | 
			
		||||
                cct.configureExcludes(this)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
{"replace": false, "values": ["#minecraft:cauldrons", "#minecraft:beehives"]}
 | 
			
		||||
{"replace": false, "values": ["#minecraft:beehives", "#minecraft:cauldrons", "minecraft:composter"]}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,14 +4,11 @@
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.peripheral.generic.methods;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
import dan200.computercraft.api.detail.VanillaDetailRegistries;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaException;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaFunction;
 | 
			
		||||
import dan200.computercraft.api.peripheral.GenericPeripheral;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IComputerAccess;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IPeripheral;
 | 
			
		||||
import dan200.computercraft.api.peripheral.PeripheralType;
 | 
			
		||||
import dan200.computercraft.shared.platform.FabricContainerTransfer;
 | 
			
		||||
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage;
 | 
			
		||||
import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant;
 | 
			
		||||
@@ -32,37 +29,30 @@ import java.util.Optional;
 | 
			
		||||
import static dan200.computercraft.core.util.ArgumentHelpers.assertBetween;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Methods for interacting with inventories. This mirrors the Forge version.
 | 
			
		||||
 * Inventory methods for Fabric's {@link SlottedStorage} and {@link ItemVariant}s.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * The generic peripheral system doesn't (currently) support generics, and so we need to wrap this in a
 | 
			
		||||
 * {@link StorageWrapper} box.
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings("UnstableApiUsage")
 | 
			
		||||
public class InventoryMethods implements GenericPeripheral {
 | 
			
		||||
    @Override
 | 
			
		||||
    public PeripheralType getType() {
 | 
			
		||||
        return PeripheralType.ofAdditional("inventory");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String id() {
 | 
			
		||||
        return ComputerCraftAPI.MOD_ID + ":inventory";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
public final class InventoryMethods extends AbstractInventoryMethods<InventoryMethods.StorageWrapper> {
 | 
			
		||||
    /**
 | 
			
		||||
     * Wrapper over a {@link SlottedStorage}.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * The generic peripheral system doesn't (currently) support generics, and so we need put the inventory in a box.
 | 
			
		||||
     *
 | 
			
		||||
     * @param storage The underlying storage
 | 
			
		||||
     */
 | 
			
		||||
    public record StorageWrapper(SlottedStorage<ItemVariant> storage) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int size(StorageWrapper inventory) {
 | 
			
		||||
    public int size(StorageWrapper inventory) {
 | 
			
		||||
        return inventory.storage().getSlots().size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static Map<Integer, Map<String, ?>> list(StorageWrapper inventory) {
 | 
			
		||||
    public Map<Integer, Map<String, ?>> list(StorageWrapper inventory) {
 | 
			
		||||
        Map<Integer, Map<String, ?>> result = new HashMap<>();
 | 
			
		||||
        var slots = inventory.storage().getSlots();
 | 
			
		||||
        var size = slots.size();
 | 
			
		||||
@@ -74,23 +64,26 @@ public class InventoryMethods implements GenericPeripheral {
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static Map<String, ?> getItemDetail(StorageWrapper inventory, int slot) throws LuaException {
 | 
			
		||||
    public Map<String, ?> getItemDetail(StorageWrapper inventory, int slot) throws LuaException {
 | 
			
		||||
        assertBetween(slot, 1, inventory.storage().getSlotCount(), "Slot out of range (%s)");
 | 
			
		||||
 | 
			
		||||
        var stack = toStack(inventory.storage().getSlot(slot - 1));
 | 
			
		||||
        return stack.isEmpty() ? null : VanillaDetailRegistries.ITEM_STACK.getDetails(stack);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static long getItemLimit(StorageWrapper inventory, int slot) throws LuaException {
 | 
			
		||||
    public long getItemLimit(StorageWrapper inventory, int slot) throws LuaException {
 | 
			
		||||
        assertBetween(slot, 1, inventory.storage().getSlotCount(), "Slot out of range (%s)");
 | 
			
		||||
        return inventory.storage().getSlot(slot - 1).getCapacity();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int pushItems(
 | 
			
		||||
    public int pushItems(
 | 
			
		||||
        StorageWrapper from, IComputerAccess computer,
 | 
			
		||||
        String toName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
 | 
			
		||||
    ) throws LuaException {
 | 
			
		||||
@@ -112,8 +105,9 @@ public class InventoryMethods implements GenericPeripheral {
 | 
			
		||||
        return moveItem(fromStorage, fromSlot - 1, to, toSlot.orElse(0) - 1, actualLimit);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int pullItems(
 | 
			
		||||
    public int pullItems(
 | 
			
		||||
        StorageWrapper to, IComputerAccess computer,
 | 
			
		||||
        String fromName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
 | 
			
		||||
    ) throws LuaException {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ plugins {
 | 
			
		||||
    id("cc-tweaked.forge")
 | 
			
		||||
    id("cc-tweaked.gametest")
 | 
			
		||||
    alias(libs.plugins.mixinGradle)
 | 
			
		||||
    id("cc-tweaked.illuaminate")
 | 
			
		||||
    id("cc-tweaked.mod-publishing")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -117,7 +116,6 @@ mixin {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
configurations {
 | 
			
		||||
    register("cctJavadoc")
 | 
			
		||||
    minecraftLibrary { extendsFrom(minecraftEmbed.get()) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -133,9 +131,9 @@ dependencies {
 | 
			
		||||
    libs.bundles.externalMods.forge.runtime.get().map { runtimeOnly(fg.deobf(it)) }
 | 
			
		||||
 | 
			
		||||
    // Depend on our other projects.
 | 
			
		||||
    api(commonClasses(project(":forge-api")))
 | 
			
		||||
    api(clientClasses(project(":forge-api")))
 | 
			
		||||
    implementation(project(":core"))
 | 
			
		||||
    api(commonClasses(project(":forge-api"))) { cct.exclude(this) }
 | 
			
		||||
    clientApi(clientClasses(project(":forge-api"))) { cct.exclude(this) }
 | 
			
		||||
    implementation(project(":core")) { cct.exclude(this) }
 | 
			
		||||
 | 
			
		||||
    minecraftEmbed(libs.cobalt) {
 | 
			
		||||
        jarJar.ranged(this, "[${libs.versions.cobalt.asProvider().get()},${libs.versions.cobalt.next.get()})")
 | 
			
		||||
@@ -167,40 +165,10 @@ dependencies {
 | 
			
		||||
    testModImplementation(testFixtures(project(":forge")))
 | 
			
		||||
 | 
			
		||||
    testFixturesImplementation(testFixtures(project(":core")))
 | 
			
		||||
 | 
			
		||||
    "cctJavadoc"(libs.cctJavadoc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
illuaminate {
 | 
			
		||||
    version.set(libs.versions.illuaminate)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Compile tasks
 | 
			
		||||
 | 
			
		||||
val luaJavadoc by tasks.registering(Javadoc::class) {
 | 
			
		||||
    description = "Generates documentation for Java-side Lua functions."
 | 
			
		||||
    group = JavaBasePlugin.DOCUMENTATION_GROUP
 | 
			
		||||
 | 
			
		||||
    source(sourceSets.main.get().java)
 | 
			
		||||
    source(project(":core").sourceSets.main.get().java)
 | 
			
		||||
    source(project(":common").sourceSets.main.get().java)
 | 
			
		||||
 | 
			
		||||
    destinationDir = layout.buildDirectory.dir("docs/luaJavadoc").get().asFile
 | 
			
		||||
    classpath = sourceSets.main.get().compileClasspath
 | 
			
		||||
 | 
			
		||||
    val options = options as StandardJavadocDocletOptions
 | 
			
		||||
    options.docletpath = configurations["cctJavadoc"].files.toList()
 | 
			
		||||
    options.doclet = "cc.tweaked.javadoc.LuaDoclet"
 | 
			
		||||
    options.addStringOption("project-root", rootProject.file(".").absolutePath)
 | 
			
		||||
    options.noTimestamp(false)
 | 
			
		||||
 | 
			
		||||
    javadocTool.set(
 | 
			
		||||
        javaToolchains.javadocToolFor {
 | 
			
		||||
            languageVersion.set(cc.tweaked.gradle.CCTweakedPlugin.JAVA_VERSION)
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tasks.processResources {
 | 
			
		||||
    inputs.property("modVersion", modVersion)
 | 
			
		||||
    inputs.property("forgeVersion", libs.versions.forge.get())
 | 
			
		||||
@@ -240,24 +208,6 @@ tasks.test {
 | 
			
		||||
    systemProperty("cct.test-files", layout.buildDirectory.dir("tmp/testFiles").getAbsolutePath())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val lintLua by tasks.registering(IlluaminateExec::class) {
 | 
			
		||||
    group = JavaBasePlugin.VERIFICATION_GROUP
 | 
			
		||||
    description = "Lint Lua (and Lua docs) with illuaminate"
 | 
			
		||||
 | 
			
		||||
    // Config files
 | 
			
		||||
    inputs.file(rootProject.file("illuaminate.sexp")).withPropertyName("illuaminate.sexp")
 | 
			
		||||
    // Sources
 | 
			
		||||
    inputs.files(rootProject.fileTree("doc")).withPropertyName("docs")
 | 
			
		||||
    inputs.files(project(":core").fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
 | 
			
		||||
    inputs.files(luaJavadoc)
 | 
			
		||||
 | 
			
		||||
    args = listOf("lint")
 | 
			
		||||
    workingDir = rootProject.projectDir
 | 
			
		||||
 | 
			
		||||
    doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
 | 
			
		||||
    doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val runGametest by tasks.registering(JavaExec::class) {
 | 
			
		||||
    group = LifecycleBasePlugin.VERIFICATION_GROUP
 | 
			
		||||
    description = "Runs tests on a temporary Minecraft instance."
 | 
			
		||||
@@ -304,7 +254,7 @@ publishing {
 | 
			
		||||
            artifact(tasks.jarJar)
 | 
			
		||||
 | 
			
		||||
            mavenDependencies {
 | 
			
		||||
                exclude(dependencies.create("cc.tweaked:"))
 | 
			
		||||
                cct.configureExcludes(this)
 | 
			
		||||
                exclude(libs.jei.forge.get())
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
{"values": ["#minecraft:cauldrons", "#minecraft:beehives"]}
 | 
			
		||||
{"values": ["#minecraft:beehives", "#minecraft:cauldrons", "minecraft:composter"]}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,54 +4,22 @@
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.peripheral.generic.methods;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaFunction;
 | 
			
		||||
import dan200.computercraft.api.peripheral.GenericPeripheral;
 | 
			
		||||
import dan200.computercraft.api.peripheral.PeripheralType;
 | 
			
		||||
import net.minecraftforge.energy.IEnergyStorage;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Methods for interacting with blocks using Forge's energy storage system.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * This works with energy storage blocks, as well as generators and machines which consume energy.
 | 
			
		||||
 * <p>
 | 
			
		||||
 * > [!NOTE]
 | 
			
		||||
 * > Due to limitations with Forge's energy API, it is not possible to measure throughput (i.e. RF
 | 
			
		||||
 * > used/generated per tick).
 | 
			
		||||
 *
 | 
			
		||||
 * @cc.module energy_storage
 | 
			
		||||
 * @cc.since 1.94.0
 | 
			
		||||
 * Fluid methods for Forge's {@link IEnergyStorage}.
 | 
			
		||||
 */
 | 
			
		||||
public class EnergyMethods implements GenericPeripheral {
 | 
			
		||||
public final class EnergyMethods extends AbstractEnergyMethods<IEnergyStorage> {
 | 
			
		||||
    @Override
 | 
			
		||||
    public PeripheralType getType() {
 | 
			
		||||
        return PeripheralType.ofAdditional("energy_storage");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String id() {
 | 
			
		||||
        return ComputerCraftAPI.MOD_ID + ":energy";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the energy of this block.
 | 
			
		||||
     *
 | 
			
		||||
     * @param energy The current energy storage.
 | 
			
		||||
     * @return The energy stored in this block, in FE.
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int getEnergy(IEnergyStorage energy) {
 | 
			
		||||
    public int getEnergy(IEnergyStorage energy) {
 | 
			
		||||
        return energy.getEnergyStored();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the maximum amount of energy this block can store.
 | 
			
		||||
     *
 | 
			
		||||
     * @param energy The current energy storage.
 | 
			
		||||
     * @return The energy capacity of this block.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int getEnergyCapacity(IEnergyStorage energy) {
 | 
			
		||||
    public int getEnergyCapacity(IEnergyStorage energy) {
 | 
			
		||||
        return energy.getMaxEnergyStored();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,10 @@
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.peripheral.generic.methods;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
import dan200.computercraft.api.detail.ForgeDetailRegistries;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaException;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaFunction;
 | 
			
		||||
import dan200.computercraft.api.peripheral.GenericPeripheral;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IComputerAccess;
 | 
			
		||||
import dan200.computercraft.api.peripheral.PeripheralType;
 | 
			
		||||
import dan200.computercraft.shared.platform.RegistryWrappers;
 | 
			
		||||
import net.minecraft.world.level.block.entity.BlockEntity;
 | 
			
		||||
import net.minecraftforge.common.capabilities.ForgeCapabilities;
 | 
			
		||||
@@ -26,37 +23,12 @@ import java.util.Optional;
 | 
			
		||||
import static dan200.computercraft.shared.util.ArgumentHelpers.getRegistryEntry;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Methods for interacting with tanks and other fluid storage blocks.
 | 
			
		||||
 *
 | 
			
		||||
 * @cc.module fluid_storage
 | 
			
		||||
 * @cc.since 1.94.0
 | 
			
		||||
 * Fluid methods for Forge's {@link IFluidHandler}.
 | 
			
		||||
 */
 | 
			
		||||
public class FluidMethods implements GenericPeripheral {
 | 
			
		||||
public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
 | 
			
		||||
    @Override
 | 
			
		||||
    public PeripheralType getType() {
 | 
			
		||||
        return PeripheralType.ofAdditional("fluid_storage");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String id() {
 | 
			
		||||
        return ComputerCraftAPI.MOD_ID + ":fluid";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get all "tanks" in this fluid storage.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Each tank either contains some amount of fluid or is empty. Tanks with fluids inside will return some basic
 | 
			
		||||
     * information about the fluid, including its name and amount.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * The returned table is sparse, and so empty tanks will be `nil` - it is recommended to loop over using `pairs`
 | 
			
		||||
     * rather than `ipairs`.
 | 
			
		||||
     *
 | 
			
		||||
     * @param fluids The current fluid handler.
 | 
			
		||||
     * @return All tanks.
 | 
			
		||||
     * @cc.treturn { (table|nil)... } All tanks in this fluid storage.
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static Map<Integer, Map<String, ?>> tanks(IFluidHandler fluids) {
 | 
			
		||||
    public Map<Integer, Map<String, ?>> tanks(IFluidHandler fluids) {
 | 
			
		||||
        Map<Integer, Map<String, ?>> result = new HashMap<>();
 | 
			
		||||
        var size = fluids.getTanks();
 | 
			
		||||
        for (var i = 0; i < size; i++) {
 | 
			
		||||
@@ -67,24 +39,9 @@ public class FluidMethods implements GenericPeripheral {
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Move a fluid from one fluid container to another connected one.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This allows you to pull fluid in the current fluid container to another container <em>on the same wired
 | 
			
		||||
     * network</em>. Both containers must attached to wired modems which are connected via a cable.
 | 
			
		||||
     *
 | 
			
		||||
     * @param from      Container to move fluid from.
 | 
			
		||||
     * @param computer  The current computer.
 | 
			
		||||
     * @param toName    The name of the peripheral/container to push to. This is the string given to [`peripheral.wrap`],
 | 
			
		||||
     *                  and displayed by the wired modem.
 | 
			
		||||
     * @param limit     The maximum amount of fluid to move.
 | 
			
		||||
     * @param fluidName The fluid to move. If not given, an arbitrary fluid will be chosen.
 | 
			
		||||
     * @return The amount of moved fluid.
 | 
			
		||||
     * @throws LuaException If the peripheral to transfer to doesn't exist or isn't an fluid container.
 | 
			
		||||
     * @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int pushFluid(
 | 
			
		||||
    public int pushFluid(
 | 
			
		||||
        IFluidHandler from, IComputerAccess computer,
 | 
			
		||||
        String toName, Optional<Integer> limit, Optional<String> fluidName
 | 
			
		||||
    ) throws LuaException {
 | 
			
		||||
@@ -107,24 +64,9 @@ public class FluidMethods implements GenericPeripheral {
 | 
			
		||||
            : moveFluid(from, new FluidStack(fluid, actualLimit), to);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Move a fluid from a connected fluid container into this oneone.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This allows you to pull fluid in the current fluid container from another container <em>on the same wired
 | 
			
		||||
     * network</em>. Both containers must attached to wired modems which are connected via a cable.
 | 
			
		||||
     *
 | 
			
		||||
     * @param to        Container to move fluid to.
 | 
			
		||||
     * @param computer  The current computer.
 | 
			
		||||
     * @param fromName  The name of the peripheral/container to push to. This is the string given to [`peripheral.wrap`],
 | 
			
		||||
     *                  and displayed by the wired modem.
 | 
			
		||||
     * @param limit     The maximum amount of fluid to move.
 | 
			
		||||
     * @param fluidName The fluid to move. If not given, an arbitrary fluid will be chosen.
 | 
			
		||||
     * @return The amount of moved fluid.
 | 
			
		||||
     * @throws LuaException If the peripheral to transfer to doesn't exist or isn't an fluid container.
 | 
			
		||||
     * @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int pullFluid(
 | 
			
		||||
    public int pullFluid(
 | 
			
		||||
        IFluidHandler to, IComputerAccess computer,
 | 
			
		||||
        String fromName, Optional<Integer> limit, Optional<String> fluidName
 | 
			
		||||
    ) throws LuaException {
 | 
			
		||||
@@ -194,7 +136,7 @@ public class FluidMethods implements GenericPeripheral {
 | 
			
		||||
     * @return The amount of fluid moved.
 | 
			
		||||
     */
 | 
			
		||||
    private static int moveFluid(IFluidHandler from, FluidStack extracted, int limit, IFluidHandler to) {
 | 
			
		||||
        if (extracted == null || extracted.getAmount() <= 0) return 0;
 | 
			
		||||
        if (extracted.getAmount() <= 0) return 0;
 | 
			
		||||
 | 
			
		||||
        // Limit the amount to extract.
 | 
			
		||||
        extracted = extracted.copy();
 | 
			
		||||
 
 | 
			
		||||
@@ -4,14 +4,10 @@
 | 
			
		||||
 | 
			
		||||
package dan200.computercraft.shared.peripheral.generic.methods;
 | 
			
		||||
 | 
			
		||||
import dan200.computercraft.api.ComputerCraftAPI;
 | 
			
		||||
import dan200.computercraft.api.detail.VanillaDetailRegistries;
 | 
			
		||||
import dan200.computercraft.api.lua.ILuaContext;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaException;
 | 
			
		||||
import dan200.computercraft.api.lua.LuaFunction;
 | 
			
		||||
import dan200.computercraft.api.peripheral.GenericPeripheral;
 | 
			
		||||
import dan200.computercraft.api.peripheral.IComputerAccess;
 | 
			
		||||
import dan200.computercraft.api.peripheral.PeripheralType;
 | 
			
		||||
import dan200.computercraft.shared.platform.ForgeContainerTransfer;
 | 
			
		||||
import net.minecraft.world.Container;
 | 
			
		||||
import net.minecraft.world.level.block.entity.BlockEntity;
 | 
			
		||||
@@ -28,59 +24,18 @@ import java.util.Optional;
 | 
			
		||||
import static dan200.computercraft.core.util.ArgumentHelpers.assertBetween;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Methods for interacting with inventories.
 | 
			
		||||
 *
 | 
			
		||||
 * @cc.module inventory
 | 
			
		||||
 * @cc.since 1.94.0
 | 
			
		||||
 * Inventory methods for Forge's {@link IItemHandler}.
 | 
			
		||||
 */
 | 
			
		||||
public class InventoryMethods implements GenericPeripheral {
 | 
			
		||||
public final class InventoryMethods extends AbstractInventoryMethods<IItemHandler> {
 | 
			
		||||
    @Override
 | 
			
		||||
    public PeripheralType getType() {
 | 
			
		||||
        return PeripheralType.ofAdditional("inventory");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public String id() {
 | 
			
		||||
        return ComputerCraftAPI.MOD_ID + ":inventory";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the size of this inventory.
 | 
			
		||||
     *
 | 
			
		||||
     * @param inventory The current inventory.
 | 
			
		||||
     * @return The number of slots in this inventory.
 | 
			
		||||
     */
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int size(IItemHandler inventory) {
 | 
			
		||||
    public int size(IItemHandler inventory) {
 | 
			
		||||
        return inventory.getSlots();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * List all items in this inventory. This returns a table, with an entry for each slot.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Each item in the inventory is represented by a table containing some basic information, much like
 | 
			
		||||
     * {@link dan200.computercraft.shared.turtle.apis.TurtleAPI#getItemDetail(ILuaContext, Optional, Optional)}
 | 
			
		||||
     * includes. More information can be fetched with {@link #getItemDetail}. The table contains the item `name`, the
 | 
			
		||||
     * `count` and an a (potentially nil) hash of the item's `nbt.` This NBT data doesn't contain anything useful, but
 | 
			
		||||
     * allows you to distinguish identical items.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * The returned table is sparse, and so empty slots will be `nil` - it is recommended to loop over using `pairs`
 | 
			
		||||
     * rather than `ipairs`.
 | 
			
		||||
     *
 | 
			
		||||
     * @param inventory The current inventory.
 | 
			
		||||
     * @return All items in this inventory.
 | 
			
		||||
     * @cc.treturn { (table|nil)... } All items in this inventory.
 | 
			
		||||
     * @cc.usage Find an adjacent chest and print all items in it.
 | 
			
		||||
     *
 | 
			
		||||
     * <pre>{@code
 | 
			
		||||
     * local chest = peripheral.find("minecraft:chest")
 | 
			
		||||
     * for slot, item in pairs(chest.list()) do
 | 
			
		||||
     *   print(("%d x %s in slot %d"):format(item.count, item.name, slot))
 | 
			
		||||
     * end
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static Map<Integer, Map<String, ?>> list(IItemHandler inventory) {
 | 
			
		||||
    public Map<Integer, Map<String, ?>> list(IItemHandler inventory) {
 | 
			
		||||
        Map<Integer, Map<String, ?>> result = new HashMap<>();
 | 
			
		||||
        var size = inventory.getSlots();
 | 
			
		||||
        for (var i = 0; i < size; i++) {
 | 
			
		||||
@@ -91,106 +46,26 @@ public class InventoryMethods implements GenericPeripheral {
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get detailed information about an item.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * The returned information contains the same information as each item in
 | 
			
		||||
     * {@link #list}, as well as additional details like the display name
 | 
			
		||||
     * (`displayName`), and item and item durability (`damage`, `maxDamage`, `durability`).
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Some items include more information (such as enchantments) - it is
 | 
			
		||||
     * recommended to print it out using [`textutils.serialize`] or in the Lua
 | 
			
		||||
     * REPL, to explore what is available.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * > [Deprecated fields][!INFO]
 | 
			
		||||
     * > Older versions of CC: Tweaked exposed an {@code itemGroups} field, listing the
 | 
			
		||||
     * > creative tabs an item was available under. This information is no longer available on
 | 
			
		||||
     * > more recent versions of the game, and so this field will always be empty. Do not use this
 | 
			
		||||
     * > field in new code!
 | 
			
		||||
     *
 | 
			
		||||
     * @param inventory The current inventory.
 | 
			
		||||
     * @param slot      The slot to get information about.
 | 
			
		||||
     * @return Information about the item in this slot, or {@code nil} if not present.
 | 
			
		||||
     * @throws LuaException If the slot is out of range.
 | 
			
		||||
     * @cc.treturn table Information about the item in this slot, or {@code nil} if not present.
 | 
			
		||||
     * @cc.usage Print some information about the first in a chest.
 | 
			
		||||
     *
 | 
			
		||||
     * <pre>{@code
 | 
			
		||||
     * local chest = peripheral.find("minecraft:chest")
 | 
			
		||||
     * local item = chest.getItemDetail(1)
 | 
			
		||||
     * if not item then print("No item") return end
 | 
			
		||||
     *
 | 
			
		||||
     * print(("%s (%s)"):format(item.displayName, item.name))
 | 
			
		||||
     * print(("Count: %d/%d"):format(item.count, item.maxCount))
 | 
			
		||||
     *
 | 
			
		||||
     * if item.damage then
 | 
			
		||||
     *   print(("Damage: %d/%d"):format(item.damage, item.maxDamage))
 | 
			
		||||
     * end
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static Map<String, ?> getItemDetail(IItemHandler inventory, int slot) throws LuaException {
 | 
			
		||||
    public Map<String, ?> getItemDetail(IItemHandler inventory, int slot) throws LuaException {
 | 
			
		||||
        assertBetween(slot, 1, inventory.getSlots(), "Slot out of range (%s)");
 | 
			
		||||
 | 
			
		||||
        var stack = inventory.getStackInSlot(slot - 1);
 | 
			
		||||
        return stack.isEmpty() ? null : VanillaDetailRegistries.ITEM_STACK.getDetails(stack);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the maximum number of items which can be stored in this slot.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * Typically this will be limited to 64 items. However, some inventories (such as barrels or caches) can store
 | 
			
		||||
     * hundreds or thousands of items in one slot.
 | 
			
		||||
     *
 | 
			
		||||
     * @param inventory Inventory to probe.
 | 
			
		||||
     * @param slot      The slot
 | 
			
		||||
     * @return The maximum number of items in this slot.
 | 
			
		||||
     * @throws LuaException If the slot is out of range.
 | 
			
		||||
     * @cc.usage Count the maximum number of items an adjacent chest can hold.
 | 
			
		||||
     * <pre>{@code
 | 
			
		||||
     * local chest = peripheral.find("minecraft:chest")
 | 
			
		||||
     * local total = 0
 | 
			
		||||
     * for i = 1, chest.size() do
 | 
			
		||||
     *   total = total + chest.getItemLimit(i)
 | 
			
		||||
     * end
 | 
			
		||||
     * print(total)
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     * @cc.since 1.96.0
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int getItemLimit(IItemHandler inventory, int slot) throws LuaException {
 | 
			
		||||
    public long getItemLimit(IItemHandler inventory, int slot) throws LuaException {
 | 
			
		||||
        assertBetween(slot, 1, inventory.getSlots(), "Slot out of range (%s)");
 | 
			
		||||
        return inventory.getSlotLimit(slot - 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Push items from one inventory to another connected one.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This allows you to push an item in an inventory to another inventory <em>on the same wired network</em>. Both
 | 
			
		||||
     * inventories must attached to wired modems which are connected via a cable.
 | 
			
		||||
     *
 | 
			
		||||
     * @param from     Inventory to move items from.
 | 
			
		||||
     * @param computer The current computer.
 | 
			
		||||
     * @param toName   The name of the peripheral/inventory to push to. This is the string given to [`peripheral.wrap`],
 | 
			
		||||
     *                 and displayed by the wired modem.
 | 
			
		||||
     * @param fromSlot The slot in the current inventory to move items to.
 | 
			
		||||
     * @param limit    The maximum number of items to move. Defaults to the current stack limit.
 | 
			
		||||
     * @param toSlot   The slot in the target inventory to move to. If not given, the item will be inserted into any slot.
 | 
			
		||||
     * @return The number of transferred items.
 | 
			
		||||
     * @throws LuaException If the peripheral to transfer to doesn't exist or isn't an inventory.
 | 
			
		||||
     * @throws LuaException If either source or destination slot is out of range.
 | 
			
		||||
     * @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
 | 
			
		||||
     * @cc.usage Wrap two chests, and push an item from one to another.
 | 
			
		||||
     * <pre>{@code
 | 
			
		||||
     * local chest_a = peripheral.wrap("minecraft:chest_0")
 | 
			
		||||
     * local chest_b = peripheral.wrap("minecraft:chest_1")
 | 
			
		||||
     *
 | 
			
		||||
     * chest_a.pushItems(peripheral.getName(chest_b), 1)
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int pushItems(
 | 
			
		||||
    public int pushItems(
 | 
			
		||||
        IItemHandler from, IComputerAccess computer,
 | 
			
		||||
        String toName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
 | 
			
		||||
    ) throws LuaException {
 | 
			
		||||
@@ -210,33 +85,9 @@ public class InventoryMethods implements GenericPeripheral {
 | 
			
		||||
        return moveItem(from, fromSlot - 1, to, toSlot.orElse(0) - 1, actualLimit);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pull items from a connected inventory into this one.
 | 
			
		||||
     * <p>
 | 
			
		||||
     * This allows you to transfer items between inventories <em>on the same wired network</em>. Both this and the source
 | 
			
		||||
     * inventory must attached to wired modems which are connected via a cable.
 | 
			
		||||
     *
 | 
			
		||||
     * @param to       Inventory to move items to.
 | 
			
		||||
     * @param computer The current computer.
 | 
			
		||||
     * @param fromName The name of the peripheral/inventory to pull from. This is the string given to [`peripheral.wrap`],
 | 
			
		||||
     *                 and displayed by the wired modem.
 | 
			
		||||
     * @param fromSlot The slot in the source inventory to move items from.
 | 
			
		||||
     * @param limit    The maximum number of items to move. Defaults to the current stack limit.
 | 
			
		||||
     * @param toSlot   The slot in current inventory to move to. If not given, the item will be inserted into any slot.
 | 
			
		||||
     * @return The number of transferred items.
 | 
			
		||||
     * @throws LuaException If the peripheral to transfer to doesn't exist or isn't an inventory.
 | 
			
		||||
     * @throws LuaException If either source or destination slot is out of range.
 | 
			
		||||
     * @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
 | 
			
		||||
     * @cc.usage Wrap two chests, and push an item from one to another.
 | 
			
		||||
     * <pre>{@code
 | 
			
		||||
     * local chest_a = peripheral.wrap("minecraft:chest_0")
 | 
			
		||||
     * local chest_b = peripheral.wrap("minecraft:chest_1")
 | 
			
		||||
     *
 | 
			
		||||
     * chest_a.pullItems(peripheral.getName(chest_b), 1)
 | 
			
		||||
     * }</pre>
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    @LuaFunction(mainThread = true)
 | 
			
		||||
    public static int pullItems(
 | 
			
		||||
    public int pullItems(
 | 
			
		||||
        IItemHandler to, IComputerAccess computer,
 | 
			
		||||
        String fromName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
 | 
			
		||||
    ) throws LuaException {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.nio.ByteBuffer;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.nio.file.InvalidPathException;
 | 
			
		||||
import java.nio.file.Path;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
@@ -97,7 +98,7 @@ public class Main {
 | 
			
		||||
        } catch (ParseException e) {
 | 
			
		||||
            System.err.println(e.getLocalizedMessage());
 | 
			
		||||
 | 
			
		||||
            var writer = new PrintWriter(System.err);
 | 
			
		||||
            var writer = new PrintWriter(System.err, false, StandardCharsets.UTF_8);
 | 
			
		||||
            new HelpFormatter().printUsage(writer, HelpFormatter.DEFAULT_WIDTH, "standalone.jar", options);
 | 
			
		||||
            writer.flush();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,12 +30,12 @@ The code for this is split into three separate components:
 | 
			
		||||
## Static content
 | 
			
		||||
Rendering the static portion of the website is fortunately much simpler.
 | 
			
		||||
 | 
			
		||||
 - Doc generation: This is mostly handled in various Gradle files. The Forge Gradle script uses [cct-javadoc] to convert
 | 
			
		||||
 - Doc generation: This is mostly handled in various Gradle files. The `common` project uses [cct-javadoc] to convert
 | 
			
		||||
   Javadoc on our peripherals and APIs to LDoc/[illuaminate] compatible documentation. This is then fed into illuaminate
 | 
			
		||||
   which spits out HTML.
 | 
			
		||||
 | 
			
		||||
 - `src/htmlTransform`: We do a small amount of post-processing on the HTML in order. This project does syntax
 | 
			
		||||
   highlighting of non-Lua code blocks, and replaces special `<mc-recipe>` tags with a rendered view of a given
 | 
			
		||||
 - `src/htmlTransform`: We do a small amount of post-processing on the HTML, which is performed by this tool. This includes
 | 
			
		||||
   syntax highlighting of non-Lua code blocks, and replacing special `<mc-recipe>` tags with a rendered view of a given
 | 
			
		||||
   Minecraft recipe.
 | 
			
		||||
 | 
			
		||||
[TeaVM]: https://github.com/konsoletyper/teavm "TeaVM - Compiler of Java bytecode to JavaScript"
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,7 @@ val illuaminateDocs by tasks.registering(cc.tweaked.gradle.IlluaminateExecToDir:
 | 
			
		||||
    // Sources
 | 
			
		||||
    inputs.files(rootProject.fileTree("doc")).withPropertyName("docs")
 | 
			
		||||
    inputs.files(project(":core").fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
 | 
			
		||||
    inputs.files(project(":forge").tasks.named("luaJavadoc"))
 | 
			
		||||
    inputs.files(project(":common").tasks.named("luaJavadoc"))
 | 
			
		||||
    // Assets
 | 
			
		||||
    inputs.files(rollup)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -87,21 +87,16 @@ public final class StaticGenerator<T> {
 | 
			
		||||
        var name = method.getDeclaringClass().getName() + "." + method.getName();
 | 
			
		||||
        var modifiers = method.getModifiers();
 | 
			
		||||
 | 
			
		||||
        // Instance methods must be final - this prevents them being overridden and potentially exposed twice.
 | 
			
		||||
        if (!Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) {
 | 
			
		||||
        // Methods must be final - this prevents them being overridden and potentially exposed twice.
 | 
			
		||||
        if (!Modifier.isFinal(modifiers) && !Modifier.isFinal(method.getDeclaringClass().getModifiers())) {
 | 
			
		||||
            System.err.printf("Lua Method %s should be final.\n", name);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!Modifier.isPublic(modifiers)) {
 | 
			
		||||
        if (!Modifier.isPublic(modifiers) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
 | 
			
		||||
            System.err.printf("Lua Method %s should be a public method.\n", name);
 | 
			
		||||
            return Optional.empty();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
 | 
			
		||||
            System.err.printf("Lua Method %s should be on a public class.\n", name);
 | 
			
		||||
            return Optional.empty();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var exceptions = method.getExceptionTypes();
 | 
			
		||||
        for (var exception : exceptions) {
 | 
			
		||||
            if (exception != LuaException.class) {
 | 
			
		||||
@@ -116,11 +111,8 @@ public final class StaticGenerator<T> {
 | 
			
		||||
            return Optional.empty();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // We have some rather ugly handling of static methods in both here and the main generate function. Static methods
 | 
			
		||||
        // only come from generic sources, so this should be safe.
 | 
			
		||||
        var target = Modifier.isStatic(modifiers) ? method.getParameterTypes()[0] : method.getDeclaringClass();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            var target = method.getDeclaringClass();
 | 
			
		||||
            var bytes = generate(classPrefix + method.getDeclaringClass().getSimpleName() + "$" + method.getName(), target, method, annotation.unsafe());
 | 
			
		||||
            if (bytes == null) return Optional.empty();
 | 
			
		||||
 | 
			
		||||
@@ -170,11 +162,9 @@ public final class StaticGenerator<T> {
 | 
			
		||||
            var mw = cw.visitMethod(ACC_PUBLIC, METHOD_NAME, methodDesc, null, EXCEPTIONS);
 | 
			
		||||
            mw.visitCode();
 | 
			
		||||
 | 
			
		||||
            // If we're an instance method, load the target as the first argument.
 | 
			
		||||
            if (!Modifier.isStatic(targetMethod.getModifiers())) {
 | 
			
		||||
                mw.visitVarInsn(ALOAD, 1);
 | 
			
		||||
                mw.visitTypeInsn(CHECKCAST, Type.getInternalName(target));
 | 
			
		||||
            }
 | 
			
		||||
            // Load the target as the first argument.
 | 
			
		||||
            mw.visitVarInsn(ALOAD, 1);
 | 
			
		||||
            mw.visitTypeInsn(CHECKCAST, Type.getInternalName(target));
 | 
			
		||||
 | 
			
		||||
            var argIndex = 0;
 | 
			
		||||
            for (var genericArg : targetMethod.getGenericParameterTypes()) {
 | 
			
		||||
@@ -184,7 +174,7 @@ public final class StaticGenerator<T> {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            mw.visitMethodInsn(
 | 
			
		||||
                Modifier.isStatic(targetMethod.getModifiers()) ? INVOKESTATIC : INVOKEVIRTUAL,
 | 
			
		||||
                INVOKEVIRTUAL,
 | 
			
		||||
                Type.getInternalName(targetMethod.getDeclaringClass()), targetMethod.getName(),
 | 
			
		||||
                Type.getMethodDescriptor(targetMethod), false
 | 
			
		||||
            );
 | 
			
		||||
@@ -244,12 +234,10 @@ public final class StaticGenerator<T> {
 | 
			
		||||
            if (klass == null) return null;
 | 
			
		||||
 | 
			
		||||
            if (klass == String.class) {
 | 
			
		||||
                mw.visitTypeInsn(NEW, INTERNAL_COERCED);
 | 
			
		||||
                mw.visitInsn(DUP);
 | 
			
		||||
                mw.visitVarInsn(ALOAD, 2 + context.size());
 | 
			
		||||
                loadInt(mw, argIndex);
 | 
			
		||||
                mw.visitMethodInsn(INVOKEINTERFACE, INTERNAL_ARGUMENTS, "getStringCoerced", "(I)Ljava/lang/String;", true);
 | 
			
		||||
                mw.visitMethodInsn(INVOKESPECIAL, INTERNAL_COERCED, "<init>", "(Ljava/lang/Object;)V", false);
 | 
			
		||||
                loadCoerced(mw, argIndex, "getStringCoerced", "(I)Ljava/lang/String;");
 | 
			
		||||
                return true;
 | 
			
		||||
            } else if (klass == ByteBuffer.class) {
 | 
			
		||||
                loadCoerced(mw, argIndex, "getBytesCoerced", "(I)Ljava/nio/ByteBuffer;");
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -324,6 +312,15 @@ public final class StaticGenerator<T> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void loadCoerced(MethodVisitor mw, int argIndex, String getter, String getterType) {
 | 
			
		||||
        mw.visitTypeInsn(NEW, INTERNAL_COERCED);
 | 
			
		||||
        mw.visitInsn(DUP);
 | 
			
		||||
        mw.visitVarInsn(ALOAD, 2 + context.size());
 | 
			
		||||
        loadInt(mw, argIndex);
 | 
			
		||||
        mw.visitMethodInsn(INVOKEINTERFACE, INTERNAL_ARGUMENTS, getter, getterType, true);
 | 
			
		||||
        mw.visitMethodInsn(INVOKESPECIAL, INTERNAL_COERCED, "<init>", "(Ljava/lang/Object;)V", false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("Guava")
 | 
			
		||||
    static <T, U> com.google.common.base.Function<T, U> catching(Function<T, U> function, U def) {
 | 
			
		||||
        return x -> {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ public class TComputerThread implements ComputerScheduler {
 | 
			
		||||
 | 
			
		||||
    private static final ArrayDeque<ExecutorImpl> executors = new ArrayDeque<>();
 | 
			
		||||
    private static final TimerHandler callback = TComputerThread::workOnce;
 | 
			
		||||
    private static boolean enqueued;
 | 
			
		||||
 | 
			
		||||
    public TComputerThread(int threads) {
 | 
			
		||||
    }
 | 
			
		||||
@@ -38,6 +39,8 @@ public class TComputerThread implements ComputerScheduler {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void workOnce() {
 | 
			
		||||
        enqueued = false;
 | 
			
		||||
 | 
			
		||||
        var executor = executors.poll();
 | 
			
		||||
        if (executor == null) throw new IllegalStateException("Working, but executor is null");
 | 
			
		||||
 | 
			
		||||
@@ -50,7 +53,14 @@ public class TComputerThread implements ComputerScheduler {
 | 
			
		||||
        }
 | 
			
		||||
        executor.afterWork();
 | 
			
		||||
 | 
			
		||||
        if (!executors.isEmpty()) Callbacks.setImmediate(callback);
 | 
			
		||||
        if (!executors.isEmpty()) enqueue();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void enqueue() {
 | 
			
		||||
        if (enqueued) return;
 | 
			
		||||
 | 
			
		||||
        enqueued = true;
 | 
			
		||||
        Callbacks.setImmediate(callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -75,8 +85,8 @@ public class TComputerThread implements ComputerScheduler {
 | 
			
		||||
            if (onQueue) return;
 | 
			
		||||
            onQueue = true;
 | 
			
		||||
 | 
			
		||||
            if (executors.isEmpty()) Callbacks.setImmediate(callback);
 | 
			
		||||
            executors.add(this);
 | 
			
		||||
            enqueue();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void beforeWork() {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user