mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2026-09-08 18:39:14 +00:00
Merge branch 'mc-1.21.x' into mc-26.1
This commit is contained in:
@@ -8,7 +8,8 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
The [`computer_command`] event is fired when the `/computercraft queue` command is run for the current computer.
|
||||
The [`computer_command`] event is fired when the [`/computercraft queue` command](../reference/computercraft_command.html#queue)
|
||||
is run for the current command computer.
|
||||
|
||||
## Return Values
|
||||
1. [`string`]: The event name.
|
||||
|
||||
@@ -22,9 +22,10 @@ wave. However, this signal is continuous, and so can't be used directly by a com
|
||||
the amplitude of the wave many times a second and then *quantise* that amplitude, rounding it to the nearest
|
||||
representable value.
|
||||
|
||||
This representation of sound - a long, uniformally sampled list of amplitudes is referred to as [Pulse-code
|
||||
This representation of sound - a long, uniformly sampled list of amplitudes is referred to as [Pulse-code
|
||||
Modulation][PCM] (PCM). PCM can be thought of as the "standard" audio format, as it's incredibly easy to work with. For
|
||||
instance, to mix two pieces of audio together, you can just add samples from the two tracks together and take the average.
|
||||
instance, to mix two pieces of audio together, you can just add samples from the two tracks together and take the
|
||||
average.
|
||||
|
||||
CC: Tweaked's speakers also work with PCM audio. It plays back 48,000 samples a second, where each sample is an integer
|
||||
between -128 and 127. This is more commonly referred to as 48kHz and an 8-bit resolution.
|
||||
@@ -105,7 +106,7 @@ sound quality. However, due to CC: Tweaked's limited processing power, it's not
|
||||
computer. Instead, we need something much simpler.
|
||||
|
||||
DFPWM (Dynamic Filter Pulse Width Modulation) is the de facto standard audio format of the ComputerCraft (and
|
||||
OpenComputers) world. Originally popularised by the addon mod [Computronics], CC:T now has built-in support for it with
|
||||
OpenComputers) world. Originally popularised by the add-on mod [Computronics], CC:T now has built-in support for it with
|
||||
the [`cc.audio.dfpwm`] module. This allows you to read DFPWM files from disk, decode them to PCM, and then play them
|
||||
using the speaker.
|
||||
|
||||
@@ -128,7 +129,7 @@ end
|
||||
Once again, we see the [`speaker.playAudio`]/[`speaker_audio_empty`] loop. However, the rest of the program is a little
|
||||
different.
|
||||
|
||||
First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
|
||||
First, we require the DFPWM module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
|
||||
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
|
||||
|
||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPWM uses a single bit for each
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
module: [kind=guide] startup
|
||||
see: reference!startup
|
||||
---
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2026 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
# Running programs on computer startup
|
||||
It's often useful to automatically start running a program when a computer is turned on, such as when running a [GPS
|
||||
host][`gps_setup`]. This can be done with a *startup file*.
|
||||
|
||||
Create a file called `startup.lua` in the root of your directory with `edit /startup.lua`, and the code you want to run
|
||||
on startup. Here we'll use [`shell.run`] to run the `hello` program.
|
||||
|
||||
```lua {data-snippet=basic data-mount=basic:startup.lua}
|
||||
shell.run("hello")
|
||||
```
|
||||
|
||||
## Multiple startup files
|
||||
Startup files may also be used to define [shell autocompletions](`shell.setCompletionFunction`),
|
||||
[settings][`settings.define`] or other setup. In those cases, it's may be useful to split your startup files into
|
||||
separate files. In addition to `startup.lua`, computers will also run any file from the `startup/` directory.
|
||||
|
||||
Let's create a basic program called `example` which reads a setting, and either prints its value or toggles it:
|
||||
|
||||
```lua {data-snippet=example data-no-run=1}
|
||||
local cmd = ...
|
||||
if cmd == "get" then
|
||||
print(settings.get("example"))
|
||||
elseif cmd == "toggle" then
|
||||
settings.set("example", not settings.get("example"))
|
||||
settings.save()
|
||||
print("Toggled our setting")
|
||||
else
|
||||
error("Unknown command", 0)
|
||||
end
|
||||
```
|
||||
|
||||
We can then create a startup file at `startup/example.lua` which adds completion and settings for this program:
|
||||
|
||||
```lua {data-snippet=example_startup data-mount=empty:startup.lua,example_startup:startup/example.lua,example:example.lua}
|
||||
settings.define("example", { type = "boolean", default = true })
|
||||
|
||||
local completion = require "cc.shell.completion"
|
||||
shell.setCompletionFunction("example.lua", completion.build(
|
||||
{ completion.choice, { "get", "toggle" } }
|
||||
))
|
||||
```
|
||||
|
||||
After running this startup file, typing `example` in the shell should provide auto-complete for the program. Now, we can
|
||||
add another startup file at `startup/hello.lua`, which (again) runs `hello`:
|
||||
|
||||
```lua {data-snippet=hello_startup data-mount=empty:startup.lua,example_startup:startup/example.lua,example:example.lua,hello_startup:startup/hello.lua}
|
||||
shell.run("hello")
|
||||
```
|
||||
|
||||
See that both startup files are run!
|
||||
+1
-1
@@ -43,7 +43,7 @@ management systems.
|
||||
While ComputerCraft is lovely for both experienced programmers and for people who have never coded before, it can be a
|
||||
little daunting getting started. Thankfully, there's several fantastic tutorials out there:
|
||||
|
||||
- [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
|
||||
- [direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
|
||||
- [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
|
||||
- [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ usage. The `dump` subcommand accepts a list of other fields to display, instead
|
||||
|
||||
|
||||
### `/computercraft queue` {#queue}
|
||||
The queue subcommand allows non-operator players to queue a `computer_command` event on *command* computers.
|
||||
The queue subcommand allows non-operator players to queue a [`computer_command`] event on *command* computers.
|
||||
|
||||
This has a similar purpose to vanilla's [`/trigger`] command. Command computers may choose to listen to this event, and
|
||||
then perform some action.
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
---
|
||||
module: [kind=reference] exceptions
|
||||
---
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2026 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
# CraftOS's exception protocol
|
||||
By default, Lua represents errors are plain strings. The file name and line number may be perpended to the error message
|
||||
(e.g. `example.lua:12: error message`), but all other information (e.g. stack trace) is lost.
|
||||
|
||||
In order to preserve this information across APIs which support catching errors ([`pcall`], [`coroutine.resume`]),
|
||||
CraftOS supports a richer form of errors that capture the context they were thrown in, referred to as
|
||||
"exceptions". These exceptions can be created by coroutine managers (such as [`parallel`]) to preserve error information
|
||||
across coroutine boundaries, allowing the shell and Lua REPL to display the precise source and location of the error.
|
||||
|
||||
An exception is defined as a table with:
|
||||
- A `message` field of type [`string`].
|
||||
- A `thread` field, of type [`thread`][`coroutine`].
|
||||
- A metatable, with a `__name = "exception"` field.
|
||||
|
||||
## Example
|
||||
As an example, let's consider the simplest coroutine manager, that just spawns a new coroutine and forwards events to
|
||||
it. When we error inside the coroutine, notice that shell does not display a rich error, as the stack trace information
|
||||
is lost!
|
||||
|
||||
```lua {data-snippet=run_basic data-no-run=1}
|
||||
local function run(fn, ...)
|
||||
local co = coroutine.create(fn)
|
||||
local ok, result = coroutine.resume(co, ...)
|
||||
while coroutine.status(co) ~= "dead" do
|
||||
local event = table.pack(os.pullEventRaw())
|
||||
if result == nil or filter == "terminated" or event[1] == filter then
|
||||
ok, result = table.pack(coroutine.resume(co, table.unpack(event, 1, event.n)))
|
||||
end
|
||||
end
|
||||
|
||||
if not ok then error(result, 0) end
|
||||
end
|
||||
|
||||
return run
|
||||
```
|
||||
|
||||
```lua {data-mount=run_basic:run.lua}
|
||||
local run = require "run"
|
||||
run(function()
|
||||
error("ohno")
|
||||
end)
|
||||
```
|
||||
|
||||
We can fix this by updating our coroutine manager to throw an exception when an error occurs. The shell now prints a
|
||||
rich error on exit.
|
||||
|
||||
```lua {data-snippet=run_exn data-no-run=1}
|
||||
-- NEW: Define our exception metatable.
|
||||
local exception_mt = {
|
||||
__name = "exception",
|
||||
__tostring = function(self) return self.message end
|
||||
}
|
||||
|
||||
local function run(fn, ...)
|
||||
local co = coroutine.create(fn)
|
||||
local ok, result = coroutine.resume(co, ...)
|
||||
while coroutine.status(co) ~= "dead" do
|
||||
local event = table.pack(os.pullEventRaw())
|
||||
if result == nil or filter == "terminated" or event[1] == filter then
|
||||
ok, result = table.pack(coroutine.resume(co, table.unpack(event, 1, event.n)))
|
||||
end
|
||||
end
|
||||
|
||||
if not ok then
|
||||
-- NEW: If we have a string error, wrap that into an exception instead
|
||||
if type(result) == "string" then
|
||||
error(setmetatable({ message = result, thread = co }, exception_mt))
|
||||
else
|
||||
error(result, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return run
|
||||
```
|
||||
|
||||
```lua {data-mount=run_exn:run.lua}
|
||||
local run = require "run"
|
||||
run(function()
|
||||
error("ohno")
|
||||
end)
|
||||
```
|
||||
|
||||
|
||||
## [`parallel`] and exceptions
|
||||
One issue you may find with the above pattern is exceptions are *not* thrown by [`parallel`] functions. For instance,
|
||||
this program has the same issue as before, and does not print a rich error.
|
||||
|
||||
```lua {data-mount=run_exn:run.lua}
|
||||
local run = require "run"
|
||||
run(function()
|
||||
parallel.waitForAny(function()
|
||||
error("ohno")
|
||||
end)
|
||||
end)
|
||||
```
|
||||
|
||||
This is done by default to preserve the backwards compatibility of CraftOS. Some user code catches errors thrown from
|
||||
within [`parallel`] functions, and inspects the errors, and trying to convert those errors into exceptions will break
|
||||
that code. However, if you run a this code *without* our `run` function, you'll notice the rich error is displayed:
|
||||
|
||||
```lua
|
||||
parallel.waitForAny(function()
|
||||
error("ohno")
|
||||
end)
|
||||
```
|
||||
|
||||
Internally, [`parallel`] functions attempt to determine whether an error message is captured by user code (with
|
||||
[`pcall`]/[`xpcall`] or [`coroutine.resume`]). If the error is never observed, then it's safe to wrap it into an
|
||||
exception!
|
||||
|
||||
What we're seeing here is that [`parallel`] doesn't know anything about our coroutine manager, and so assumes it's not
|
||||
safe to throw an exception.
|
||||
|
||||
This can be fixed by making the first function in our child coroutine the magic `debug.getregistry().try_barrier`
|
||||
function. This function:
|
||||
- Accepts a "context", the function to call, and the function's arguments as parameters, then immediately calls the
|
||||
function.
|
||||
- The "context" is a table with:
|
||||
- A `co` field, containing the parent coroutine.
|
||||
- An optional `can_wrap` field, indicating whether exceptions can be wrapped or not.
|
||||
|
||||
Support for this in our coroutine manager looks as follows:
|
||||
|
||||
```lua {data-snippet=run_barrier data-no-run=1}
|
||||
-- NEW: Define our magic try_barrier function:
|
||||
local try_barrier = debug.getregistry().cc_try_barrier
|
||||
if not try_barrier then
|
||||
local function bounce(...) return ... end
|
||||
try_barrier = function(parent, f, ...) return bounce(f(...)) end
|
||||
debug.getregistry().cc_try_barrier = try_barrier
|
||||
end
|
||||
|
||||
local exception_mt = {
|
||||
__name = "exception",
|
||||
__tostring = function(self) return self.message end
|
||||
}
|
||||
|
||||
local function run(fn, ...)
|
||||
-- NEW: Start our coroutine using the try_barrier function instead. We use
|
||||
-- { can_wrap = true } to tell parallel that it can always wrap errors into
|
||||
-- exceptions.
|
||||
local co = coroutine.create(try_barrier)
|
||||
local ok, result = coroutine.resume(co, { co = co, can_wrap = true }, fn, ...)
|
||||
|
||||
while coroutine.status(co) ~= "dead" do
|
||||
local event = table.pack(os.pullEventRaw())
|
||||
if result == nil or filter == "terminated" or event[1] == filter then
|
||||
ok, result = table.pack(coroutine.resume(co, table.unpack(event, 1, event.n)))
|
||||
end
|
||||
end
|
||||
|
||||
if not ok then
|
||||
if type(result) == "string" then
|
||||
error(setmetatable({ message = result, thread = co }, exception_mt))
|
||||
else
|
||||
error(result, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return run
|
||||
```
|
||||
|
||||
```lua {data-mount=run_barrier:run.lua}
|
||||
local run = require "run"
|
||||
run(function()
|
||||
parallel.waitForAny(function()
|
||||
error("ohno")
|
||||
end)
|
||||
end)
|
||||
```
|
||||
@@ -144,7 +144,7 @@ If this item can be damaged (e.g. a pickaxe), then its damage and durability wil
|
||||
- `maxDamage: number`: The maximum amount of damage this item has taken.
|
||||
- `durability?: number`: If this item is damaged (i.e. the durability bar is visible), the percentage left on the
|
||||
durability bar, between 0 and 1 (inclusive).
|
||||
- `unbreakable?: boolean`: `true`, if the item is nubreakable
|
||||
- `unbreakable?: boolean`: `true`, if the item is unbreakable
|
||||
|
||||
### Example
|
||||
An unused diamond pickaxe:
|
||||
@@ -197,7 +197,7 @@ A diamond pickaxe with Efficiency V:
|
||||
```
|
||||
|
||||
## Potion effects
|
||||
The effects this potion (or potion-embued item, such as a tipped arrow) has:
|
||||
The effects this potion (or potion-imbued item, such as a tipped arrow) has:
|
||||
|
||||
- `potionEffects: { table... }`: The effects this item has. Each potion effect is a table containing several
|
||||
properties:
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
module: [kind=reference] startup
|
||||
see: guide!startup
|
||||
---
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2026 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
# Computer startup
|
||||
When a computer turns on, it searches for files to run as part of the startup process. This page details this process.
|
||||
|
||||
For information about creating a basic startup file, see [`guide!startup`].
|
||||
|
||||
1. `/rom/autorun`: Computers first look in the `/rom/autorun` folder, and run every file in that folder. This folder is
|
||||
empty by default, but may be extended by datapacks or other mods. See the [example
|
||||
datapack](https://github.com/cc-tweaked/datapack-example) for an example.
|
||||
|
||||
2. If the `shell.allow_disk_startup` [setting][`settings`] is `true`, then connected disk drives are searched for a
|
||||
`startup` file, `startup.lua` file, or `startup/` directory. The first disk containing these files will be used for
|
||||
startup.
|
||||
|
||||
1. The `startup` (*or* `startup.lua`) file will be run.
|
||||
2. All programs under `startup/` will be run.
|
||||
|
||||
The order disks are iterated over is not defined, and so it is recommended to only have one disk containing startup
|
||||
files connected to a computer.
|
||||
|
||||
3. If no startup files are found on a disk, and the `shell.allow_startup` [setting][`settings`] is `true`, then the
|
||||
root directory is searched for startup files in the same way (`startup` *or* `startup.lua`, then all files in
|
||||
`startup/`).
|
||||
|
||||
When listing a files from a directory (either `startup/` or `rom/autorun`), the result of [`fs.list`] is used
|
||||
directly. This will always return files in lexicographical order. This means that `startup/a.lua` will always run before
|
||||
`startup/b.lua`.
|
||||
+2
-3
@@ -8,11 +8,10 @@ org.gradle.parallel=true
|
||||
kotlin.stdlib.default.dependency=false
|
||||
kotlin.jvm.target.validation.mode=error
|
||||
|
||||
neogradle.subsystems.conventions.runs.enabled=false
|
||||
|
||||
# Mod properties
|
||||
modVersion=1.120.0
|
||||
|
||||
isUnstable=true
|
||||
modVersion=1.119.0
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=26.1.2
|
||||
|
||||
@@ -56,7 +56,7 @@ cctJavadoc = "1.9.0"
|
||||
checkstyle = "13.4.1"
|
||||
errorProne-core = "2.49.0"
|
||||
errorProne-plugin = "4.3.0"
|
||||
fabric-loom = "1.16.1"
|
||||
fabric-loom = "1.17.11"
|
||||
githubRelease = "2.5.2"
|
||||
gradleVersions = "0.54.0"
|
||||
ideaExt = "1.3"
|
||||
@@ -67,7 +67,7 @@ modDevGradle = "2.0.141"
|
||||
nullAway = "0.13.4"
|
||||
shadow = "9.4.1"
|
||||
spotless = "8.4.0"
|
||||
teavm = "0.14.0"
|
||||
teavm = "0.15.0"
|
||||
vanillaExtract = "0.3.1"
|
||||
versionCatalogUpdate = "1.1.0"
|
||||
|
||||
@@ -154,8 +154,7 @@ teavm-core = { module = "org.teavm:teavm-core", version.ref = "teavm" }
|
||||
teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" }
|
||||
teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" }
|
||||
teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" }
|
||||
teavm-metaprogramming-api = { module = "org.teavm:teavm-metaprogramming-api", version.ref = "teavm" }
|
||||
teavm-metaprogramming-impl = { module = "org.teavm:teavm-metaprogramming-impl", version.ref = "teavm" }
|
||||
teavm-extension-apis = { module = "org.teavm:teavm-extension-apis", version.ref = "teavm" }
|
||||
teavm-platform = { module = "org.teavm:teavm-platform", version.ref = "teavm" }
|
||||
teavm-tooling = { module = "org.teavm:teavm-tooling", version.ref = "teavm" }
|
||||
vanillaExtract = { module = "cc.tweaked.vanilla-extract:plugin", version.ref = "vanillaExtract" }
|
||||
@@ -182,5 +181,5 @@ test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||
testRuntime = ["junit-jupiter-engine", "junit-platform-launcher", "jqwik-engine"]
|
||||
|
||||
# Build tools
|
||||
teavm-api = ["teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api"]
|
||||
teavm-tooling = ["teavm-tooling", "teavm-metaprogramming-impl", "teavm-jso-impl"]
|
||||
teavm-api = ["teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-extension-apis"]
|
||||
teavm-tooling = ["teavm-tooling", "teavm-jso-impl"]
|
||||
|
||||
Generated
+298
-322
File diff suppressed because it is too large
Load Diff
+11
-2
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.shared.pocket.items;
|
||||
|
||||
import dan200.computercraft.annotations.FabricOverride;
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
@@ -149,10 +150,18 @@ public class PocketComputerItem extends Item {
|
||||
return UpgradeManager.getName(getDescriptionId(), getUpgrade(stack, PocketSide.BACK), getUpgrade(stack, PocketSide.BOTTOM));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getCreatorModId(ItemStack stack) {
|
||||
return PocketUpgrades.instance().getOwner(getUpgradeWithData(stack, PocketSide.BACK), getUpgradeWithData(stack, PocketSide.BOTTOM));
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
public String getCreatorModId(HolderLookup.Provider registries, ItemStack stack) {
|
||||
return PocketUpgrades.instance().getOwner(getUpgradeWithData(stack, PocketSide.BACK), getUpgradeWithData(stack, PocketSide.BOTTOM));
|
||||
return getCreatorModId(stack);
|
||||
}
|
||||
|
||||
@FabricOverride
|
||||
public String getCreatorNamespace(ItemStack stack) {
|
||||
return getCreatorModId(stack);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+11
-2
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.shared.turtle.items;
|
||||
|
||||
import dan200.computercraft.annotations.FabricOverride;
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
@@ -29,10 +30,18 @@ public class TurtleItem extends BlockItem {
|
||||
return UpgradeManager.getName(getDescriptionId(), getUpgrade(stack, TurtleSide.LEFT), getUpgrade(stack, TurtleSide.RIGHT));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getCreatorModId(ItemStack stack) {
|
||||
return TurtleUpgrades.instance().getOwner(getUpgradeWithData(stack, TurtleSide.LEFT), getUpgradeWithData(stack, TurtleSide.RIGHT));
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
public String getCreatorModId(HolderLookup.Provider registries, ItemStack stack) {
|
||||
return TurtleUpgrades.instance().getOwner(getUpgradeWithData(stack, TurtleSide.LEFT), getUpgradeWithData(stack, TurtleSide.RIGHT));
|
||||
return getCreatorModId(stack);
|
||||
}
|
||||
|
||||
@FabricOverride
|
||||
public String getCreatorNamespace(ItemStack stack) {
|
||||
return getCreatorModId(stack);
|
||||
}
|
||||
|
||||
public static @Nullable ITurtleUpgrade getUpgrade(ItemStack stack, TurtleSide side) {
|
||||
|
||||
+61
-55
@@ -6,14 +6,12 @@ package dan200.computercraft.core.apis.http.options;
|
||||
|
||||
import com.google.common.net.InetAddresses;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A predicate on an address. Matches against a domain and an ip address.
|
||||
@@ -33,25 +31,27 @@ interface AddressPredicate {
|
||||
private final byte[] min;
|
||||
private final byte[] max;
|
||||
|
||||
HostRange(byte[] min, byte[] max) {
|
||||
private HostRange(byte[] min, byte[] max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(InetAddress address) {
|
||||
var entry = address.getAddress();
|
||||
if (entry.length != min.length) return false;
|
||||
|
||||
for (var i = 0; i < entry.length; i++) {
|
||||
var value = 0xFF & entry[i];
|
||||
if (value < (0xFF & min[i]) || value > (0xFF & max[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return matches(address.getAddress());
|
||||
}
|
||||
|
||||
public static HostRange parse(String addressStr, String prefixSizeStr) {
|
||||
private boolean matches(byte[] address) {
|
||||
return address.length == min.length && Arrays.compareUnsigned(min, address) <= 0 && Arrays.compareUnsigned(address, max) <= 0;
|
||||
}
|
||||
|
||||
private static HostRange parse(String cidr) {
|
||||
var idx = cidr.lastIndexOf('/');
|
||||
if (idx < 0) throw new InvalidRuleException(String.format("Invalid host '%s', not in CIDR notation", cidr));
|
||||
return HostRange.parse(cidr.substring(0, idx), cidr.substring(idx + 1));
|
||||
}
|
||||
|
||||
static HostRange parse(String addressStr, String prefixSizeStr) {
|
||||
int prefixSize;
|
||||
try {
|
||||
prefixSize = Integer.parseInt(prefixSizeStr);
|
||||
@@ -72,10 +72,6 @@ interface AddressPredicate {
|
||||
));
|
||||
}
|
||||
|
||||
return parse(address, prefixSize);
|
||||
}
|
||||
|
||||
public static HostRange parse(InetAddress address, int prefixSize) {
|
||||
// Mask the bytes of the IP address.
|
||||
byte[] minBytes = address.getAddress(), maxBytes = address.getAddress();
|
||||
var size = prefixSize;
|
||||
@@ -113,13 +109,18 @@ interface AddressPredicate {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches any private/reserved IP address.
|
||||
*
|
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc6890">RFC 6890</a>
|
||||
* @see <a href="https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml">IPv4 Special-Purpose Address Space</a>
|
||||
* @see <a href="https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml">IPv6 Special-Purpose Address Space</a>
|
||||
*/
|
||||
final class PrivatePattern implements AddressPredicate {
|
||||
static final PrivatePattern INSTANCE = new PrivatePattern();
|
||||
|
||||
private static final Set<InetAddress> additionalAddresses = Arrays.stream(new String[]{
|
||||
// Block various cloud providers internal IPs.
|
||||
"192.0.0.192", // Oracle
|
||||
}).map(InetAddresses::forString).collect(Collectors.toUnmodifiableSet());
|
||||
private PrivatePattern() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(InetAddress socketAddress) {
|
||||
@@ -128,43 +129,48 @@ interface AddressPredicate {
|
||||
|| socketAddress.isLinkLocalAddress() // 169.254.0.0/16, fe80::/10
|
||||
|| socketAddress.isSiteLocalAddress() // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fec0::/10
|
||||
|| socketAddress.isMulticastAddress() // 224.0.0.0/4, ff00::/8
|
||||
|| isUniqueLocalAddress(socketAddress) // fd00::/8
|
||||
|| isCarrierGradeNatAddress(socketAddress) // 100.64.0.0/10
|
||||
|| NAT64_RANGE.matches(socketAddress) // 64:ff9b::/96
|
||||
|| additionalAddresses.contains(socketAddress);
|
||||
|| isAnyAdditional(socketAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an IP address lives inside the ULA address range.
|
||||
*
|
||||
* @param address The IP address to test.
|
||||
* @return Whether this address sits in the ULA address range.
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Unique_local_address">Unique local address on Wikipedia</a>
|
||||
* Additional address ranges reserved by IANA.
|
||||
*/
|
||||
private boolean isUniqueLocalAddress(InetAddress address) {
|
||||
// ULA is actually defined as fc00::/7 (so both fc00::/8 and fd00::/8). However, only the latter is actually
|
||||
// defined right now, so let's be conservative.
|
||||
return address instanceof Inet6Address && (address.getAddress()[0] & 0xff) == 0xfd;
|
||||
}
|
||||
private static final List<HostRange> ADDITIONAL_RANGES = Stream.of(
|
||||
// Shared Address Space ([RFC 6598](https://datatracker.ietf.org/doc/html/rfc6598)), used for
|
||||
// [Carrier-grade NAT](https://en.wikipedia.org/wiki/Carrier-grade_NAT).
|
||||
"100.64.0.0/10",
|
||||
// IETF Protocol Assignments.
|
||||
"192.0.0.0/24",
|
||||
// TEST-NET-1 ([RFC 5737](https://datatracker.ietf.org/doc/html/rfc5737)).
|
||||
"192.0.2.0/24",
|
||||
// 6to4 Relay Anycast ([RFC 3068](https://datatracker.ietf.org/doc/html/rfc3068)).
|
||||
"192.88.99.0/24",
|
||||
// Network Interconnect Device Benchmark Testing
|
||||
// ([RFC 2544](https://datatracker.ietf.org/doc/html/rfc2544)).
|
||||
"198.18.0.0/15",
|
||||
// TEST-NET-2 ([RFC 5737](https://datatracker.ietf.org/doc/html/rfc5737)).
|
||||
"198.51.100.0/24",
|
||||
// TEST-NET-3 ([RFC 5737](https://datatracker.ietf.org/doc/html/rfc5737)).
|
||||
"203.0.113.0/24",
|
||||
// Reserved ([RFC 1112](https://datatracker.ietf.org/doc/html/rfc1112#section-4)).
|
||||
"192.0.2.0/24",
|
||||
|
||||
/**
|
||||
* Determine if an IP address lives within the CGNAT address range (100.64.0.0/10).
|
||||
*
|
||||
* @param address The IP address to test.
|
||||
* @return Whether this address sits in the CGNAT address range.
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Carrier-grade_NAT">Carrier-grade NAT on Wikipedia</a>
|
||||
*/
|
||||
private boolean isCarrierGradeNatAddress(InetAddress address) {
|
||||
if (!(address instanceof Inet4Address)) return false;
|
||||
var bytes = address.getAddress();
|
||||
return bytes[0] == 100 && ((bytes[1] & 0xFF) >= 64 && (bytes[1] & 0xFF) <= 127);
|
||||
}
|
||||
// IPv4-IPV6 Translation Address ([RFC 6052](https://datatracker.ietf.org/doc/html/rfc6052)).
|
||||
// See also [NAT64 on Wikipedia](https://en.wikipedia.org/wiki/NAT64).
|
||||
"64:ff9b::/96",
|
||||
// The Local-Use IPv4/IPv6 Translation Prefix ([RFC 8215](https://datatracker.ietf.org/doc/html/rfc8215)).
|
||||
"64:ff9b:1::/48",
|
||||
// IETF Protocol Assignments ([RFC 2928](https://datatracker.ietf.org/doc/html/rfc2928)).
|
||||
// This includes various sub-allocations including TEREDO and ORCHID.
|
||||
"2001::/23",
|
||||
// Unique Local address ([RFC 4193](https://datatracker.ietf.org/doc/html/rfc4193)). See also
|
||||
// [Wikipedia](https://en.wikipedia.org/wiki/Unique_local_address).
|
||||
"fc00::/7"
|
||||
).map(HostRange::parse).toList();
|
||||
|
||||
/**
|
||||
* The NAT64 address range (64:ff9b::/96).
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/NAT64">NAT64 on Wikipedia</a>
|
||||
*/
|
||||
private static final HostRange NAT64_RANGE = HostRange.parse(InetAddresses.forString("64:ff9b::"), 96);
|
||||
private static boolean isAnyAdditional(InetAddress address) {
|
||||
var addressBytes = address.getAddress();
|
||||
return ADDITIONAL_RANGES.stream().anyMatch(x -> x.matches(addressBytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ public final class AddressRule {
|
||||
}
|
||||
|
||||
public static AddressRule parse(String filter, OptionalInt port, PartialOptions partial) {
|
||||
var cidr = filter.indexOf('/');
|
||||
var cidr = filter.lastIndexOf('/');
|
||||
if (cidr >= 0) {
|
||||
var addressStr = filter.substring(0, cidr);
|
||||
var prefixSizeStr = filter.substring(cidr + 1);
|
||||
@@ -74,7 +74,7 @@ public final class AddressRule {
|
||||
var ipv4Address = address instanceof Inet6Address inet6 && InetAddresses.is6to4Address(inet6)
|
||||
? InetAddresses.get6to4IPv4Address(inet6) : null;
|
||||
|
||||
for (AddressRule rule : rules) {
|
||||
for (var rule : rules) {
|
||||
if (!rule.matches(domain, port, address, ipv4Address)) continue;
|
||||
options = options.merge(rule.partial);
|
||||
}
|
||||
|
||||
@@ -39,63 +39,15 @@ the other.
|
||||
@since 1.2
|
||||
]]
|
||||
|
||||
local exception = dofile("rom/modules/main/cc/internal/tiny_require.lua")("cc.internal.exception")
|
||||
|
||||
local function create(...)
|
||||
local barrier_ctx = { co = coroutine.running() }
|
||||
|
||||
local functions = table.pack(...)
|
||||
local threads = {}
|
||||
for i = 1, functions.n, 1 do
|
||||
local fn = functions[i]
|
||||
if type(fn) ~= "function" then
|
||||
error("bad argument #" .. i .. " (function expected, got " .. type(fn) .. ")", 3)
|
||||
end
|
||||
|
||||
threads[i] = { co = coroutine.create(function() return exception.try_barrier(barrier_ctx, fn) end), filter = nil }
|
||||
end
|
||||
|
||||
return threads
|
||||
end
|
||||
|
||||
local function runUntilLimit(threads, limit)
|
||||
local count = #threads
|
||||
if count < 1 then return 0 end
|
||||
local living = count
|
||||
|
||||
local event = { n = 0 }
|
||||
while true do
|
||||
for i = 1, count do
|
||||
local thread = threads[i]
|
||||
if thread and (thread.filter == nil or thread.filter == event[1] or event[1] == "terminate") then
|
||||
local ok, param = coroutine.resume(thread.co, table.unpack(event, 1, event.n))
|
||||
if ok then
|
||||
thread.filter = param
|
||||
elseif type(param) == "string" and exception.can_wrap_errors() then
|
||||
error(exception.make_exception(param, thread.co))
|
||||
else
|
||||
error(param, 0)
|
||||
end
|
||||
|
||||
if coroutine.status(thread.co) == "dead" then
|
||||
threads[i] = false
|
||||
living = living - 1
|
||||
if living <= limit then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
event = table.pack(os.pullEventRaw())
|
||||
end
|
||||
end
|
||||
local require = dofile("rom/modules/main/cc/internal/tiny_require.lua")
|
||||
local expect = require("cc.expect").expect
|
||||
local exception = require("cc.internal.exception")
|
||||
|
||||
--[[- Switches between execution of the functions, until any of them
|
||||
finishes. If any of the functions errors, the message is propagated upwards
|
||||
from the [`parallel.waitForAny`] call.
|
||||
|
||||
@tparam function ... The functions this task will run
|
||||
@tparam function ... The functions to run in parallel.
|
||||
@usage Print a message every second until the `q` key is pressed.
|
||||
|
||||
local function tick()
|
||||
@@ -115,15 +67,77 @@ from the [`parallel.waitForAny`] call.
|
||||
print("Everything done!")
|
||||
]]
|
||||
function waitForAny(...)
|
||||
local threads = create(...)
|
||||
return runUntilLimit(threads, #threads - 1)
|
||||
local barrier_ctx = { co = coroutine.running() }
|
||||
|
||||
local functions = table.pack(...)
|
||||
local threads = {}
|
||||
for i = 1, functions.n do
|
||||
local fn = functions[i]
|
||||
expect(i, fn, "function")
|
||||
threads[i] = {
|
||||
co = coroutine.create(function() return exception.try_barrier(barrier_ctx, fn) end),
|
||||
filter = nil,
|
||||
}
|
||||
end
|
||||
|
||||
local count = functions.n
|
||||
if count < 1 then return 0 end
|
||||
|
||||
local event = { n = 0 }
|
||||
while true do
|
||||
for i = 1, count do
|
||||
local thread = threads[i]
|
||||
if thread.filter == nil or thread.filter == event[1] or event[1] == "terminate" then
|
||||
local ok, param = coroutine.resume(thread.co, table.unpack(event, 1, event.n))
|
||||
if not ok then
|
||||
error(exception.wrap_error(param, thread.co), 0)
|
||||
end
|
||||
|
||||
-- Abort if this coroutine has finished
|
||||
if coroutine.status(thread.co) == "dead" then return i end
|
||||
|
||||
thread.filter = param
|
||||
end
|
||||
end
|
||||
|
||||
event = table.pack(os.pullEventRaw())
|
||||
end
|
||||
end
|
||||
|
||||
--[[- Switches between execution of the functions, until all of them are
|
||||
finished. If any of the functions errors, the message is propagated upwards
|
||||
from the [`parallel.waitForAll`] call.
|
||||
--[[-
|
||||
Runs several functions in parallel, until all of them are finished.
|
||||
|
||||
If any of the functions errors, the other functions are not resumed, and the
|
||||
error is propagated upwards.
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> While any number of functions can be run in parallel, running too many things
|
||||
> in parallel can sometimes cause issues:
|
||||
>
|
||||
> - Computers only buffer 256 events at a time. Trying to run several hundred
|
||||
> functions in parallel (particularly when calling peripheral methods) can
|
||||
> cause the event queue to fill up, resulting in events being dropped, and
|
||||
> programs getting stuck.
|
||||
> - Computers only run 16 HTTP requests at a time. Trying to run more than that
|
||||
> in parallel will have no effect.
|
||||
|
||||
### Spawning new parallel functions
|
||||
In some cases, you may want to start running additional functions in parallel
|
||||
from an existing [`parallel.waitForAll`] call. Every function passed to
|
||||
[`waitForAll`] can accept a `spawn` argument, which can be called to spawn new
|
||||
parallel functions.
|
||||
|
||||
```lua
|
||||
parallel.waitForAll(function(spawn)
|
||||
spawn(function() sleep(1); print("Finished 1") end)
|
||||
spawn(function() sleep(2); print("Finished 2") end)
|
||||
end)
|
||||
```
|
||||
|
||||
@tparam function(spawn: function(fn: function, any...)) ... The functions to run
|
||||
in parallel.
|
||||
|
||||
@tparam function ... The functions this task will run
|
||||
@usage Start off two timers and wait for them both to run.
|
||||
|
||||
local function a()
|
||||
@@ -137,8 +151,100 @@ from the [`parallel.waitForAll`] call.
|
||||
|
||||
parallel.waitForAll(a, b)
|
||||
print("Everything done!")
|
||||
|
||||
@usage Generate a list of functions to run in parallel.
|
||||
|
||||
local funcs = {}
|
||||
for i = 1, 5 do
|
||||
table.insert(funcs, function()
|
||||
sleep(math.random())
|
||||
print("Finished " .. i)
|
||||
end)
|
||||
end
|
||||
|
||||
parallel.waitForAll(table.unpack(funcs))
|
||||
print("Everything done!")
|
||||
|
||||
@usage Run new functions in parallel from within `waitForAll`.
|
||||
|
||||
parallel.waitForAll(function(spawn)
|
||||
for i = 1, 5 do
|
||||
spawn(function()
|
||||
sleep(math.random())
|
||||
print("Finished " .. i)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
print("Everything done!")
|
||||
|
||||
@changed 1.120.0 Added ability to spawn new parallel functions.
|
||||
]]
|
||||
function waitForAll(...)
|
||||
local threads = create(...)
|
||||
return runUntilLimit(threads, 0)
|
||||
local barrier_ctx = { co = coroutine.running() }
|
||||
|
||||
local can_spawn, threads, count = false, {}, 0
|
||||
|
||||
local function spawn(fn, ...)
|
||||
expect(1, fn, "function")
|
||||
if not can_spawn then error("Cannot spawn new functions outside of waitForAll", 2) end
|
||||
|
||||
threads[count + 1] = {
|
||||
co = coroutine.create(function(...) return exception.try_barrier(barrier_ctx, fn, ...) end),
|
||||
filter = nil,
|
||||
resume_with = table.pack(...),
|
||||
}
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
local functions = table.pack(...)
|
||||
can_spawn = true
|
||||
for i = 1, functions.n, 1 do
|
||||
local fn = functions[i]
|
||||
expect(i, fn, "function")
|
||||
spawn(fn, spawn)
|
||||
end
|
||||
can_spawn = false
|
||||
|
||||
local event = { n = 0 }
|
||||
while true do
|
||||
local i = 1
|
||||
while i <= count do
|
||||
local thread = threads[i]
|
||||
|
||||
-- If this is a new coroutine, start it with the "resume_with" data,
|
||||
-- otherwise resume it with the event (if it matches).
|
||||
local resume_with
|
||||
if thread.resume_with then
|
||||
resume_with = thread.resume_with
|
||||
thread.resume_with = nil
|
||||
elseif thread.filter == nil or thread.filter == event[1] or event[1] == "terminate" then
|
||||
resume_with = event
|
||||
end
|
||||
|
||||
if resume_with then
|
||||
can_spawn = true
|
||||
local ok, param = coroutine.resume(thread.co, table.unpack(resume_with, 1, resume_with.n))
|
||||
can_spawn = false
|
||||
|
||||
if not ok then
|
||||
error(exception.wrap_error(param, thread.co), 0)
|
||||
end
|
||||
|
||||
if coroutine.status(thread.co) == "dead" then
|
||||
-- If this thread has died, remove it and repeat this
|
||||
-- iteration.
|
||||
table.remove(threads, i)
|
||||
i, count = i - 1, count - 1
|
||||
end
|
||||
|
||||
thread.filter = param
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if count == 0 then return end
|
||||
|
||||
event = table.pack(os.pullEventRaw())
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# New features in CC: Tweaked 1.120.0
|
||||
|
||||
* Support spawning new parallel functions in `parallel.watiForAll`.
|
||||
|
||||
One bug fix:
|
||||
* Make HTTP IP filtering stricter.
|
||||
|
||||
# New features in CC: Tweaked 1.119.0
|
||||
|
||||
* Add `commands.getDimension()`.
|
||||
@@ -320,8 +327,8 @@ Several bug fixes:
|
||||
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()`.
|
||||
* Fix deadlock occurring when adding/removing observers.
|
||||
* Allow placing seeds into composter barrels with `turtle.place()`.
|
||||
|
||||
# New features in CC: Tweaked 1.109.0
|
||||
|
||||
@@ -393,7 +400,7 @@ Several bug fixes:
|
||||
|
||||
Several bug fixes:
|
||||
* Fix client config file being generated on a dedicated server.
|
||||
* Fix numbers ending in "f" or "d" being treated as avalid.
|
||||
* Fix numbers ending in "f" or "d" being treated as valid.
|
||||
* Fix `string.pack`'s "z" specifier causing out-of-bounds errors.
|
||||
* Fix several issues with `turtle.dig`'s custom actions (tilling, making paths).
|
||||
|
||||
@@ -416,7 +423,7 @@ Several bug fixes:
|
||||
|
||||
Several bug fixes:
|
||||
* Fix turtles rendering incorrectly when upside down.
|
||||
* Fix misplaced calls to IArguments.escapes.
|
||||
* Fix misplaced calls to `IArguments.escapes`.
|
||||
* Lua REPL no longer accepts `)(` as a valid expression.
|
||||
* Fix several inconsistencies with `require`/`package.path` in the Lua REPL (Wojbie).
|
||||
* Fix turtle being able to place water buckets outside its reach distance.
|
||||
@@ -597,14 +604,14 @@ Several bug fixes:
|
||||
* Various documentation improvements (MCJack123, FayneAldan).
|
||||
* Allow CC's blocks to be rotated when used in structure blocks (Seniorendi).
|
||||
* Several performance improvements to computer execution.
|
||||
* Add parse_empty_array option to textutils.unserialiseJSON (@ChickChicky).
|
||||
* Add `parse_empty_array` option to `textutils.unserialiseJSON` (@ChickChicky).
|
||||
* Add an API to allow other mods to provide extra item/block details (Lemmmy).
|
||||
* All blocks with GUIs can now be "locked" (via a command or NBT editing tools) like vanilla inventories. Players can only interact with them with a specific named item.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix printouts being rendered with an offset in item frames (coolsa).
|
||||
* Reduce position latency when playing audio with a noisy pocket computer.
|
||||
* Fix total counts in /computercraft turn-on/shutdown commands.
|
||||
* Fix total counts in `/computercraft turn-on|shutdown` commands.
|
||||
* Fix "Run" command not working in the editor when run from a subdirectory (Wojbie).
|
||||
* Pocket computers correctly preserve their on state.
|
||||
|
||||
@@ -656,7 +663,7 @@ Several bug fixes:
|
||||
|
||||
# New features in CC: Tweaked 1.99.1
|
||||
|
||||
* Add package.searchpath to the cc.require API. (MCJack123)
|
||||
* Add `package.searchpath` to the `cc.require` API. (MCJack123)
|
||||
* Provide a more efficient way for the Java API to consume Lua tables in certain restricted cases.
|
||||
|
||||
Several bug fixes:
|
||||
@@ -758,7 +765,7 @@ And several bug fixes:
|
||||
* Fix turtles not updating peripherals when upgrades are unequipped (Ronan-H).
|
||||
* Fix computers not shutting down on fatal errors within the Lua VM.
|
||||
* Speakers now correctly stop playing when broken, and sound follows noisy turtles and pocket computers.
|
||||
* Update the `wget` to be more resiliant in the face of user-errors.
|
||||
* Update the `wget` to be more resilient in the face of user-errors.
|
||||
* Fix exiting `paint` typing "e" in the shell.
|
||||
* Fix coloured pocket computers using the wrong texture.
|
||||
* Correctly render the transparent background on pocket/normal computers.
|
||||
@@ -786,8 +793,8 @@ And several bug fixes:
|
||||
|
||||
Several bug fixes:
|
||||
* Correctly serialise sparse arrays into JSON (livegamer999)
|
||||
* Fix hasAudio/playAudio failing on record discs.
|
||||
* Fix rs.getBundledInput returning the output instead (SkyTheCodeMaster)
|
||||
* Fix `hasAudio`/`playAudio` failing on record discs.
|
||||
* Fix `rs.getBundledInput` returning the output instead (SkyTheCodeMaster)
|
||||
* Programs run via edit are now a little better behaved (Wojbie)
|
||||
* Add User-Agent to a websocket's headers.
|
||||
|
||||
@@ -864,12 +871,12 @@ And several bug fixes:
|
||||
# New features in CC: Tweaked 1.92.0
|
||||
|
||||
* Bump Cobalt version:
|
||||
* Add support for the __pairs metamethod.
|
||||
* string.format now uses the __tostring metamethod.
|
||||
* Add support for the `__pairs` metamethod.
|
||||
* string.format now uses the `__tostring` metamethod.
|
||||
* Add date-specific MOTDs (MCJack123).
|
||||
|
||||
And several bug fixes:
|
||||
* Correctly handle tabs within textutils.unserailizeJSON.
|
||||
* Correctly handle tabs within `textutils.unserailizeJSON`.
|
||||
* Fix sheep not dropping items when sheared by turtles.
|
||||
|
||||
# New features in CC: Tweaked 1.91.1
|
||||
@@ -881,13 +888,13 @@ And several bug fixes:
|
||||
* [Generic peripherals] Expose NBT hashes of items to inventory methods.
|
||||
* Bump Cobalt version:
|
||||
* Optimise handling of string concatenation.
|
||||
* Add string.{pack,unpack,packsize} (MCJack123)
|
||||
* Add `string.{pack,unpack,packsize}` (MCJack123)
|
||||
* Update to 1.16.2
|
||||
|
||||
And several bug fixes:
|
||||
* Escape non-ASCII characters in JSON strings (neumond)
|
||||
* Make field names in fs.attributes more consistent (abby)
|
||||
* Fix textutils.formatTime correctly handle 12 AM (R93950X)
|
||||
* Make field names in `fs.attributes` more consistent (abby)
|
||||
* Fix `textutils.formatTime` correctly handle 12 AM (R93950X)
|
||||
* Fix turtles placing buckets multiple times.
|
||||
|
||||
# New features in CC: Tweaked 1.90.3
|
||||
@@ -907,7 +914,7 @@ And several bug fixes:
|
||||
|
||||
# New features in CC: Tweaked 1.90.0
|
||||
|
||||
* Add cc.image.nft module, for working with nft files. (JakobDev)
|
||||
* Add `cc.image.nft` module, for working with nft files. (JakobDev)
|
||||
* [experimental] Provide a generic peripheral for any tile entity without an existing one. We currently provide methods for working with inventories, fluid tanks and energy storage. This is disabled by default, and must be turned on in the config.
|
||||
* Add configuration to control the sizes of monitors and terminals.
|
||||
* Add configuration to control maximum render distance of monitors.
|
||||
@@ -930,8 +937,8 @@ And several bug fixes:
|
||||
|
||||
* Compress monitor data, reducing network traffic by a significant amount.
|
||||
* Allow limiting the bandwidth monitor updates use.
|
||||
* Several optimisations to monitor rendering (@Lignum).
|
||||
* Expose block and item tags to turtle.inspect and turtle.getItemDetail.
|
||||
* Several optimisations to monitor rendering (Lignum).
|
||||
* Expose block and item tags to turtle.inspect and `turtle.getItemDetail`.
|
||||
|
||||
And several bug fixes:
|
||||
* Fix settings.load failing on defined settings.
|
||||
@@ -949,7 +956,7 @@ And several bug fixes:
|
||||
* Add a TBO backend for monitors, with a significant performance boost.
|
||||
* The Lua REPL warns when declaring locals (lupus590, exerro)
|
||||
* Add config to allow using command computers in survival.
|
||||
* Add fs.isDriveRoot - checks if a path is the root of a drive.
|
||||
* Add `fs.isDriveRoot` - checks if a path is the root of a drive.
|
||||
* `cc.pretty` can now display a function's arguments and where it was defined. The Lua REPL will show arguments by default.
|
||||
* Move the shell's `require`/`package` implementation to a separate `cc.require` module.
|
||||
* Move treasure programs into a separate external data pack.
|
||||
@@ -989,13 +996,13 @@ And several bug fixes:
|
||||
And several bug fixes:
|
||||
* Fix turtle texture being incorrectly oriented (magiczocker10).
|
||||
* Prevent copying folders into themselves.
|
||||
* Normalise file paths within shell.setDir (JakobDev)
|
||||
* Normalise file paths within `shell.setDir` (JakobDev)
|
||||
* Fix turtles treating waterlogged blocks as water.
|
||||
* Register an entity renderer for the turtle's fake player.
|
||||
|
||||
# New features in CC: Tweaked 1.86.2
|
||||
|
||||
* Fix peripheral.getMethods returning an empty table.
|
||||
* Fix `peripheral.getMethods` returning an empty table.
|
||||
* Update to Minecraft 1.15.2. This is currently alpha-quality and so is missing features and may be unstable.
|
||||
|
||||
# New features in CC: Tweaked 1.86.1
|
||||
@@ -1035,14 +1042,14 @@ And several bug fixes:
|
||||
|
||||
# New features in CC: Tweaked 1.85.0
|
||||
|
||||
* Window.reposition now allows changing the redirect buffer
|
||||
* Add cc.completion and cc.shell.completion modules
|
||||
* command.exec also returns the number of affected objects, when exposed by the game.
|
||||
* `window.reposition` now allows changing the redirect buffer.
|
||||
* Add `cc.completion` and `cc.shell.completion` modules.
|
||||
* `command.exec` also returns the number of affected objects, when exposed by the game.
|
||||
|
||||
And several bug fixes:
|
||||
* Change how turtle mining drops are handled, improving compatibility with some mods.
|
||||
* Fix several GUI desyncs after a turtle moves.
|
||||
* Fix os.day/os.time using the incorrect world time.
|
||||
* Fix `os.day`/`os.time` using the incorrect world time.
|
||||
* Prevent wired modems dropping incorrectly.
|
||||
* Fix mouse events not firing within the computer GUI.
|
||||
|
||||
@@ -1302,7 +1309,7 @@ And several bug fixes:
|
||||
# New features in CC: Tweaked 1.80pr1.4
|
||||
|
||||
* Verify the action can be completed in `copy`, `rename` and `mkdir` commands.
|
||||
* Add `/rom/modules` so the package path.
|
||||
* Add `/rom/modules` to the package path.
|
||||
* Add `read` to normal file handles - allowing reading a given number of characters.
|
||||
* Various minor bug fixes.
|
||||
* Ensure ComputerCraft peripherals are thread-safe. This fixes multiple Lua errors and crashes with modems monitors.
|
||||
@@ -1325,7 +1332,7 @@ And several bug fixes:
|
||||
* Fix `term.getTextScale()` not working across multiple monitors.
|
||||
* Fix computer state not being synced to client when turning on/off.
|
||||
* Provide an API for registering custom APIs.
|
||||
* Render turtles called "Dinnerbone" or "Grumm" upside*down.
|
||||
* Render turtles called "Dinnerbone" or "Grumm" upsidedown.
|
||||
* Fix `getCollisionBoundingBox` not using all AABBs.
|
||||
* **Experimental:** Add map-like rendering for pocket computers.
|
||||
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
New features in CC: Tweaked 1.119.0
|
||||
New features in CC: Tweaked 1.120.0
|
||||
|
||||
* Add `commands.getDimension()`.
|
||||
* Add `cc.base64` module.
|
||||
* Update Cobalt to 0.9.9, bringing in several Lua 5.5 changes:
|
||||
* Floats are now printed with enough digits to round trip correctly.
|
||||
* Add `table.create`.
|
||||
* `utf8.offset` now returns the final position of the codepoint.
|
||||
* Support spawning new parallel functions in `parallel.watiForAll`.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix handling of integer indexes in `LuaTable`.
|
||||
* Correct `min` and `sec` defaults in `os.time`. (sircfenner)
|
||||
One bug fix:
|
||||
* Make HTTP IP filtering stricter.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
||||
+15
@@ -144,6 +144,20 @@ local function can_wrap_errors(thread)
|
||||
return false
|
||||
end
|
||||
|
||||
--[[- Wrap an error into an exception, when it is safe to do so.
|
||||
|
||||
@param err The error to wrap.
|
||||
@tparam coroutine thread The coroutine the error occurred on.
|
||||
@return The constructed exception, or the original error.
|
||||
]]
|
||||
local function wrap_error(err, thread)
|
||||
if type(err) == "string" and can_wrap_errors() then
|
||||
return make_exception(err, thread)
|
||||
else
|
||||
return err
|
||||
end
|
||||
end
|
||||
|
||||
--[[- Attempt to call the provided function `func` with the provided arguments.
|
||||
|
||||
@tparam function func The function to call.
|
||||
@@ -244,6 +258,7 @@ end
|
||||
|
||||
return {
|
||||
make_exception = make_exception,
|
||||
wrap_error = wrap_error,
|
||||
|
||||
try_barrier = try_barrier,
|
||||
can_wrap_errors = can_wrap_errors,
|
||||
|
||||
+13
-2
@@ -31,13 +31,20 @@ public class AddressRuleTest {
|
||||
@ValueSource(strings = {
|
||||
"0.0.0.0", "[::]",
|
||||
"localhost", "127.0.0.1.nip.io", "127.0.0.1", "[::1]",
|
||||
"172.17.0.1", "192.168.1.114", "[0:0:0:0:0:ffff:c0a8:172]", "10.0.0.1",
|
||||
"172.17.0.1",
|
||||
"192.168.1.114",
|
||||
"10.0.0.1",
|
||||
// IPv4-mapped address. This is converted to IPv4 by getByName.
|
||||
"0:0:0:0:0:ffff:c0a8:172",
|
||||
// 6to4 address
|
||||
"2002:7f00:0001::", // 127.0.0.1
|
||||
// Multicast
|
||||
"224.0.0.1", "ff02::1",
|
||||
// CGNAT
|
||||
"100.64.0.0", "100.127.255.255",
|
||||
// NAT64
|
||||
"64:ff9b::c0a8:0101",
|
||||
"64:ff9b:1::c0a8:0101",
|
||||
// Cloud metadata providers
|
||||
"100.100.100.200", // Alibaba
|
||||
"192.0.0.192", // Oracle
|
||||
@@ -51,7 +58,11 @@ public class AddressRuleTest {
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
// Ensure either side of the CGNAT range is allowed.
|
||||
"100.63.255.255", "100.128.0.0"
|
||||
"100.63.255.255", "100.128.0.0",
|
||||
// IPv4-mapped address. This is converted to IPv4 by getByName.
|
||||
"0:0:0:0:0:ffff:104.20.23.154",
|
||||
// 6to4 address
|
||||
"2002:6814:179a::", // 104.20.23.154
|
||||
})
|
||||
public void allowsNonLocalDomains(String domain) {
|
||||
assertEquals(apply(CoreConfig.httpRules, domain, 80).action(), Action.ALLOW);
|
||||
|
||||
@@ -33,10 +33,12 @@ describe("The parallel library", function()
|
||||
it("accepts an arbitrary number of functions", function()
|
||||
local count = 0
|
||||
local fns = {}
|
||||
for i = 1, 50 do fns[i] = function()
|
||||
count = count + 1
|
||||
coroutine.yield()
|
||||
end end
|
||||
for i = 1, 50 do
|
||||
fns[i] = function()
|
||||
count = count + 1
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
os.queueEvent("dummy")
|
||||
parallel.waitForAny(table.unpack(fns))
|
||||
expect(count):eq(50)
|
||||
@@ -79,25 +81,63 @@ describe("The parallel library", function()
|
||||
local function a()
|
||||
entries[#entries + 1] = "first"
|
||||
local s = coroutine.yield()
|
||||
entries[#entries + 1] = s
|
||||
entries[#entries + 1] = "a: " .. s
|
||||
end
|
||||
local function b()
|
||||
entries[#entries + 1] = "second"
|
||||
local s = coroutine.yield()
|
||||
entries[#entries + 1] = s
|
||||
entries[#entries + 1] = "b: " .. s
|
||||
end
|
||||
os.queueEvent("yield")
|
||||
parallel.waitForAll(a, b)
|
||||
expect(entries):same({ "first", "second", "yield", "yield" })
|
||||
expect(entries):same({ "first", "second", "a: yield", "b: yield" })
|
||||
end)
|
||||
|
||||
it("can spawn new functions", function()
|
||||
local entries = {}
|
||||
local function a(name)
|
||||
entries[#entries + 1] = name
|
||||
local s = coroutine.yield()
|
||||
entries[#entries + 1] = name .. ": " .. s
|
||||
end
|
||||
local function b(spawn)
|
||||
entries[#entries + 1] = "b"
|
||||
spawn(a, "a1")
|
||||
spawn(a, "a2")
|
||||
local s = coroutine.yield()
|
||||
entries[#entries + 1] = "b: " .. s
|
||||
spawn(a, "a3")
|
||||
end
|
||||
os.queueEvent("yield")
|
||||
os.queueEvent("yield")
|
||||
parallel.waitForAll(b)
|
||||
expect(entries):same({
|
||||
"b",
|
||||
"a1",
|
||||
"a2",
|
||||
"b: yield",
|
||||
"a1: yield",
|
||||
"a2: yield",
|
||||
"a3",
|
||||
"a3: yield",
|
||||
})
|
||||
end)
|
||||
|
||||
it("cannot spawn outside of parallel", function()
|
||||
local spawn
|
||||
parallel.waitForAll(function(s) spawn = s end)
|
||||
expect.error(spawn, function() end):eq("Cannot spawn new functions outside of waitForAll")
|
||||
end)
|
||||
|
||||
it("accepts an arbitrary number of functions", function()
|
||||
local count = 0
|
||||
local fns = {}
|
||||
for i = 1, 50 do fns[i] = function()
|
||||
count = count + 1
|
||||
coroutine.yield()
|
||||
end end
|
||||
for i = 1, 50 do
|
||||
fns[i] = function()
|
||||
count = count + 1
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
os.queueEvent("dummy")
|
||||
parallel.waitForAll(table.unpack(fns))
|
||||
expect(count):eq(50)
|
||||
|
||||
@@ -124,82 +124,82 @@ loom {
|
||||
|
||||
runs {
|
||||
configureEach {
|
||||
ideConfigGenerated(true)
|
||||
generateRunConfig = true
|
||||
ideConfigFolder = "Fabric"
|
||||
|
||||
property("fabric-tag-conventions-v2.missingTagTranslationWarning", "VERBOSE")
|
||||
}
|
||||
|
||||
named("client") {
|
||||
configName = "Client"
|
||||
displayName = "Client"
|
||||
}
|
||||
|
||||
named("server") {
|
||||
configName = "Server"
|
||||
runDir("run/server")
|
||||
displayName = "Server"
|
||||
runDirectory = layout.projectDirectory.dir("run/server")
|
||||
}
|
||||
|
||||
fun RunConfigSettings.configureForData(sourceSet: SourceSet) {
|
||||
client()
|
||||
runDir("run/run${name.capitalise()}")
|
||||
property("fabric-api.datagen")
|
||||
property(
|
||||
runDirectory = layout.buildDirectory.dir("run${name.capitalise()}")
|
||||
systemProperties.put("fabric-api.datagen", "true")
|
||||
systemProperties.put(
|
||||
"fabric-api.datagen.output-dir",
|
||||
layout.buildDirectory.dir(sourceSet.getTaskName("generateResources", null)).getAbsolutePath(),
|
||||
)
|
||||
property("fabric-api.datagen.strict-validation")
|
||||
systemProperties.put("fabric-api.datagen.strict-validation", "true")
|
||||
}
|
||||
|
||||
register("data") {
|
||||
configName = "Datagen"
|
||||
displayName = "Datagen"
|
||||
configureForData(sourceSets.main.get())
|
||||
source(sourceSets.datagen.get())
|
||||
sourceSet = sourceSets.datagen.name
|
||||
}
|
||||
|
||||
fun RunConfigSettings.configureForGameTest() {
|
||||
source(sourceSets.testMod.get())
|
||||
sourceSet = sourceSets.testMod.name
|
||||
|
||||
val testSources = project(":common").file("src/testMod/resources").absolutePath
|
||||
property("cctest.sources", testSources)
|
||||
systemProperties.put("cctest.sources", testSources)
|
||||
|
||||
// Load cctest last, so it can override resources. This bypasses Fabric's shuffling of mods
|
||||
property("fabric.debug.loadLate", "cctest")
|
||||
systemProperties.put("fabric.debug.loadLate", "cctest")
|
||||
|
||||
vmArg("-ea")
|
||||
jvmArguments.add("-ea")
|
||||
}
|
||||
|
||||
val testClient by registering {
|
||||
configName = "Test Client"
|
||||
displayName = "Test Client"
|
||||
client()
|
||||
configureForGameTest()
|
||||
|
||||
runDir("run/testClient")
|
||||
property("cctest.tags", "client,common")
|
||||
runDirectory = layout.projectDirectory.dir("run/testClient")
|
||||
systemProperties.put("cctest.tags", "client,common")
|
||||
}
|
||||
|
||||
register("gametest") {
|
||||
configName = "Game Test"
|
||||
displayName = "Game Test"
|
||||
server()
|
||||
configureForGameTest()
|
||||
|
||||
property("fabric-api.gametest")
|
||||
property(
|
||||
systemProperties.put("fabric-api.gametest", "true")
|
||||
systemProperties.put(
|
||||
"fabric-api.gametest.report-file",
|
||||
layout.buildDirectory.dir("test-results/runGametest.xml").getAbsolutePath(),
|
||||
)
|
||||
runDir("run/gametest")
|
||||
runDirectory = layout.projectDirectory.dir("run/gametest")
|
||||
}
|
||||
|
||||
register("exampleClient") {
|
||||
client()
|
||||
configName = "Example Mod Client"
|
||||
source(sourceSets.examples.get())
|
||||
displayName = "Example Mod Client"
|
||||
sourceSet = sourceSets.examples.name
|
||||
}
|
||||
|
||||
register("exampleData") {
|
||||
configName = "Example Mod Datagen"
|
||||
displayName = "Example Mod Datagen"
|
||||
configureForData(sourceSets.examples.get())
|
||||
source(sourceSets.examples.get())
|
||||
sourceSet = sourceSets.examples.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,10 +45,12 @@ neoForge {
|
||||
|
||||
runs {
|
||||
configureEach {
|
||||
ideName = "Forge - ${name.capitalise()}"
|
||||
ideName = "${name.capitalise()} (Forge)"
|
||||
systemProperty("forge.logging.markers", "REGISTRIES")
|
||||
systemProperty("forge.logging.console.level", "debug")
|
||||
loadedMods.add(computercraft)
|
||||
|
||||
ideFolderName = "Forge"
|
||||
}
|
||||
|
||||
register("client") {
|
||||
|
||||
@@ -76,13 +76,14 @@ class Window extends Component<WindowProps, WindowState> {
|
||||
const elements = document.querySelectorAll("pre[data-lua-kind]");
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const element = elements[i] as HTMLElement;
|
||||
if (element.hasAttribute("data-no-run")) continue
|
||||
|
||||
let example = element.innerText;
|
||||
|
||||
const snippet = element.getAttribute("data-snippet");
|
||||
if (snippet) this.snippets[snippet] = example;
|
||||
|
||||
if (element.hasAttribute("data-no-run")) continue
|
||||
|
||||
// We attempt to pretty-print the result of a function _except_ when the function
|
||||
// is print. This is pretty ugly, but prevents the confusing trailing "1".
|
||||
if (element.getAttribute("data-lua-kind") == "expr" && !example.startsWith("print(")) {
|
||||
|
||||
@@ -14,7 +14,11 @@ import dan200.computercraft.api.peripheral.PeripheralType;
|
||||
import dan200.computercraft.core.methods.LuaMethod;
|
||||
import dan200.computercraft.core.methods.NamedMethod;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.teavm.metaprogramming.*;
|
||||
import org.teavm.extension.introspect.IntrospectClass;
|
||||
import org.teavm.metaprogramming.CompileTime;
|
||||
import org.teavm.metaprogramming.Meta;
|
||||
import org.teavm.metaprogramming.Metaprogramming;
|
||||
import org.teavm.metaprogramming.Value;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
@@ -35,7 +39,7 @@ public class MethodReflection {
|
||||
@Meta
|
||||
public static native boolean getMethods(Class<?> type, Consumer<NamedMethod<LuaMethod>> make);
|
||||
|
||||
private static void getMethods(ReflectClass<?> klass, Value<Consumer<NamedMethod<LuaMethod>>> make) {
|
||||
private static void getMethods(IntrospectClass<?> klass, Value<Consumer<NamedMethod<LuaMethod>>> make) {
|
||||
var result = getMethodsImpl(klass, make);
|
||||
// Using "unsupportedCase" here causes us to skip generating any code and just return null. While null isn't
|
||||
// a boolean, it's still false-y and thus has the same effect in the generated JS!
|
||||
@@ -43,15 +47,15 @@ public class MethodReflection {
|
||||
Metaprogramming.exit(() -> result);
|
||||
}
|
||||
|
||||
private static boolean getMethodsImpl(ReflectClass<?> klass, Value<Consumer<NamedMethod<LuaMethod>>> make) {
|
||||
if (!klass.getName().startsWith("dan200.computercraft.") && !klass.getName().startsWith("cc.tweaked.web.peripheral")) {
|
||||
private static boolean getMethodsImpl(IntrospectClass<?> klass, Value<Consumer<NamedMethod<LuaMethod>>> make) {
|
||||
if (!klass.name().startsWith("dan200.computercraft.") && !klass.name().startsWith("cc.tweaked.web.peripheral")) {
|
||||
return false;
|
||||
}
|
||||
if (klass.getName().contains("lambda")) return false;
|
||||
if (klass.name().contains("lambda")) return false;
|
||||
|
||||
Class<?> actualClass;
|
||||
try {
|
||||
actualClass = Metaprogramming.getClassLoader().loadClass(klass.getName());
|
||||
actualClass = Metaprogramming.environment().classLoader().loadClass(klass.name());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -60,7 +64,7 @@ public class MethodReflection {
|
||||
for (var method : methods) {
|
||||
var name = method.name();
|
||||
var nonYielding = method.nonYielding();
|
||||
var actualField = method.method().getField("INSTANCE");
|
||||
var actualField = Metaprogramming.accessor(method.method().field("INSTANCE"));
|
||||
|
||||
Metaprogramming.emit(() -> make.get().accept(new NamedMethod<>(name, (LuaMethod) actualField.get(null), nonYielding, null)));
|
||||
}
|
||||
@@ -69,7 +73,7 @@ public class MethodReflection {
|
||||
}
|
||||
|
||||
private static final class Internal {
|
||||
private static final LoadingCache<Class<?>, List<NamedMethod<ReflectClass<LuaMethod>>>> CLASS_CACHE = CacheBuilder
|
||||
private static final LoadingCache<Class<?>, List<NamedMethod<IntrospectClass<LuaMethod>>>> CLASS_CACHE = CacheBuilder
|
||||
.newBuilder()
|
||||
.build(CacheLoader.from(Internal::getMethodsImpl));
|
||||
|
||||
@@ -77,7 +81,7 @@ public class MethodReflection {
|
||||
LuaMethod.class, List.of(ILuaContext.class), Internal::createClass
|
||||
);
|
||||
|
||||
static List<NamedMethod<ReflectClass<LuaMethod>>> getMethods(Class<?> klass) {
|
||||
static List<NamedMethod<IntrospectClass<LuaMethod>>> getMethods(Class<?> klass) {
|
||||
try {
|
||||
return CLASS_CACHE.get(klass);
|
||||
} catch (ExecutionException e) {
|
||||
@@ -85,7 +89,7 @@ public class MethodReflection {
|
||||
}
|
||||
}
|
||||
|
||||
private static ReflectClass<?> createClass(byte[] bytes) {
|
||||
private static IntrospectClass<?> createClass(byte[] bytes) {
|
||||
/*
|
||||
StaticGenerator is not declared to be @CompileTime, to ensure it loads in the same module/classloader as
|
||||
other files in this package. This means it can't call Metaprogramming.createClass directly, as that's
|
||||
@@ -94,11 +98,11 @@ public class MethodReflection {
|
||||
We need to use an explicit call (rather than a MethodReference), as TeaVM doesn't correctly rewrite the
|
||||
latter.
|
||||
*/
|
||||
return Metaprogramming.createClass(bytes);
|
||||
return Metaprogramming.environment().createClass(bytes);
|
||||
}
|
||||
|
||||
private static List<NamedMethod<ReflectClass<LuaMethod>>> getMethodsImpl(Class<?> klass) {
|
||||
ArrayList<NamedMethod<ReflectClass<LuaMethod>>> methods = null;
|
||||
private static List<NamedMethod<IntrospectClass<LuaMethod>>> getMethodsImpl(Class<?> klass) {
|
||||
ArrayList<NamedMethod<IntrospectClass<LuaMethod>>> methods = null;
|
||||
|
||||
// Find all methods on the current class
|
||||
for (var method : klass.getMethods()) {
|
||||
@@ -122,7 +126,7 @@ public class MethodReflection {
|
||||
return Collections.unmodifiableList(methods);
|
||||
}
|
||||
|
||||
private static void addMethod(List<NamedMethod<ReflectClass<LuaMethod>>> methods, Method method, LuaFunction annotation, @Nullable PeripheralType genericType, ReflectClass<LuaMethod> instance) {
|
||||
private static void addMethod(List<NamedMethod<IntrospectClass<LuaMethod>>> methods, Method method, LuaFunction annotation, @Nullable PeripheralType genericType, IntrospectClass<LuaMethod> instance) {
|
||||
var names = annotation.value();
|
||||
var isSimple = method.getReturnType() != MethodResult.class && !annotation.mainThread();
|
||||
if (names.length == 0) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.jspecify.annotations.Nullable;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.teavm.metaprogramming.ReflectClass;
|
||||
import org.teavm.extension.introspect.IntrospectClass;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
@@ -58,13 +58,13 @@ public final class StaticGenerator<T> {
|
||||
private final String methodDesc;
|
||||
private final String classPrefix;
|
||||
|
||||
private final Function<byte[], ReflectClass<?>> createClass;
|
||||
private final Function<byte[], IntrospectClass<?>> createClass;
|
||||
|
||||
private final LoadingCache<Method, Optional<ReflectClass<T>>> methodCache = CacheBuilder
|
||||
private final LoadingCache<Method, Optional<IntrospectClass<T>>> methodCache = CacheBuilder
|
||||
.newBuilder()
|
||||
.build(CacheLoader.from(catching(this::build, Optional.empty())));
|
||||
|
||||
public StaticGenerator(Class<T> base, List<Class<?>> context, Function<byte[], ReflectClass<?>> createClass) {
|
||||
public StaticGenerator(Class<T> base, List<Class<?>> context, Function<byte[], IntrospectClass<?>> createClass) {
|
||||
this.base = base;
|
||||
this.context = context;
|
||||
this.createClass = createClass;
|
||||
@@ -79,11 +79,11 @@ public final class StaticGenerator<T> {
|
||||
classPrefix = StaticGenerator.class.getPackageName() + "." + base.getSimpleName() + "$";
|
||||
}
|
||||
|
||||
public Optional<ReflectClass<T>> getMethod(Method method) {
|
||||
public Optional<IntrospectClass<T>> getMethod(Method method) {
|
||||
return methodCache.getUnchecked(method);
|
||||
}
|
||||
|
||||
private Optional<ReflectClass<T>> build(Method method) {
|
||||
private Optional<IntrospectClass<T>> build(Method method) {
|
||||
var name = method.getDeclaringClass().getName() + "." + method.getName();
|
||||
var modifiers = method.getModifiers();
|
||||
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version("0.8.0")
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version("1.0.0")
|
||||
}
|
||||
|
||||
val mcVersion: String by settings
|
||||
|
||||
Reference in New Issue
Block a user