mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-01 22:22: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