We now suggest alternative table keys when code errors with "attempt
to index/call 'foo' (a nil value)". For example: "redstone.getinput()",
will now suggest "Did you mean: getInput".
This is a bit tricky to get right! In the above example, our code reads
like:
1 GETTABUP 0 0 0 ; r0 := _ENV["redstone"]
2 GETFIELD 0 0 1 ; r0 := r0["getinput"]
3 CALL 0 1 1 ; r0()
Note, that when we get to the problematic line, we don't have access to
the original table that we attempted to index. In order to do this, we
borrow ideas from Lua's getobjname — we effectively write an evaluator
that walks back over the code and tries to reconstruct the expression
that resulted in nil.
For example, in the above case:
- We know an instruction happened at pc=3, so we try to find the
expression that computed r0.
- We know this was set at pc=2, so we step back one. This is a GETFIELD
instruction, so we check the key (it's a constant, so worth
reporting), and then try to evaluate the table.
- This version of r0 was set at pc=1, so we step back again. It's a
GETTABUP instruction, so we can just evaluate that directly.
We then use this information (indexing _ENV.redstone with "getinput") to
find alternative keys (e.g. getInput, getOutput, etc...) and then pick
some likely suggestions with Damerau-Levenshtein/OSD.
I'm not entirely thrilled by the implementation here. The core
interpretation logic is implemented in Java. Which is *fine*, but a)
feels a little cheaty and b) means we're limited to what Lua bytecode
can provide (for instance, we can't inspect outer functions, or list all
available names in scope). We obviously can expand the bytecode if
needed, but something we'd want to be careful with.
The alternative approach would be to handle all the parsing in
Lua. Unfortunately, this is quite hard to get right — I think we'd need
some lazy parsing strategy to avoid constructing the whole AST, while
still retaining all the scope information we need.
I don't know. We really could make this as complex as we like, and I
don't know what the right balance is. It'd be cool to detect patterns
like the following, but is it *useful*?
local monitor = peripheral.wrap("left")
monitor.write("Hello")
-- ^ monitor is nil. Is there a peripheral to the left of the
-- computer?
For now, the current approach feels the easiest, and should allow us to
prototype things and see what does/doesn't work.
In the original implementation of our prettier runtime errors (#1320), we
wrapped the errors thrown within parallel functions into an exception
object. This means the call-stack is available to the catching-code, and
so is able to report a pretty exception message.
Unfortunately, this was a breaking change, and so we had to roll that
back. Some people were pcalling the parallel function, and matching on
the result of the error.
This is a second attempt at this, using a technique I've affectionately
dubbed "magic throws". The parallel API is now aware of whether it is
being pcalled or not, and thus able to decide whether to wrap the error
into an exception or not:
- Add a new `cc.internal.tiny_require` module. This is a tiny
reimplementation of require, for use in our global APIs.
- Add a new (global, in the debug registry) `cc_try_barrier` function.
This acts as a marker function, and is used to store additional
information about the current coroutine.
Currently this stores the parent coroutine (used to walk the full call
stack) and a cache of whether any `pcall`-like function is on the
stack.
Both `parallel` and `cc.internal.exception.try` add this function to
the root of the call stack.
- When an error occurs within `parallel`, we walk up the call stack,
using `cc_try_barrier` to traverse up the parent coroutine's stack
too. If we do not find any `pcall`-like functions, then we know the
error is never intercepted by user code, and so its safe to throw a
full exception.
This allows shift+clicking a pocket computer on to a lectern. These
computers can be right clicked, opening the no-term computer GUI.
Terminal contents is rendered in-world, and broadcast to everyone in
range.
- Add a new lectern PocketHolder.
- Refactor some of the `PocketItemComputer` code to allow ticking pocket
computers from a non-player/entity source.
- Add a new model for pocket computers. This requires several new
textures (somewhat mirroring the item ones), which is a little
unfortunate, but looks much better than reusing the map renderer or
item form.
In 94ad6dab0e5b8d9eb65467dd1b635d708ce58b53, we changed it so typing
characters outside of CC's codepage were replaced with '?' rather than
ignored. This can be quite annoying for non-European users (where latin1
isn't very helpful!), so it makes sense to revert this change.
See discussion in #860 for more context.
- Store the filter alongside the coroutine rather than in a separate
table (like we do in multishell).
- Remove the redudant (I think!) second loop that checks for dead
coroutines. We already check for dead coroutines in the main loop.
- Rename some variables to be a bit more consistent. This makes this
commit look noisier than it is. Sorry!
Oh, this is so broken, and really has been since the 1.13 update, if not
earlier.
- Fix call to isUnobstructed using the bounding box of the
*destination* block rather than the turtle. This is almost always
air, so the box is empty.
- Because the above check has been wrong for so many years, we now
significantly relax the "can push" checks for entities. We now allow
pushing entities in any direction.
We also remove the "isUnobstructed" check for the destination entity
pos. This causes problems (if two entities are standing on a turtle,
they'll obstruct each other), and given pistons don't perform such a
check, I don't think we need it.
- Also do a bit of cleanup around air/liquid checks. We often ended up
reading the block state multiple times, which is a little ugly.
I'd originally kept this around to prevent mods that use CC internals,
but fa2140d00ba667902d361b4873804dd97dde3a19 has probably broken those
anyway, so let's not worry too much.
- Remove ContainerData.open.
- Change PlatformHelper.openMenu to take a separate display name and
MenuConstructor, rather than a MenuProvider. This makes the interface
slightly easier to use in the common case, where we want to use
lambdas instead.
- Check whether the computer is a command computer before registering
the capability.
- Add tests to check what is/isn't a peripheral. See also #2020, where
we forgot to register a peripheral on NeoForge 1.21.1.
Fixes#2070.
This isn't required in vanilla, as the command computer is a
GameMasterBlock, and so isn't placeable in the first place.
*However*, this is a problem with Create contraptions — with those it's
possible to "place" a command computer complete with NBT. We override
onlyOpCanSetNbt to prevent this [^1].
[^1]: 7a7993deb8/src/main/java/com/simibubi/create/foundation/utility/NBTProcessors.java (L179)
This is where vanilla will read the sprites from in future versions, so
means we have a consistent layout between versions.
Also move the turtle "selected slot" texture to a sprite sheet. It would
be good to do more of these in the future (e.g. printer progress, maybe
bits of printouts).
Sorry to resource pack artists for causing trouble again.
We currently need to pass a whole bunch of arguments to a ServerComputer
in order to construct it, and if we implement #1814, this will get a
whole lot worse. Instead, we now pass most parameters (computer id,
family, label, term size, components) via a separate Properties class,
much like Minecraft does for blocks and items.
I'm not wild about the design of the API here, but I think it's a step
in the right direction.
We now convert uncode characters from "char" and "paste" events to CC's
charset[^1], rather than just leaving them unconverted. This means you
can paste in special characters like "♠" or "🮙" and they will be
converted correctly. Characters outside that range will be replaced with
"?", as before.
It would be nice to make this a bi-directional mapping, and do this for
Lua methods too (e.g. os.setComputerLabel). However, that has much wider
ramifications (and more likelyhood of breaking something), so avoiding
that for now.
- Remove the generic "queue event" client->server message, and replace
it with separate char/terminate/paste messages. This allows us to
delete a chunk of code (all the NBT<->Object conversion), and makes
server-side validation of events possible.
- Fix os.setComputerLabel accepting the section sign — this is treated
as special by Minecraft's formatting code. Sorry, no fun allowed.
- Convert paste/char codepoints to CC's charset. Sadly MC's char hook
splits the codepoint into surrogate pairs, which we *don't* attempt
to reconstruct, so you can't currently use unicode input for block
characters — you can paste them though!
[^1]: I'm referring this to the "terminal charset" within the code. I've
flip-flopped between "CraftOS", "terminal", "ComputerCraft", but feel
especially great.
This abstraction never made much sense on InputHandler, as we only leave
the default methods on ServerComputer.
We now add a new class (ComputerEvents), which has a series of *static*
methods, that can queue an event on a ComputerEvents.Receiver object.
This is a bit of an odd indirection (why not just make them instance
methods on Receiver?!), but I don't really want those methods leaking
everywhere.
When "placing" the item (e.g. hoeing soil), we were using the tool item,
rather than the passed stack. This was introduced in
9a914e75c4a4e122c16aac5d010059d28b475b5e, so never made it into a
release.
- Disable Gradle module metadata for all Minecraft projects
- Run dependency exclusion code for all projects
We need the former for MDG on 1.21, so might as well do some other
cleanup while we're here.
useOn is now only responsible for firing the actual mod loader events,
and just returns the result of firing that event. The actual calling of
Block.use/Item.useOn now live in TurtlePlaceCommand.
This isn't especially useful for 1.20.1, but is more relevant on 1.21.1
when we look at #2011, as the shared code is much larger.
Okay, listen. I started writing a few more gametests (see #1682), and
then thought I'd do a cheeky Gradle update. However, that broke
vanilla-extract[^1], and also triggered a load of deprecation warnings,
and at that point it was too late to separate the too.
[^1]: 8975ed5a7b
There's too many issues here around object pooling and reference
counting, that it's just not practical to correctly handle. We now just
use a plain old byte[] rather than a ByteBuf.
This does mean the array now lives on the Java heap rather than the
direct heap, but I *think* that's fine. It's something that is hard to
measure.
Fixes#2059
I've no motivation for modding right now, but always got time for build
system busywork!
CC:T (and CC before that) has always published its API docs. However,
they're not always the most helpful — they're useful if you know what
you're looking for, but aren't a good getting-started guide.
Part of the issue here is there's no examples, and everything is
described pretty abstractly. I have occasionally tried to improve this
(e.g. the peripheral docs in bdffabc08e2eb9895f966c949acc8334a2bf4475),
but it's a long road.
This commit adds a new example mod, which registers peripherals, an API
and a turtle upgrade. While the mod itself isn't exported as part of the
docs, we reference blocks of it using Java's new {@snippet} tag.
- Switch the Forge project to use NeoForge's new Legacy MDG plugin. We
don't *need* to do this, but it means the build logic for Forge and
NeoForge is more closely aligned.
- Add a new SnippetTaglet, which is a partial backport of Java 18+'s
{@snippet}.
- Add an example mod. This is a working multi-loader mod, complete with
datagen (albeit with no good multi-loader abstractions).
- Move our existing <pre>{@code ...}</pre> blocks into the example mod,
replacing them with {@snippet}s.
- Add a new overview page to the docs, providing some getting-started
information. We had this already in the dan200.computercraft.api
package docs, but it's not especially visible there.
fs.getDrive returns nil for missing files, rather than the mount of the
parent file. This is a bit confusing — other mount-related functions
(e.g. getFreeSpace) find the nearest mount — but I think it's too late
to change this. Instead, we check if the file exists first.
MC 1.21.4 means we have to move more data generation code into the
client source set. Given all this code movement, it probably makes sense
to put data generation in a separate source set instead.
1.21.4 also has split data generators for client and server, but neither
mod loader recommends this. This means we can/should merge DataProviders
and ClientDataProviders into a single class.
Data generators are no longer bundled with the jar, which does reduce
file size, but by a tiny amount (~70KiB).
I've tried so many rewrites of the config system over the last few
months, in an attempt to get started on #1727. All of them stink, so
this is an attempt to apply some of the cleanup.
- Move some of the common logic into ConfigFile. This means we now
store more information ourselves for Forge, rather than reading it
out of the ForgeConfigSpec.
- Don't include the Range/Allowed keys in the translation key. This was
mostly there because of how we read comments from Forge, but it never
made much sense.
- Remove our separate Trie structure, and just encode the tree as part
of the children of a Group.
If the cursor is not visible then we'd end up blinking the last
character on the screen. And if the screen was empty we'd spew the logs
with GL errors.
- Use the correct index count for the cursor quad. Monitors are now
rendered as quads, rather than triangles.
- *Skip* rendering the cursor vertex, rather than additionally
rendering it.
I confess, I'm baffled how this code was ever written. From what I can
tell, this has been broken since it was first introduced in
4228011b848e99de64eb79c26598d81490c32bad, and I'm sure I tested it then.
Fixes#2013. Probably.