Minecraft 1.21.2 added a mouseScroll override to
AbstractContainerScreen, which means that child widgets no longer
receive scroll events. We reimplement that logic in our computer screen.
Fixes#2245.
We use getLevel() specifically for reading the current registries
(and/or server). We don't need the exact position of the speaker to
query this, so add a dedicated method for it.
We actually had a similar method on 1.21.7 already for upgrades. This
just moves it to SpeakerPeripheral.
Fixes#2236.
- Remove unused MonitorRenderer. I'm sure this had been deleted
already, but apparently not!
- Add missing items to the changelog.
- Fix crash when clearing tests in a world.
- Bump Iris deps, to help with debugging #2229.
I don't love the implementation of this (see discussion in #2220), but
it's better than nothing. Wow, the editor really needs a bit of a
rewrite, the code is kinda messy.
Fixes#1396.
Still one TODO left, but around data fixers, so fairly small.
- GUI rendering got a big overhaul. I avoided the worst of it with
9272e2efcd, but things like terminals
and printouts still require some custom rendering.
- Item models are now de-duplicated when rendering in the UI, so we
need to keep track of their identity with
(appendModelidentityElement). I'm not sure I've got this entirely
right — whole thing feels unfortunately error-prone.
- BE serialisation now goes through a Value{Input,Output} class, rather
than using NBT directly. Fairly simple change, but has changed the
format of the GameProfile used in turtle's owners. Need a DFU patch
for this.
I've been putting this off for a while, as I had issues in the past with
people using old Node versions (e.g. #1806), but it no long works on my
machine, so time to make the switch.
Also do a bit of a package update. Hit a rollup bug while doing this
(https://github.com/rollup/plugins/issues/1877), so holding that update
back for now.
In the 1.21.4 update (9277aa33e9), we
removed our DirectVertexBuffer class, and switched to vanilla's
VertexBuffer. This forced us to use MeshData and thus
ByteBufferBuilder instead of allocating the ByteBuffer ourselves.
One thing I'd missed with this is that Iris's text vertex sink API
requires us to allocate the whole buffer up-front and so the resulting
buffer has a limit of the *maximum* number of vertices rendered, not the
actual one.
This wasn't an issue on 1.21.4, as we didn't check this (I guess we just
silently rendered junk??), but for the 1.21.5 update
(a1df196673) we added some extra
assertions here, which now fail on Iris.
Typically, the whole original change was is now entirely redundant, as
Vanilla has removed VertexBuffer entirely, and so we can/should work in
terms of raw ByteBuffers again.
Fixes#2219.
- Move remaining sprites to the vanilla GUI atlas.
- Convert our computer border/sidebar sprites to use vanilla's
nine-sliced mcmeta files. I thought I'd have to do something custom
here for the sidebar, as that has no right border, but vanilla
supports that natively!
- Use the normal GuiGraphics.blitSprite for rendering computer
border/sidebar.
- Obey nine-slice scaling within the pocket computer renderer.
This is slightly more accurate for long strings and comments. Note that
we still work a line at a time (and in a non-incremental manner), so
doesn't actaully support multi-line strings (#1396).
We do now treat goto as a keyword (fixes#1653). We don't currently
support labels — those *technically* aren't a token (`:: foo --[[ a
comment ]] ::` is a valid label!), but maybe we could special-case the
short `::foo::` form.
Apparently this has been broken since the file was created in
53546b9f57d9acaa4cdca14ae00eaf68ce8c50bd!? I'm sure I fixed this before,
but maybe that was a different but similar issue >_>.
Convert GLFW's key codes back to their actual key, and then use that
when checking keyboard shortcuts. We *don't* do this for the paste key,
just to be consistent with vanilla's behaviour.
Fixes#2207.
Back in f10e401aea, we changed turtle
overlays to be loaded as a dynamic registry. This solved some of the
problems we had with upgrades (elf-compatibility), and seemed like a
good idea at the time.
However, because overlays are part of datapacks (not resource packs), we
also needed support for loading overlay models, which we did via an
"extra_models.json" file.
With the ItemModel changes, we can go for a different approach:
- Turtle overlays are now stored on the item/BE as a simple
ResourceLocation again.
- The TurtleOverlay class is moved to the client, and loaded from
resource packs, during model loading. They're now split into a baked
form (holding a StandaloneModel) and an unbaked one (holding the
ResourceLocation).
- extra_model.json is no longer supported.
This allows equipping pocket computers on both the back (as before) and
bottom of a pocket computer. The asymmetry is a little unfortunate here,
but makes some sense with the crafting recipe (above goes behind, below
goes on the bottom).
- Move some functionality from IPocketAccess into a PocketComputer
interface (and PocketComputerInternal) interface, used by the pocket
API.
IPocketAccess preserves the same interface as before. Unlike
ITurtleAccess, we /don't/ expose the PocketSide in the public API.
- Several pocket-computer methods (e.g. setUpgradeData, setColour) are
now required to be called on the main thread, and when the computer
is being held. This allows us to write back changes to the item
immediately, rather than the next time the item is ticked.
Sadly this doesn't actually remove the need for onCraftedPostProcess
as I'd originally hoped, but I think does make the code a little
simpler.
- Rename "computercraft:pocket_computer" component to
"computercraft:back_pocket_computer".
- And finally, support multiple upgrades on the pocket computer. This
is actually quite an easy change, just tedious — there's lots of
places to update!
Fixes#1406, and I think fixes#1148 — you can use a speaker to notify
you now.
Every few years I get confused about which side turtle upgrades go on
when crafting. The fact that it's flipped always throws me! Let's add a
comment to the recipe, and add some tests to reassure myself.
- Use new tooltip component registry. This means we can move the
tooltip appending magic to Neo only.
- Use new chunk level change event. I'm not actually sure if we need
this on recent versions (I can't reproduce the monitor update bug
that we introduced this to fix), but I've no clue what's changed here.
0/10, would not recommend. Though increasingly feeling that about
modding as a whole — really not feeling as emotionally rewarding as it
once did.
Server-side changes are well, not simple, but relatively straightforward:
- Block removal code is now called before the BE is removed, not after.
- Monitors now need to track if they've being removed or not again.
- Turtle drop consuming code no longer tries to insert items into
the turtle immediately, instead waiting 'til the action is
complete. Otherwise if the turtle gets destroyed mid-action
(e.g. the block explodes), then it tries to insert its drops into
itself!
We previously guarded against this by checking if the turtle BE had
been removed, but obviously this no longer works, so just easier to
shift the insertion.
- The interface for reading/writing NBT has been overhauled. It has
native "getOr" and codec support (nice!) but also has been changed
again in the latest snapshot (less nice!).
- The dye item component no longer has a "hide tooltip" flag. We now
hide the tooltip with a default component instead.
- Related to the above, we can now do all the tooltip-related things we
needed to do with vanilla's TooltipProvider. This did require
splitting NonNegativeId into subclasses for disk/computer, but
otherwise is quite nice.
- Some changes to model datagen. Annoying, but boring.
- Game tests got a complete overhaul. I'm keeping the interface the
same for now (@GameTest), because I'm blowed if I'm datagenning test
instances :p. If it's any consolation, both NF and Fabric are doing
this too.
Client changes are a bit more involved though:
- VertexBuffer has been entirely removed. We now construct the
GpuBuffer directly.
- BakedModel is gone! Oh this caused so much suffering for turtle
models. I ended up rewriting the whole system in processes (which
then involved PRs to NF and Fabric). Rather than returning a
TransformedModel, turtle models are now responsible for rendering the
model.
This may see another rewrite in the future. I'd like to switch to
JSON-based turtle models (like item models), but that's blocked on
some changes to NF right now.
Sorry to all add-on devs, I know this is a big change.
Reverts 76968f2f28. We'd originally added
this to gather some numbers for #1580, with the hope that it would also
be useful for server admins. Sadly, it's not as accurate as I originally
hoped — the number sometimes goes down for unclear reasons (something to
do with the TLAB maybe??).
Closes#1739.
The main thing of note is Spotless, which also bumps the version of
Ktlint. I've been putting this off for a while[^1], as this changed a
bunch of formatting, and Spotless's (broken) caching was making it hard
to test. Ended up downloading ktlint and running it localy.
[^1]: 8204944b5f
- Actually set colour when constructing the brain.
- Sync it back after crafting, much like we do for upgrades (see
dcc74e15c7) for more details.
We should take a proper look at this on 1.21.4 and make these methods
main-thread only, so we can sync immediately.
Fixes#2157
Fixes#2141. Hah, I wrote some tests for this in
b03546a158, but they pass because hoppers
still support vanilla inventories, but turtles don't.
Wish NeoForge registered a fallback for any inventory, like Fabric does,
but there we go.
- Fix isValidClipboard always returning true.
- Fix characters >=128 being rejected. We changed the signature from a
byte to an int in 0f123b5efd, but
didn't update all call sites.
Valhalla cannot come soon enough. I would love to be able to have
(cheap) wrapper classes for some of these types.
See Zeus-guy's comments in #860.
Sort of closes#2125. I've really struggled to find a way to make it
clear that the information returned here is a snapshot of the current
item, and not a live view and/or proxy. Most wordings I've tried end up
feeling really clunky — given that this is a relatively rare
misunderstanding, let's not stress about this too much.
Oh dear. I'd originally set out to *remove* logic from DiskItem — we're
so close to being able to remove this item in 1.21! However, while
looking at this code, I realised I could remove the whole Forge-specific
doesSneakBypassUse.
We now remove the use hook on the block, and override useOn on the item.
Obvious in retrospect!
Oh. This is from ye olde days (it's one of the first PRs to CC[^1]!). In
pre-1.13 days, furnaces changing their lit state would replace the block
(creating a new BE) and then set back the old BE. CC wouldn't pick up
the second event, and so would continue to use the peripheral from the
first.
We don't really need this any more, for a couple of reasons:
a) Furnaces don't do this any more.
b) Peripherals are now refreshed on the next tick rather than
immediately.
c) Forge's capabilities have an explicit invalidate() hook already. This
technically only detects *removing* block entities, but I'm not sure
there's any cases where you add a block entity without also
triggering a block state change.
Ironically, the place we probably need this more is Fabric, where the
lookup API doesn't have a public invalidate hook (it's hidden away in
the BlockApiCache). I'm mostly relying on c) here, in that we just won't
see this happen in practice.
[^1]: https://github.com/dan200/ComputerCraft/pull/180
We have several items (e.g. ComputerItem), which only exist for their
custom tooltip implementation. We remove these, and replace them
vanilla-style component-based tooltips (TooltipProvider).
The implementation here is a little janky — as the vanilla list of
components is hard-coded, and neither mod loader offers a way to extend
it. For now we just use the generic mod-loader tooltip hook — this
probably would be easier with a mixin, but let's do things Properly.
It would be nice to fully remove DiskItem (we only keep this around for
doesSneakBypassUse), but that can be a future task.
Introduced by the previous commit — I'd made one of the checks too lax.
Add some tests for this, so it doesn't happen again, though this code
does get a complete rewrite in 1.21 anyway >_>.
Another go at fixing #2127.
In a892739f8e we set the precision on the
Tbo uniform. However, this is stripped in the shader pre-processing
Pojav/gl4es does, and so has no effect. As a (terrible) workaround, we
now just ignore shader loading errors. This probably does leak memory
(we'll never clean up the program), but there's not much we can do about
that.
We send the item-form of the current computer in the computer menu data.
However, this leaks the current LockCode, as we include all components.
We now only gather a safe subset of components when constructing the
item.
These just return details about the currently equipped *item*. This
allows us to expose information about the currently equipped upgrade,
without having to invent a whole new format.
Docs are a bit consise, but didn't really know how to flesh them out any
further.
Fixes#964, fixes#1613, closes#1692.
- Remove the /computercraft-computer-folder client command, and replace
it with OPEN_FILE on NeoForge, or a /computercraft-open-folder
command on Fabric (which now accepts a path, rather than an id).
- Store command computer files in "computercraft/command_computer/<id>".
Fixes#1581.
Some people run Minecraft on OpenGL ES GPUs via the gl4es translation
bridge. This sets the default precision for floats and ints, but not
usamplerBuffer.
Using lowp should be fine here (we don't need to encode much info!), but
we use mediump just in case. Have run this through the Mali Offline
compiler, and it seems fine with it.
Fixes#2127.
Please don't talk to me about this. The first couple of hours of this
update were quite enjoyable, and then the rest was one of the most
miserable times I've had modding.
This has been a real slog, partly due to some large MC changes (item
models are a great change, but a pain to adapt to), and partly due to
mental health reasons — honestly, I've opened up my IDE so many times,
and then just closed it because I've hated the thought of even working
on this.
I will publish this to my maven, so mod authors can depend on it, but I
have no plans to publish a 1.21.4 version. 1.21.5 is right around the
corner (again, with some cool, but no-doubt painful changes), and I
need some time to focus on some breaking changes.
This commit actually includes the 1.21.3 update — the git history got so
messy here, so I just clobbered the whole thing. Sorry.
== Rendering ==
- Remove TBO monitor renderer: There was a big overhaul to how shaders
are defined and loaded in 1.21.2. It might have been possible to
update the monitor shader code to this version, it doesn't see much
use nowadays, so let's just delete it.
This is a real shame — the TBO renderer was one of my favourite
projects I've worked on. Unfortunately, it just doesn't seem worth
the ongoing maintenance burden. It lives on in the standalone
emulator :D.
- Similarly, the VBO rendering code got a bit of an overhaul. We no
longer use a custom VBO subclass, and instead just hack vanilla's to
support changing the number of vertices rendered.
This does mean we need to construct a MeshData, rather than a raw
ByteBuffer. This isn't too hard, but not sure how it'll play with
Iris. Given recent vanilla performance improvements, maybe we can
remove our Unsafe code and use a normal BufferBuilder now.
- Remove our custom emissive model code, now that vanilla supports
it. We should add emissive textures to some other models at some
point.
- Remove mod-loader specific model code, and replace it with vanilla's
ItemModel. This does constrain the design of turtle upgrade modellers
quite a bit — we now only accept an untransformed BakedModel or a
transformed ItemStack model. We may relax this in the future,
unclear.
This change does mean that updsidedown turtles are broken. RIP :(.
- Entity rendering now separates reading state from the entity from
actual rendering. This means we need to pass some extra state around
for item frames. Easy on Forge, but requires a mixin on Fabric.
== Recipes ==
There were several major changes to ingredients this update. The code
here hasn't been very well tested right now — might be nice to add some
game tests for this.
- Ingredients can no longer be constructed directly from a tag key (it
needs to be fetched from the current registries), so the recipe
generation code needs a bit of a reshuffle.
- DiskRecipe now accepts a custom list of ingredients, rather than
being hard-coded (fixes#1755). Recipes can now return custom
`RecipeDisplay`s used to show a recipe in the crafting book. We use
this to replace the impostor recipes.
I'm not entirely sure how well this'll play with other recipe
mods. Here's hoping.
- Similarly, our recipe mod integration has been updated to use
RecipeDisplay. We had to do this as ingredients no longer accept
arbitrary ItemStacks (only a specific item).
== Misc ==
- Blocks/items now need to know their ID ahead of time (so they can
compute their description). This requires some reshuffling to the
registration code, but it's pretty minor.
- updateShape and neighborChanged no longer take a direction (the
Orientation is mostly null) and so invalidates all redstone and
peripherals.
- All the positions were lowered by one in game tests. It's a good
change (they now match the positions in structures), but annoying to
update for!
This bumps them to be 48x48, which allows them to be downscaled to a
mipmap level of 4. We possibly should bump these to be 64x64 (actual
power of two), but I kinda want to avoid that, as it's so much wasted
space. If this does become a problem, we should probably put these on a
separate atlas instead.
Honestly, the whole design around volume and playSound/playAudio is a
little janky — it probably should be a separate setVolume method which
updates directly. But too late to change that now, so let's do what we
can.
See #2108
ComputerItem was just an empty subclass, so no sense keeping around. We
probably could get rid of CommandComputerItem too (just do an `instanceof
GameMasterBlock` in `getPlacementState`), but there's no rush.
We now register these separately, rather than relying on the implicit
IMedia. This allows us to share a bit more logic between
PocketComputerItem and AbstractComputerItem. This doesn't make much
difference on 1.20.1, but does help a bit more on 1.21.1.
Now, hear me out, what if instead of having three @Nullable annotations,
we had *four*?
I've been wanting to switch away from javax.annoations for a while. The
library has been deprecated for ever and, unlike other @Nullable
annotations, the annotation is attached to the parameter/function
itself, rather than the type.
We use JSpecify rather than one of the alternatives (JetBrains,
CheckerFramework) mostly because it's what NullAway recommends. We keep
CheckerFramework around for @DefaultQualifier, and JB's for @Contract.
There are some ugly changes here — for instance, `@Nullable byte[]` is
replace by `byte @Nullable`, and `@Nullable ILuaMachine.Factory` is
`ILuaMachine.@Nullable Factory`. Ughr, I understand why, but it does not
spark joy :).
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 94ad6dab0e, 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.
I removed this in fc834cd97f, way back in
late 2024. Looks like it's been updating in the meantime and I hadn't
noticed, so add it back.
I've simplified the code a little bit, to make use of our NeoForge's new
capability system, but otherwise it's almost exactly the same :D.
- 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!
It's not actually safe to reuse this, as we need to recompute the
internal StackedContents each time the inventory changes, otherwise
ShapelessRecipe.matches will continue to return true, even if the actual
inventory doesn't include the required items.
Fixes#2094
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.
- 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 adds a new "computercraft:storage_capacity" component to items (and
"Capacity" NBT tag to BEs), that overrides the capacity for the given
item.
Fixes#1814
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.
This occurs when syncing the server config to the client. Ideally we'd
not hit this code path in the first place, but unfortunately there's no
way to tell where the config file comes from.
Fixes#2065
As part of this, we also rewrite some of the turtle placing code, and
how it uses the turtle_can_use tag:
Minecraft 1.21 cleaned up the item/block clicking code a little bit,
splitting Block.use into Block.useItemOn and Block.useWithoutItem. The
first of these is pretty much exactly what we wanted in the first place,
so the tag was kinda redundant and we commented it out in the 1.21
update.
This was never meant to be a long-term fix, but time has gone by anyway.
We now check that tag, and call useWithoutItem() if present —
effectively restoring the previous behaviour.
Fixes#2011
When "placing" the item (e.g. hoeing soil), we were using the tool item,
rather than the passed stack. This was introduced in
9a914e75c4, 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 bdffabc08e),
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).
This was only present on the 1.21 NF version, hence not being noticed
before. Fixes#2020.
I pruned my Gradle cache recently, and I'm on some truly terrible hotel
wifi, so this is entirely untested. No beta, we die like men.
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.
Some mods run their own datafixer chain, rather than piggybacking on top
of vanilla's. This is A BAD IDEA, but what can you do. If such a mod
tries to use ItemStackComponentizationFix in their own schema, then
CC:T's mixins will try to look up the turtle block entitie, and fail (as
they're not registered under the modded schema).
We now inject the block entity fix as a separate fixer, rather than
abusing ItemStackComponentizationFix.
See #2012
- 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
4228011b84, and I'm sure I tested it then.
Fixes#2013. Probably.
Been dragging my feet over this for a while now, but increasingly
uncomfortable with Overwolf. I'm not going to delete the project (or any
existing versions), just not publish any new versions there.
- Don't construct a fake player when crafting: vanilla now has its own
automated crafting, so no longer requires the presence of a player.
- Fix remainder stack not being set in some situations. Closes#2007.
- Move redstone methods out of the IAPIEnvironment, and into a new
RedstoneAccess. We similarly move the implementation from Environment
into a new RedstoneState class.
The interface is possibly a little redundant (interfaces with a
single implementation are always a little suspect), but it's nice to
keep the consumer/producer interfaces separate.
- Abstract most redstone API methods into a separate shared class, that
can be used by both the rs API and the new redstone relay.
- Add the new redstone relay block.
The docs are probably a little lacking here, but I really struggled to
write anything which wasn't just "look, it's the same as the redstone
API".
Several functions accept a "timeout" argument, which is implemented by
starting a timer, and then racing the desired output against the timer
event.
However, if the timer never wins, we weren't cancelling the timer, and
so it was still queued. This is especially problematic if dozens or
hundreds of rednet (or websocket) messages are received in quick
succession, as we could fill the entire event queue, and stall the
computer.
See #1995
As part of the multi-loader work, we unified some of our event listening
code (0908acbe9b). This incorrectly caused
client pocket computer state to be reset when the player changes
dimension, rather than when the player (dis)connects.
The server code isn't aware of this behaviour, and so does not resend
pocket computer state when the player moves level. We could change this,
but just fixing when we clear the pocket computer state is a much nicer
fix!
Fixes#2004
v4 of the action seems to be much more restrictive in what can upload
coveage. I'll miss this (the historic view of coverage was nice!), but
not worth trying to get working again.
We also stop running client tests. These break so often, we only really
ran them for code coverage reasons.
The most annoying thing about pocket computers is handling computer
state (label, upgrades, etc...). Unlike other computers, which are tied
to a specific block entity, pocket computers float untethered. We can't
hold a reference to a specific item stack (as the computer might be
moved between inventories, crafted, etc...), so instead we explicitly
sync data between the computer and *current* stack, whenever the holding
player/entity is ticked.
In ed0b156e05 I rewrote this syncing code
to always treat the computer as the source of truth. Upgrades would be
copied to the computer, but never the other way round. However, this
meant that upgrades obtained by crafting would never be detected,
requiring the computer to be destroyed and recreated.
A more long-term fix here is probably to rewrite IPocketAccess to only
allow updating upgrade data on the main thread, and when we have a valid
PocketHolder. This is a breaking API change though, and so will have to
wait for 1.21.3.
For now, we just add a hook that refreshes the upgrade after crafting.
Fixes#1957
The update to Python 3.12 has broken the pre-commit action (as it
installs via pip rather than pipx). The maintainer seems unwilling to
fix it (to put it diplomatically), so let's just stop using the action
and imlement it ourselves.
One of the easiest things to mess up with writing a custom peripheral is
handling attached peripherals. IPeripheral.{attach,detach} are called
from multiple threads, so naive implementations that just store
computers in a set/list will at some point throw a CME.
Historically I've suggested using a concurrent collection (i.e.
ConcurrentHashMap). While this solves the problems of CMEs, it still has
some flaws. If a computer is detached while iterating over the
collection, the iterator will still yield the now-detached peripheral,
causing usages of that computer (e.g. queueEvent) to throw an exception.
The only fix here is to use a lock when updating and iterating over the
collection. This does come with some risks, but I think they are not too
serious:
- Lock contention: Contention is relatively rare in general (as
peripheral attach/detach is not especially frequent). If we do see
contention, both iteration and update actions are cheap, so I would
not expect the other thread to be blocked for a significant time.
- Deadlocks: One could imagine an implementation if IComputerAccess
that holds a lock both when detaching a peripheral and inside
queueEvent.
If we queue an event on one thread, and try to detach on the other,
we could see a deadlock:
Thread 1 | Thread 2
----------------------------------------------------------
AttachedComputerSet.queueEvent | MyModem.detach
(take lock #1) | (take lock #2)
-> MyModem.queueEvent | AttachedComputerSet.remove
(wait on lock #2) | (wait on lock #1)
Such code would have been broken already (some peripherals already
use locks), so I'm fairly sure we've fixed this in CC. But definitely
something to watch out for.
Anyway, the long and short of it:
- Add a new AttachedComputerSet that can be used to track the computers
attached to a peripheral. We also mention this in the attach/detach
docs, to hopefully make it a little more obvoius.
- Update speakers and monitors to use this new class.
Iris now has built-in support for NeoForge, so we can use the same
integration on both.
We also re-enable Forge's client tests, and test Iris there too.
Fixes#1967
Under Forge, netty-codec lives on the BOOT layer. However, this means it
does not have access to our jzlib (which lives on the GAME layer). To
fix this, we now shadow netty-codec (and its dependents, like netty-http
and netty-proxy) rather than jar-in-jaring them.
This involves some horrible build logic, but means websocket compression
works on Forge.
Fixes#1958.
- Place the player above the test region before running tests. This
guarantees the client has the chunks loaded (and rendered) before we
start running tests.
- Reset the time after running the monitor/printout tests.
- Fix rotation of turtle item models.
This still isn't perfect - the first test still fails with Iris and
Sodium - but is an improvement. Probably will still fail in CI though
:D:.
Previously we used an RGBA byte array. However, this comes with some
overhead (extra memory reads, bounds checks).
Minecraft 1.21+ uses ARGB32 colours for rendering (well, in the public
code — internaly it converts to ABGR), so it makes sense to match that
here.
We also add some helper functions for dealing with ARGB32 colours. These
can be removed in 1.21, as Minecraft will have these builtin.
This probably isn't useful in practice — nobody is escaping 1MB of data.
Right. Right???? But no harm in doing it.
- Cache globals as locals.
- Remove redundant pattern capture.
- Merge string.format calls into one.
Also remove the "if str then" check. I assume we accepted nil values a
long time ago, but that was broken when we added arg checks. Woops!
While mods shouldn't be depending on common, sometimes it's unavoidable
(e.g. for cc-prometheus). In those cases, you want all the CC classes
available, not just the common ones.
In cdcd82679c, we rewrote the Lua
conversion function to update the "Java -> Lua" mapping after
conversion, rather than part way through.
This made the code a little cleaner (as we only updated the mapping in
one place), but is entirely incorrect — we need to store the object
first, in order to correctly handle recursive objects — otherwise we'll
just recurse infinitely (or until we overflow).
This partially reverts the above commit, while preserving the new
behaviour for singleton collections.
Fixes#1955.
Even more of these! I really need to stop getting bored half way through
writing sentences and then continue writing at the wrong place. Or,
y'know, get better at proof reading.
While Item.inventoryTick is passed a slot number, apparently that slot
corresponds to the offset within a particular inventory compartment
(such as the main inventory or armour)[^1], rather than the inventory as
a whole.
In the case of the off-hand, this means the pocket computer is set to be
in slot 0. When we next tick the computer (to send terminal updates), we
then assume the item has gone missing, and so skip sending updates.
Fixes#1945.
[^1]: A fun side effect of this is that the "selected" flag is true for
the off-hand iff the player has slot 0 active. This whole thing feels
like a vanilla bug, but who knows!
- Mention only diamond tools can be used as upgrades, and be clearer
that only the pickaxe and sword are actually useful. We probably
could be more explicit here, but struggled to find a way to do that.
- Expliitly list which peripherals can be equipped.
- Add turtle recipes.
It's not entirely clear what the correct behaviour of fs.getDir("..")
should be, and there's not much consensus between various languages.
I think the intended behaviour of this function is to move "up" one
directory level in the path, meaning it should return "../..".
CC tries to preserve sharing of objects when crossing the Lua/Java
boundary. For instance, if you queue (or send over a modem)
`{ tbl, tbl }`, then the returned table will have `x[1] == x[2]`.
However, this sharing causes issues with Java singletons. If some code
uses a singleton collection (such as List.of()) in multiple places, then
the same Lua table will be used in all those locations. It's incredibly
easy to accidentally, especially when using using Stream.toList.
For now, we special case these collections and don't de-duplicate them.
I'm not wild about this (it's a bit of a hack!), but I think it's
probably the easiest solution for now.
Fixes#1940
- Add a new custom lectern block, that is used to hold the printed
pages. We have to roll quite a lot of custom logic, so this is much
cleaner than trying to mixin to the existing lectern code.
- Add a new (entity) model for printed pages and books placed on a
lectern. I did originally think about just rendering the item (or the
in-hand/map version), but I think this is a bit more consistent with
vanilla.
However, we do still need to sync the item to the client (mostly to
get the current page count!). There is a risk of chunkbanning here,
but I think it's much harder than vanilla, due to the significantly
reduced page limit.
Rather than having a general "held-item" container, we now have a
specialised one for printouts. This now is a little more general,
supporting any container (not just the player inventory), and syncs the
current page via a data slot.
Currently this isn't especially useful, but should make it a little
easier to add lectern support in the future.
There's a whole load of gnarly issues that occur when a turtle is broken
mid-dig/attack (normally due to an explosion). We fixed most of these in
24af36743d, but not perfectly.
Part of the fix here was to not capture drops if the turtle BE has been
removed. However, on removal, turtles drop their items *before* removing
the BE. This meant that the drop consumer still triggered, and attempted
to insert items back into the turtle.
This bug only triggers if the turtle contains a stack larger than 10
(ish, I think) items, which is possibly why I'd never reproduced before.
We now drop items after removing the BE, which resolves the issue.
Fixes#1936.
This was originally noticed on 1.21, as it causes disk drives to not be
detected as peripherals. However, things will still be broken (albeit
more subtly) on 1.20, so worth fixing here.
There's been a couple of bug reports in the past where the game would
crash if a turtle is destroyed while breaking a block (typically due to
the block exploding). This commit adds a test, to ensure that this is
handled gracefully.
I'm not entirely sure this is testing the right thing. Looking at the
issues in question, it doesn't look like I ever managed to reproduce the
bug. However, it's hopefully at least a quick sanity test to check we
never break this case.
Computer drops are currently[^1] implemented via a dynamic drop. To
support this, we need to inject the dynamic drop into the loot
parameters.
We currently do this by implementing our own drop logic in
playerWillDestroy[^2], manually creating the loot params and adding our
additional drop. However, if the item is dropped via some other method
(such as via explosions), we'll go through vanilla's drop logic and so
never add the dynamic drop!
The correct way to do this is to override getDrops to add the dynamic
drop instead. I don't know why we didn't always do this -- the code in
question was first written for MC 1.14[^3], when things were very
different.
[^1]: This is no longer the case on 1.21, where we can just copy
capabilities.
[^2]: We need to override vanilla's drop behaviour to ensure items are
dropped in creative mode.
[^3]: See 594bc4203c. Which probably means
the bug has been around for 5 years :/.
Disk IDs and treasure disk colour were not being correctly converted.
This also adds several tests to ensure that these items are handled
correctly.
Closes#1934.
There's a mismatch between how Lua and JSON's values are defined, which
means that serialisation is a little confusing at times. This commit
attempts to document them a little better.
Closes#1885, closes#1920
This check should be impossible (the BE has not been removed, but is no
longer present in the world), but we've had one instance where it has
happened (#1925). I don't have a good solution here, so at least let's
print both BEs for now.
Historically we'd provide specific data based on the current item and
NBT (hence BasicItemDetailProvider). However, with components, it now
makes sense to provide details for all items with a specific component.
This commit adds a new "ComponentDetailProvider" class, that reads a
component from an item stack (or other component holder) and provides
details about it if present.
Allow registering details providers matching any super type, not just
the exact type. This is mostly useful for 1.21, where we can have
providers for any DataComponentHolder, not just item stacks.
This adds a new mechanism for attaching additional objects to a
computer, allowing them to be queried by other mods. This is primarily
designed for mods which add external APIs, allowing them to add APIs
which depend on the computer's position or can interact with the turtle
inventory.
I will stress that the use-cases for custom APIs are few and far
between. Almost all the time a peripheral would be the better option,
and I am wary that this PR will encourage misuse of APIs. However, there
are some legitimate use-cases, and I think we should enable them.
- Add a new "ComputerComponent" class, and several built-in components
(for turtle, pocket and command computers).
- Add a method to `IComputerSystem` to read a component from the
computer. We also add methods to get the level and position of the
computer.
- Move all our existing APIs (built-in turtle, pocket, command) to use
the public API.
We don't actually use this functionality in other projects (e.g.
emulators). In fact the method to add new APIs only exists in the mod
itself!
We still need some mechanism to remove mounts when the computer is
shutdown. We add a new ApiLifecycle interface (with startup and
shutdown hooks), and use those in the ComputerSystem impl.
Oh, I hate the pocket computer code so much. Minecraft was really not
designed to attach this sort of behaviour to computers. This commit is
an attempt of cleaning this up[^1].
Firstly, we move the the pocket computer state (upgrades, light) out of
PocketServerComputer and into a new PocketBrain class. This now acts as
the sole source-of-truth, with all state being synced back to the
original item stack on the entity tick.
This also adds a new PocketHolder interface, which generalises over the
various types that can hold a pocket computer (players and item
entities right now, possibly lecterns in the future).
[^1]: I'd say simplifying, but this would be a lie.
- Update to latest NeoForge, fixing issues with config API changes.
Closes#1903.
- Update to latest Fabric, switching to the ender pearl conventional
tag, and new loot API.
In c8eadf4011 we marked our various modems
as "brittle", which ensures they do not pop-off computers when the whole
structure moves.
However, this still requires the modem to be glued — if the modem is
outside the superglue range, it will still pop off. We can fix it by
registering a special "attached check" for the various modem blocks,
which says that the modem should be moved when the adjacent block does.
Fixes#1913
The EsperNet webchat has been decommissioned, with no plans to
replace. KiwiIRC is the recommended replacement, but I'm not comfortable
using it as a drop-in replacement[^1], so I've rephrased this section to
make it more clear.
[^1]: There seems to be ongoing issues with TLS certificates
(https://github.com/kiwiirc/kiwiirc/issues/1870), which meant it
wasn't usable when I first tried.
This avoids us having to support requireless environments inside
cc.strings.
I do kinda wonder if os.loadAPI-loaded files should also have their own
shared "require", just so we're not loading 10 copies of cc.expect.
- Update location of the generated language file to point to common
rather than Fabric.
- Remove usage of OrderedDict, as dicts are ordered on recent versions
of Python.
- Rename ToolActions to ItemAbilities. Closes#1881.
- Remove our source set helper, as NG has built-in support for this
now.
- Remove our code to generate new JavaExec tasks from runs, as NG now
generates JavaExec tasks normally.
- Add a new computercraft:turtle_overlay dynamic registry, which stores
turtle overlays. Turtle overlays are just a model id and an
(optional) boolean flag, which specifies whether this overlay is
compatible with the elf/christmas model.
- Change the computercraft:overlay component to accept a
Holder<TurtleOverlay> (instead of just a model ID). This accepts both
an overlay ID or an inline overlay object (e.g. you can do
cc:turtle_normal[computercraft:overlay={model:"foo"}].
- Update turtle model and BE rendering code to render both the overlay
and elf (if compatible). Fixes#1663.
- Ideally we'd automatically load all models listed in the overlay
registry. However, resource loading happens separately to datapacks,
so we can't link the two.
Instead, we add a new assets/computercraft/extra_models.json file
that lists any additional models that should be loaded and baked.
This file includes all built-in overlay models, but external resource
packs and/or mods can easily extend it.
Build system:
- Switch to our new maven server. This has a cleaner separation between
published packages and mirrored packages, to avoid leaking those into
other people's builds.
- Update Gradle and Loom versions.
Code:
- Link to definitions instead in the breaking changes page.
- Fix several unused variable warnings.
Other:
- Remove unsupported Minecraft versions from the issue template.
The X scale factor should now be flipped. I'm not quite sure what in MC
has meant this should be changed, possibly the cameraOrientation matrix?
Fixes#1872
Due to the earlier commits, the only functionality this block entity
adds is to register the command API. This commit:
- Add the command API when constructing the ServerComputer instead.
This is not a good long-term solution (I think we need to make API
factories more powerful), but is sufficient for now.
- Replace usages of CommandComputerBlockEntity with a normal
ComputerBlockEntity.
- Move the command permisssion checks to a new
ComputerFamily.checkUsable method (from
CommandComputerBlockEntity and ViewComputerMenu). I don't feel great
about putting new functionality in ComputerFamily (trying to move
away from it), but I think this is fine for now.
- Use this method from within the computer menu and computer block, to
check whether computers can be interacted with.
- Remove ViewComputerMenu, as it now no longer needs any special
is-usable logic.
Historically we used to have separate menu types for computers and
pocket computers, as the screen had to be initialised with the correct
terminal size.
However, as of c49547b962 (which was
admittedly two years ago now), we have the terminal available when
constructing the screen, and so the code for the two is identical.
This change actually merges the two screens, replacing usages of the
pocket computer UI with the computer one.
API Changes:
- Minecraft had updated ModelResourceLocation to no longer inherit from
ResourceLocation.
To allow referencing both already baked models
(ModelResourceLocation) and loading new models (via ResourceLocation)
in turtle model loadders, we add a new "ModelLocation" class, that
acts as a union between the two.
I'm not entirely convinced by the design here, so might end up
changing again before a stable release.o
- Merge IMedia.getAudioTitle and IMedia.getAudio into a single
IMedia.getAudio method, which now returns a JukeboxSong rather than a
SoundEvent.
Other update notes:
- Minecraft had rewritten how buffers are managed again. This is a
fairly minor change for us (vertex -> addVertex, normal -> setNormal,
etc...), with the exception that you can no longer use
MultiBufferSource.immediate with the tesselator.
I've replaced this with GuiGraphics.bufferSource, which appears to be
fine, but worth keeping an eye on in case there's any odd render
state issues.
- Crafting now uses a CraftingInput (a list of items) rather than a
CraftingContainer, which allows us to simplify turtle crafting code.
Historically (and according to the docs) getAudioTitle returned "false"
when the drive was empty (or had invalid media), and "null" when the
disk had no item. This was accidentally changed in a later refactor --
this change fixes that behaviour.
We keep getting bug reports on 1.20.1 about an Optifine bug that causes
Forge's capabilities to not work (#1458). The cause of this bug is not
immediately visible to users, and can be very confusing when hit.
Optifine have not released a fix for this bug (despite it being reported
a year ago), and we continue to receive bug reports about it.
Nobody likes it when mods complain about other mods. So much Minecraft
drama can be traced back to this, and it's a slippery slope to go down.
I've tried to keep this as unobtrusive as possible — it's just a chat
message at world join, and it'll turn off if the bug is fixed.
Rather than having one single hard-coded recipe, we now have separate
recipes for printed pages and printed books. These recipes are defined
in terms of
- A list of ingredients (like shapeless recipes).
- A result item.
- An ingredient defining the acceptable page items (so printed page(s),
but not books). This cannot overlap with any of the main ingredients.
- The minimum number of printouts required.
We then override the shapeless recipe crafting logic to allow for
multiple printouts to appear.
It feels like it'd be nice to generalise this to a way of defining
shapeless recipes with variable-count ingredients (for instance, the
disk recipe could also be defined this way), but I don't think it's
worth it right now.
This solves some of the issues in #1755. Disk recipes have not been
changed yet.
- Update EMI and REI integration, and fix some issues with the upgrade
crafting hooks.
- Just use smooth stone for recipes, not #c:stone. We're mirroring
redstone's crafting recipes here.
- Some cleanup to printouts.
- Remote upgrade data generators - these can be replaced with the
standard registry data generators.
- Remove the API's PlatformHelper - we no longer have any
platform-specific code in the API.
Turtles currently read their textures from a single 128x128 sprite
sheet. Most of this texture is unused which means we end up wasting a
lot of the block texture atlas[^1].
This change splits up the turtle textures into individual 32x32
textures[^2], one for each side, and then an additional backpack
texture.
I'm very sorry to any resource pack artists out there. The
tools/update-resources.py script will update existing packs, but does
not (currently) handle non-standard resolutions.
[^1]: It used to be worse: https://github.com/dan200/ComputerCraft/issues/145
[^2]: Turtle textures are a bit weird, in that they mostly *look* 16x16,
but have some detail in places.
Turtle tools were not equippable, as we considered the stack enchanted
due to the item's base attribute modifiers. We now only check the
component patch for enchantments/attribute modifiers.
This also removes the craftItem property of tools - this hasn't worked
since we added support for enchanted tools!
Fixes#1810
- Use TinyRemapper to remap mixins on Fabric. Mixins in the common
project weren't being remapped correctly.
- Update to latest NeoForge
- Switch to the new tick events.
- Call refreshDimensions() in the fake player constructor.
Replace turtle_modem_{normal,advanced} with a single turtle_modem
upgrade (and likewise for pocket upgrades). This is slightly more
complex (we now need a custom codec), but I think is more idiomatic.
Ever since 1.17, turtle and pocket upgrades have been loaded from
datpacks, rather than being hard-coded in Java. However, upgrades have
always been stored in our own registry-like structure, rather than using
vanilla's registries.
This has become a bit of a problem with the introduction of components.
The upgrade components now hold the upgrade object (rather than just its
id), which means we need the upgrades to be available much earlier (e.g.
when reading recipes).
The easiest fix here is to store upgrades in proper registries instead.
This means that upgrades can no longer be reloaded (it requires a world
restart), but otherwise is much nicer:
- UpgradeData now stores a Holder<T> rather than a T.
- UpgradeSerialiser has been renamed to UpgradeType. This now just
provides a Codec<T>, rather than JSON and network reading/writing
functions.
- Upgrade classes no longer implement getUpgradeID(), but instead have
a getType() function, which returns the associated UpgradeType.
- Upgrades are now stored in turtle_upgrade (or pocket_upgrade) rather
than turtle_upgrades (or pocket_upgrades). This will break existing
datapacks, sorry!
This adds a new "recipe function" system, that allows transforming the
result of a recipe according to some datapack-defined function.
Currently, we only provide one function: computercraft:copy_components,
which copies components from one of the ingredients to the result. This
allows us to replace several of our existing recipes:
- Turtle overlay recipes are now defined as a normal shapeless recipe
that copies all (non-overlay) components from the input turtle.
- Computer conversion recipes (e.g. computer -> turtle, normal ->
advanced) copy all components from the input computer to the result.
This is more complex (and thus more code), but also a little more
flexible, which hopefully is useful for someone :).
In 1.20.1, Forge and Fabric have different "common" tag conventions (for
instance, Forge uses forge:dusts/redstone, while Fabric uses
c:redstone_dusts). This means the generated recipes (and advancements)
will be different for the two loader projects. As such, we run data
generators for each loader, and store the results separately.
However, aside from some recipes and advancements, most resources /are/
the same between the two. This means we end up with a lot of duplicate
files, which make the diff even harder to read. This gets worse in
1.20.5, when NeoForge and Fabric have (largely) unified their tag names.
This commit now merges the generated resources of the two loaders,
moving shared files to the common project.
- Add a new MergeTrees command, to handle the de-duplication of files.
- Change the existing runData tasks to write to
build/generatedResources.
- Add a new :common:runData task, that reads from the
build/generatedResources folder and writes to the per-project
src/generated/resources.
NF now loads mods from neoforge.mods.toml rather than mods.toml, so CC
wasn't actually being loaded. Tests all passed, because they didn't get
run in the first place!
- Use enums for key and mouse actions, rather than integer ids.
- Change TerminalState to always contain a terminal. We now make
TerminalState nullable when we want to skip sending anything.
- Update Gradle to 8.7
- Configure IntelliJ to build internally, rather than delgating to
Gradle. We've seen some weird issues with using delegated builds, so
best avoided.
- Remove gitpod config. This has been broken for a while (used Java 16
rather than 17) and nobody noticed, so I suspect nobody uses this.
- Add the core TeaVM jar to the runtime the classpath, to ensure
various runtime classes are present.
- Fix computer initialisation errors not being displayed on the screen.
The terminal was set to the default 0x0 size when logging the error,
and so never displayed anything!
Rather than handling right clicks within the block entity code, we now
handle it within the block. Turtles now handle the nametagging
behaviour themselves, rather than overriding canNameWithTag.
Rather than rendering the background further back. This was causing some
of the pages to not be rendered. I'm not quite sure why this is -- there
shouldn't be any z-fighting -- but this does work as a fix!
Fixes#1777
Minecraft.hitResult may /technically/ be null when rendering a turtle.
In vanilla, this doesn't appear to happen, but other mods (e.g.
Immersive Portals) may still take advantage of this.
This hitResult is then propagated to BlockEntityRenderDispatcher, where
the field was /not/ marked as nullable. This meant we didn't even notice
the potential of an NPE!
Closes#1775
This fixes several issues with @Nullable fields not being checked. This
is great in principle, but a little annoying in practice as MC's
@Nullable annotations are sometimes a little overly strict -- we now
need to wrap a couple of things in assertNonNull checks.
This theoretically allows you to use the emulator to run the test suite
(via --mount-ro projects/core/src/test/resources/test-rom/:test-rom),
but not sure how useful this is in practice.
This tells Create that modems will pop-off if their neighbour is moved,
and so changes the order that the block is moved in.
We possibly should use BlockMovementChecks.AttachedCheck instead, to
properly handle the direction modems are facing in. However, this
doesn't appear to be part of the public API, so probably best avoided.
Fixes#948
When the terminal data is not present, width/height are set to 0, rather
than the terminal's width/height. This meant we'd create an empty
terminal, which then crashes when we try to render it.
We now make the terminal nullable and initialise it the first time we
receive the terminal data. To prevent future mistakes, we hide
width/height, and use TerminalState.create everywhere.
Fixes#1765
- Mention the timer event in os.startTimer. Really we should have a
similar example here too, but let's at least link the two for now.
- Fix strftime link
I have mixed feelings about speaker.playSound. On one hand, it's pretty
useful to be able to play any sound. On the other, it sometimes feels
... maybe a little too magic?
One particular thing I don't like is that it allows you to play
arbitrary records, which sidesteps both a vanilla mechanic (finding
record discs) and existing CC functionality (disk.playAudio). We now
prevent playing record tracks from the speaker.
This was added in 4675583e1c to handle
Forge on longer supporting RUN_COMMAND for client-side commands.
However, the mixins are still present on NF/1.20.4, so we don't need
this!
In 5d8c46c7e6, we switched to using UUIDs
for looking up computers (rather than an integer ID). However, for
compatibility in some of the command code, we need to maintain the old
integer lookup map.
Most of the code was updated to handle this, *except* the code to remove
a computer from the registry. This meant that we'd fail to remove a
computer from the UUID lookup map, so computers ended up in a phantom
state where they were destroyed, but still accessible.
This is not an issue on 1.20.4, because the legacy int lookup map was
removed.
Fixes#1760
The two mod loaders expose different methods for this (Forge's method
takes a ItemPropertyFunction, Fabric's a ClampedItemPropertyFunction).
This is fine in a Gradle build, as the methods are compatible. However,
when running from IntelliJ, we get crashes as the common code tries to
reference the wrong method.
We now pass in the method reference instead, ensuring we use the right
method on each loader.
BYTECODE WAS NOT SUPPOSED TO BE REWRITTEN
YEARS OF DEBUGGING REMAPPING FAILURES yet NO ACTUAL SOLUTION FOUND.
Wanted to use Mixins for anyway for a laugh? We had a tool for that: it
was called "FABRIC LOOM".
"Yes, please produce completely broken jars for no discernable reason"
Statements dreamed up by the utterly Deranged.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
This removes our two mixins used on Forge:
- Breaking progress for cabled/wired modems.
- Running client commands from chat click events. We now suggest the
command on Forge instead.
Occasionally we get issues where the mixin annotation processor doesn't
write its tsrg file in time for the reobfJar/reobfJarJar task. I thought
we'd fixed that cb8e06af2a, but sometimes
we still produce missing jars - I have a feeling this might be to do
with incremental compilation.
We can maybe re-evaluate this on 1.20.4, where we don't need to worry
about remapping any more.
We were seeing some strange issues in the Fabric test code where we
tried to load the implementation from a different classloader. This
ensures that the classloaders are consistent.
Due to the asynchronous nature of main-thread tasks, it's possible for
them to be executed on peripherals which have been detached. This has
been known for a long time (#893 was opened back in 2021), but finding a
good solution here is tricky.
Most of the time the method will silently succeed, but if we try to
interact with an IComputerAccess (such as in inventory methods, as seen
in #1750), we throw a NotAttachedException exception and spam the logs!
This is an initial step towards fixing this - when calling a peripheral
method via peripheral.call/modem.callRemote, we now wrap any enqueued
main-thread tasks and silently skip them if the peripheral has been
detached since.
This means that peripheral methods may start to return nil when they
didn't before. I think this is *fine* (though not ideal for sure!) - we
return nil if the peripheral has been detached, so it's largely
equivalent to that.
Double chests peripherals were getting reattached every time there was a
block update, as the inventories were not comparing equal (despite being
so!). We now check for a couple of common cases, which should be enough
for vanilla/vanilla-like inventories.
I actively Do Not Like This Code, but do not see a good alternative.
This should never happen, but apparently it does!? We now log an error
(rather than crashing), and include the original BE (and associated
block), as the BE type isn't very useful.
See #1750. Technically this fixes it, but want to do some more poking
there first.
Here's a fun bug you can try at home:
- Create a new world
- Spawn in a pocket computer, turn it on, and place it in a chest.
- Reload the world - the pocket computer in the chest should now be
off.
- Spawn in a new pocket computer, and turn it on. The computer in chest
will also appear to be on!
This bug has been present since pocket computers were added (27th March,
2024).
When a pocket computer is added to a player's inventory, it is assigned
a unique *per-session* "instance id" , which is used to find the
associated computer. Note the "per-session" there - these ids will be
reused if you reload the world (or restart the server).
In the above bug, we see the following:
- The first pocket computer is assigned an instance id of 0.
- After reloading, the second pocket computer is assigned an instance
id of 0.
- If the first pocket computer was in our inventory, it'd be ticked and
assigned a new instance id. However, because it's in an inventory, it
keeps its old one.
- Both computers look up their client-side computer state and get the
same value, meaning the first pocket computer mirrors the second!
To fix this, we now ensure instance ids are entirely unique (not just
per-session). Rather than sequentially assigning an int, we now use a
random UUID (we probably could get away with a random long, but this
feels more idiomatic).
This has a couple of user-visible changes:
- /computercraft no longer lists instance ids outside of dumping an
individual computer.
- The @c[instance=...] selector uses UUIDs. We still use int instance
ids for the legacy selector, but that'll be removed in a later MC
version.
- Pocket computers now store a UUID rather than an int.
Related to this change (I made this change first, but then they got
kinda mixed up together), we now only create PocketComputerData when
receiving server data. This makes the code a little uglier in some
places (the data may now be null), but means we don't populate the
client-side pocket computer map with computers the server doesn't know
about.
- Remove "initial connections" flag, and just refresh connections +
peripherals on the first tick.
- Remove "peripheral attached" from NBT, and just read/write it from
the block state. This might cause issues with #1010, but that's
sufficiently old I hope it won't!
Our GatedPredicate hack was clever, but also fundamentally didn't work.
The predicate is called before extraction, so if extraction fails (for
instance, canTakeItemThroughFace returns false), then we still think an
item has been removed.
To fix that, we inline StorageUtil.move, specialising it for what we
need.
This feels a little overkill, but nice to standardise how this code
looks.
There's a bit of me which wonders if we should remove
IPeripheral.equals, and just use Object.equals, but I do also kinda like
the explicitness of the current interface? IDK.
This ensures the client decoder is in sync with the server. Well, mostly
- we don't handle the anti-jerk, but that should correct itself within a
few samples.
Fixes#1748
The original runtime error reporting PR[^1] added a "cc.exception"
module, which allowed coroutine managers (such as parallel) to throw
rich errors, detailing the original context where the error was thrown.
Unfortunately, the change to parallel broke some programs (>_>, don't do
string pattern matching on your errors!), and so had to be reverted,
along with the cc.exception module.
As a minimal replacement for this, we add support for user-thrown
exceptions within our internal code. If an error object "looks" like an
exception ("exception" __name, and a message and thread field), then we
use that as our error information instead.
This is currently undocumented (at least in user-facing documentation),
mostly because I couldn't figure out where to put it - the interface
should remain stable.
[^1]: https://github.com/cc-tweaked/CC-Tweaked/pull/1320
This adds support for computer selectors, in the style of entity
selectors. The long-term goal here is to replace our existing ad-hoc
selectors. However, to aid migration, we currently support both - the
previous one will most likely be removed in MC 1.21.
Computer selectors take the form @c[<key>=<value>,...]. Currently we
support filtering by id, instance id, label, family (as before) and
distance from the player (new!). The code also supports computers within
a bounding box, but there's no parsing support for that yet.
This commit also (finally) documents the /computercraft command. Well,
sort of - it's definitely not my best word, but I couldn't find better
words.
When rendering non-origin monitors, we would fetch the origin monitor,
read its client state, and then cache that on the current monitor to
avoid repeated lookups.
However, if the origin monitor is unloaded/removed on the client, and
then loaded agin, this cache will be not be invalidated, causing us to
render both the old and new monitor!
I think the correct thing to do here is cache the origin monitor. This
allows us to check when the origin monitor has been removed, and
invalidate the cache if needed.
However, I'm wary of any other edge cases here, so for now we do
something much simpler, and remove the cache entirely. This does mean
that monitors now need to perform extra block entity lookups, but the
performance cost doesn't appear to be too bad.
Fixes#1741
Historically, computers tracked whether any world-visible state
(on/off/blinking, label and redstone outputs) had changed with a single
"has changed" flag. While this is simple to use, this has the curious
side effect of that term.setCursorBlink() or os.setComputerLabel() would
cause a block update!
This isn't really a problem in practice - it just means slightly more
block updates. However, the redstone propagation sometimes causes the
computer to invalidate/recheck peripherals, which masks several other
(yet unfixed) bugs.
- colors.toBlit now performs bounds checks on the passed value,
preventing weird behaviour like color.toBlit(2 ^ 16) returning "10".
- The window API now uses colors.toBlit (or rather a copy of it) for
parsing colours, allowing doing silly things like
term.setTextColour(colours.blue + 5).
- Add some top-level documentation to the term API to explain some of
the basics.
Closes#1736
Minecraft sometimes keeps chunks in-memory, but not actively loaded. If
we schedule a block entity to be ticked and that chunk is is then
transitioned to this partially-loaded state, then the block entity is
never actually ticked.
This is most visible with monitors. When a monitor's contents changes,
if the monitor is not already marked as changed, we set it as changed
and schedule a tick (see ServerMonitor). However, if the tick is
dropped, we don't clear the changed flag, meaning subsequent changes
don't requeue the monitor to be ticked, and so the monitor is never
updated.
We fix this by maintaining a list of block entities whose tick was
dropped. If these block entities (or rather their owning chunk) is ever
re-loaded, then we reschedule them to be ticked.
An alternative approach here would be to add the scheduled tick directly
to the LevelChunk. However, getting hold of the LevelChunk for unloaded
blocks is quiet nasty, so I think best avoided.
Fixes#1146. Fixes#1560 - I believe the second one is a duplicate, and
I noticed too late :D.
When we remove a wired node from a network, we need to find connected
components in the rest of the graph. Typically, this requires a
traversal of the whole graph, taking O(|V| + |E|) time.
If we remove a lot of nodes at once (such as when unloading chunks),
this ends up being quadratic in the number of nodes. In some test
networks, this can take anywhere from a few seconds, to hanging the game
indefinitely.
This attempts to reduce the cases where this can happen, with a couple
of optimisations:
- Instead of constructing a new hash set of reachable nodes (requiring
multiple allocations and hash lookups), we store reachability as a
temporary field on the WiredNode.
- We abort our traversal of the graph if we can prove the graph remains
connected after removing the node.
There's definitely future work to be done here in optimising large wired
networks, but this is a good first step.
- Replace usages of WiredNetwork.connect/disconnect/remove with the
WiredNode equivalents.
- Convert "testLarge" into a proper JMH benchmark.
- Don't put a peripheral on every node in the benchmarks. This isn't
entirely representative, and means the peripheral juggling code ends
up dominating the benchmark time.
We've been out-of-date for a while now, as we needed to update
lua_menhir to work with lrgrep 3.
- Better handling of standalone names/expressions - we now correctly
handle lists of names.
- Handle missing commas in tables in a few more places.
- We checked the backing array when reading rather than the file's
length, so could read beyond the end of the file.
- We used the entry length when resizing, which effectively meant we
doubled the size of the backing array on each write.
- cc.require now uses the internal _LOADED table to get the list of
built-in globals. This fixes several globals not showing up on the
list (e.g. utf8), and allows us to inject more modules from the Java
side.
- ILuaAPI now has a getModuleName() function. This is used to inject
the API into the aforementioned _LOADED table, allowing it to be
"require"d.
One common issue we get when a program exits after handling a "key"
event is that it leaves the "char" event on the queue. This means that
the shell (or whatever program we switch in to) then receives the "char"
event, often displaying it to the screen.
Previously we've got around this by doing sleep(0) before exiting the
program. However, we also see this problem in edit's run handler script,
and I'm less comfortable doing the same hack there.
This adds a new internal discard_char function, which will either
wait one tick or return when seeing a char/key_up event.
Fixes#1705
- Mark our core test-fixtures jar as part of the "cctest", rather than
a separate library. I'm fairly sure this was actually using the
classpath version of CC rather than the legacyClasspath version!
- Add a new "testMinecraftLibrary" configuration, instead of trying to
infer it from the classpath. We have to jump through some hoops to
avoid having multiple versions of a library on the classpath at once,
but it's not too bad.
I'm working on a patch to bsl which might allow us to kill of
legacyClasspath instead. Please, anything is better than this.
Forge doesn't run client-side commands from sendUnsignedCommand, so we
still require a mixin there.
We do need to change the command name, as Fabric doesn't properly merge
the two command trees.
I didn't make a new years resolution to stop writing build tooling, but
maybe I should have.
This replaces our use of VanillaGradle with a new project,
VanillaExtract. This offers a couple of useful features for multi-loader
dev, including Parchment and Unpick support, both of which we now use in
CC:T.
- Debug hooks are now correctly called for every function.
- Fix several minor inconsistencies with debug.getinfo.
- Fix Lua tables being sized incorrectly when created from varargs.
- Update FG to 6.0.20 - no major changes, but required for the Gradle
update.
- Update Loom to 1.5.x - this adds Vineflower support by default, so we
can remove loom-vineflower.
This was copied over from the old binary handle, and so states we
always return a single number if no count is given. This is only the
case when the file is opened in binary mode.
Rather than mixing-in to CachedOutput, we just wrap our DataProviders to
use a custom CachedOutput which reformats the JSON before writing. This
allows us to drop mixins for common+non-client code.
Disk drives have had a long-standing issue with mutating their contents
on the computer thread, potentially leading to all sorts of odd bugs.
We tried to fix this by moving setDiskLabel and the mounting code to run
on the main thread. Unfortunately, this means there is a slight delay to
mounts being attached, breaking disk startup.
This commit implements an alternative solution - we now do mounting on
the computer thread again. If the disk's stack is modified, we update it
in the peripheral-facing item, but not the actual inventory. The next
time the disk drive is ticked, we then sync the two items.
This does mean that there is a fraction of a tick where the two will be
out-of-sync. This isn't ideal - it would potentially be possible to
cycle through disk ids - but I don't really think that's avoidable
without significantly complicating the IMedia API.
Fixes#1649, fixes#1686.
Originally we exposed a single registerTurtleUpgradeModellermethod which
could be called from both Fabric (during a mod's client init) and Forge
(during FMLClientSetupEvent).
This was fine until we allowed upgrades to specify model dependencies,
which would then automatically loaded, as this means model loading now
depends on upgrade modellers being loaded. Unknown to me, this is not
guaranteed to be the case on Forge - mod setup happens at the same time
as resource reloading!
Unfortunately there's not really a salvageable way of fixing this with
the current API. Forge now uses a registration event-based system,
meaning we can guarantee all modellers are loaded before models are
baked.
- Add support for version overrides/exclusions in our dependency check.
Sometimes mod loaders use different versions to vanilla, and we need
some way to handle that.
- Rescan wired network connections on the tick after invalidation,
rather than when invalidated.
- Convert some constant lambdas to static method references. Lambdas
don't allocate if they don't capture variables, so this has the same
performance and is a little less ugly.
- Small code-style/formatting changes.
Historically we used Forge's SimpleChannel methods (and
PacketDistributor) to send the packets to the client. However, we don't
need to do that - it is sufficient to convert it to a vanilla packet,
and send the packet ourselves.
Given we need to do this on Fabric, it makes sense to do this on Forge
as well. This allows us to unify (and thus simplify) a lot of how packet
sending works.
At the same time, we also remove the handling of speaker audio during
decoding. We originally did this to avoid the additional copy of audio
data. However, this doesn't work on 1.20.4 (as packets aren't
encoded/decoded on singleplayer), so it makes sense to do this
Correctly(TM).
This also allows us to get rid of ClientNetworkContext.get(). We do
still need to service load this class (as Forge's networking isn't split
up in the same way Fabric's is), but we'll be able to drop that in
1.20.4.
Finally, we move the record playing code from ClientNetworkContext to
ClientPlatformHelper. This means the network context no longer needs to
be platform-specific!
After embarrassing, let's do some proper work.
Rather than passing the level and position each time we call
ComponentAccess.get(), we now pass them at construction time (in the
form of the BE). This makes the consuming code a little cleaner, and is
required for the NeoForge changes in 1.20.4.
I was able to reproduce this by starting two computers, and then warming
up the JIT by running:
while true do os.queueEvent("x") os.pullEvent("x") end
and then running the following on one computer, while typing on the
other:
while true do end
I'm not quite sure why this happens. It's possible that once the JIT is
warm, we can resume computers without actually allocating anything,
though I'm a little unconvinced.
Fixes#1672
Everything old is new again!
CC's network message implementation has gone through several iterations:
- Originally network messages were implemented with a single class,
which held an packet id/type and and opaque blobs of data (as
string/int/byte/NBT arrays), and a big switch statement to decode and
process this data.
- In 42d3901ee3, we split the messages
into different classes all inheriting from NetworkMessage - this bit
we've stuck with ever since.
Each packet had a `getId(): int` method, which returned the
discriminator for this packet.
- However, getId() was only used when registering the packet, not when
sending, and so in ce0685c31f we
removed it, just passing in a constant integer at registration
instead.
- In 53abe5e56e, we made some relatively
minor changes to make the code more multi-loader/split-source
friendly. However, this meant when we finally came to add Fabric
support (8152f19b6e), we had to
re-implement a lot of Forge's network code.
In 1.20.4, Forge moves to a system much closer to Fabric's (and indeed,
Minecraft's own CustomPacketPayload), and so it makes sense to adapt to
that now. As such, we:
- Add a new MessageType interface. This is implemented by the
loader-specific modules, and holds whatever information is needed to
register the packet (e.g. discriminator, reader function).
- Each NetworkMessage now has a type(): MessageType<?> function. This
is used by the Fabric networking code (and for NeoForge's on 1.20.4)
instead of a class lookup.
- NetworkMessages now creates/stores these MessageType<T>s (much like
we'd do for registries), and provides getters for the
clientbound/serverbound messages. Mod initialisers then call these
getters to register packets.
- For Forge, this is relatively unchanged. For Fabric, we now
`FabricPacket`s.
- Clean up option parsing a bit, so it uses the Option, rather than its
corresponding character code.
- Add a new -L/--allow-local-domains flag to remove the $private rule
from the HTTP rules.
While ComputerFamily is still useful, there's definitely some places
where it adds an extra layer of indirection. This commit attempts to
clean up some places where we no longer need it.
- Remove ComputerFamily from AbstractComputerBlock. The only place this
was needed is in TurtleBlock, and that can be replaced with normal
Minecraft explosion resistence!
- Pass in the fuel limit to the turtle block entity, rather than
deriving it from current family.
- The turtle BERs now derive their model from the turtle's item, rather
than the turtle's family.
- When creating upgrade/overlay recipes, use the item's name, rather
than {pocket,turtle}_family. This means we can drop getFamily() from
IComputerItem (it is still needed on to handle the UI).
- We replace IComputerItem.withFamily with a method to change to a
different item of the same type. ComputerUpgradeRecipe no longer
takes a family, and instead just uses the result's item.
- Computer blocks now use the normal Block.asItem() to find their
corresponding item, rather than looking it up via family.
The above means we can remove all the family-based XyzItem.create(...)
methods, which have always felt a little ugly.
We still need ComputerFamily for a couple of things:
- Permission checks for command computers.
- Checks for mouse/colour support in ServerComputer.
- UI textures.
- Add a check to ensure declared dependencies in the :core project, and
those inherited from Minecraft are the same.
- Compute the next Cobalt version, rather than specifying it manually.
- Add the gradle versions plugin (and version catalog update), and
update some versions.
Previously we prevented our published full jar depending on any of the
other projects by excluding the whole cc.tweaked jar. However, as Cobalt
also now lives in that group, this meant we were missing the Cobalt
dependency.
Rather than specifying a wildcard, we now exclude the dependencies when
adding them to the project.
This is a bit of an odd combination of a few bugs:
- When the terminal component is blurred, we fire a mouse_up event for
the last-held button. However, we had an off-by-1 error here, so this
only triggered for the right/middle buttons.
- This was obsucuring the second bug, which is when we clicked within
the terminal, this caused the terminal to be blurred (thus releasing
the mouse) and then focused again.
We fix this by only setting the focus if there's actually a change.
Fixes#1655
When adding/removing observers, we locked on the observer, then
acquired the global lock. When a metric is observed, then we acquire the
global lock and then the observer lock.
If these happen at the same time, we can easily end up with a deadlock.
We simply avoid holding the observer lock for the entire add/remove
process (instead only locking when actually needed).
Closes#1639
This commit adds abstract classes to describe the interface for our
mod-loader-specific generic peripherals (inventories, fluid storage,
item storage).
This offers several advantages:
- Javadoc to illuaminate conversion no longer needs the Forge project
(just core and common).
- Ensures we have a consistent interface between Forge and Fabric.
Note, this does /not/ implement fluid or energy storage for Fabric. We
probably could do fluid without issue, but not something worth doing
right now.
Rather than assuming static methods are generic, and instance methods
are direct, the Generator now has separate entrypoints for handling
instance and generic methods.
As a result of this change, we've also relaxed some of the validation
code. As a result, we now allow calling private/protected methods
which are annotated with @LuaFunction.
This adds a new "java_allocation" metric, which tracks the number of
bytes allocated while executing the computer (as measured by Java). This
is not an 100% reliable number, but hopefully gives some insight into
what computers are doing.
Historically CC has supported two modes when working with file handles
(and HTTP requests):
- Text mode, which reads/write using UTF-8.
- Binary mode, which reads/writes the raw bytes.
However, this can be confusing at times. CC/Lua doesn't actually support
unicode, so any characters beyond the 0.255 range were replaced with
'?'. This meant that most of the time you were better off just using
binary mode.
This commit unifies text and binary mode - we now /always/ read the raw
bytes of the file, rather than converting to/from UTF-8. Binary mode now
only specifies whether handle.read() returns a number (and .write(123)
writes a byte rather than coercing to a string).
- Refactor the entire handle hierarchy. We now have an AbstractMount
base class, which has the concrete implementation of all methods. The
public-facing classes then re-export these methods by annotating
them with @LuaFunction.
These implementations are based on the
Binary{Readable,Writable}Handle classes. The Encoded{..}Handle
versions are now entirely removed.
- As we no longer need to use BufferedReader/BufferedWriter, we can
remove quite a lot of logic in Filesystem to handle wrapping
closeable objects.
- Add a new WritableMount.openFile method, which generalises
openForWrite/openForAppend to accept OpenOptions. This allows us to
support update mode (r+, w+) in fs.open.
- fs.open now uses the new handle types, and supports update (r+, w+)
mode.
- http.request now uses the new readable handle type. We no longer
encode the request body to UTF-8, nor decode the response from UTF-8.
- Websockets now return text frame's contents directly, rather than
converting it from UTF-8. Sending text frames now attempts to treat
the passed string as UTF-8, rather than treating it as latin1.
- Update Cobalt to 0.8.0, switching our Lua version to 5.2(ish).
- Remove our `load` wrapper, as we no longer need to inject _ENV into
the enviroment table.
- Update the parser to handle labels and goto. This doesn't check that
gotos are well formed, but at least means the parser doesn't fall
over on them.
- Update our docs to reflect the changes to Cobalt.
This tries to cover some holes in our existing coverage.
- Port some of our Java readable handle tests to Lua (and also clean up
the Java versions to stop using ObjectWrapper - that dates to
pre-@LuaFunction!)
- Test a couple of discrepancies between binary and text handles. This
is mostly to do with the original number-based .read() and .write()
interface for binary handles.
- Fix a couple of edge cases in file-size accounting.
This is largely based on our existing wiki page. I've pruned out a
couple of entries which I think are largely irrelevant (config file
splitting, Java API changes).
10/10 job by me of changing nothing since 1.13. Shame to break that
streak really.
Does it count as an emulator when it's official? I hope not, as this'd
make it my fourth or fifth emulator at this point.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Developing/debugging CraftOS is a massive pain to do inside Minecraft,
as any change to resources requires a compile+hot swap cycle (and
sometimes a `/reload` in-game). As such, it's often more convenient to
spin up an emulator, pointing it to load the ROM from CC:T's sources.
However, this isn't practical when also making changes to the Java
classes. In this case, we either need to go in-game, or build a custom
version of CCEmuX.
This commit offers an alternative option: we now have our own emulator,
which allows us to hot swap both Lua and Java to our heart's content.
Most of the code here is based on our monitor TBO renderer. We probably
could share some more of this, but there's not really a good place for
it - feels a bit weird just to chuck it in :core.
This is *not* a general-purpose emulator. It's limited in a lot of
ways (won't launch on Mac[^1], no support for multiple computers) - just
stick to what's there already.
[^1]: We require OpenGL 4.5 due to our use of DSA.
In practice, we're never going to change this to true by default. The
old Tekkit Legends pack enabled this[^1], and that caused a lot of
problems, though admittedly back in 2016 so things might be better now.
If people do want this functionality, it should be fairly easy to
replicate with a datapack, adding a file to rom/autorun.
[^1]: See https://www.computercraft.info/forums2/index.php?/topic/27663-
Hate that I remember this, why is this still in my brain?
- Move most error message constants to a new MountHelpers class.
- Be a little more consistent in when we throw "No such file" vs "Not a
file/directory" messages.
The two implementations aren't entirely compatible - the implementation
returned by .of will throw an NPE on .contains(null), whereas the
Collections implementations just return false. However, we try to avoid
passing null to collections methods, so this should be safe.
There's no strong reason to do this, but it helps make the code a little
more consistent
As of 1.20, sign messages are immutable - we need to do
text = text.setMesssage(...) instead. Also do a tiny bit of cleanup to
this function while we're here.
Probably not the best use of my lunch break :D:.
Fixes#1611.
This is an attempt to enforce better separation between ComputerThread
and ComputerExecutor. Both of these classes are pretty complex in their
own right, and the way the two bleed into each other makes it all the
more confusing!
This effectively splits the ComputerExecutor into two separate classes:
- ComputerScheduler.Executor (with the actual implementation inside
ComputerThread): This holds all the ComputerThread-related logic
which used to be in ComputerExecutor, including:
- before/after work hooks
- is-on-thread tracking
- virtual runtime computation
- ComputerScheduler.Worker: This encapsulates all the computer-related
behaviour. The actual implementation remains in ComputerExecutor.
The boundaries are still a little fuzzy here, and it's all definitely
more coupled then I'd like, but still an improvement!
There are several additional changes at the same time:
- TimeoutState has also been split up, to better define the boundary
between consumers (such as ComputerExecutor and ILuaMachine) and
controllers (ComputerThread).
The getters still live in TimeoutState, but the core logic lives in
ManagedTimeoutState.
- We no longer track cumulative time in the TimeoutState. Instead, we
allow varying the timeout of a computer. When a computer is paused,
we store the remaining time, and restore it when resuming again.
This also allows us give a longer timeout for computer
startup/shutdown, hopefully avoiding some of those class-not-found
issues we've seen.
- We try to make the state machine of how ComputerExecutors live on the
queue a little more explicit. This is very messy/confusing -
something I want to property test in the future.
I'm sure there's more to be done here, especially in ComputerExecutor,
but hopefully this makes future changes a little less intimidating.
Allows registering arbitrary block lookup functions instead of a
platform-specific capability. This is roughly what Fabric did before,
but generalised to also take an invalidation callback.
This callback is a little nasty - it needs to be a NonNullableConsumer
on Forge, but that class isn't available on Fabric. For now, we make the
lookup function (and thus the generic peripheral provider) generic on
some <T extends Runnable> type, then specialise that on the Forge side.
Hopefully we can clean this up when NeoForge reworks capabilities.
This is the second time I've rewritten our class generation in a little
over a month. Oh dear!
Back in d562a051c7 we started using method
handles inside our generated ASM, effectively replacing a direct call
with .invokeExact on a constant method handle.
This goes one step further and removes our ASM entirely, building up a
MethodHandle that checks arguments and then wraps the return value.
Rather than generating a class, we just return a new LuaFunction
instance that invokeExacts the method handle.
This is definitely slower than what we had before, but in the order of
8ns vs 12ns (in the worst case, sometimes they're much more comparable),
so I'm not too worried in practice.
However, generation of the actual method is now a bit faster. I've not
done any proper benchmarking, but it's about 20-30% faster.
This also gives us a bit more flexibility in the future, for instance
uisng bound MethodHandles in generation (e.g. for instance methods on
GenericSources). Not something I'm planning on doing right now, but is
an option.
Or rather, being published to the wrong place. The java-convention
plugin sets the group, but that was applied after the publishing one - I
was hoping it'd read that property lazy, but clearly not!
Wow, some of this is /old/. All the Maps.newHashMap stuff dates back to
Java 6, so must originally be CCTweaks code?!
We're unlikely to drop our Guava dependency (we use too much other
stuff), but we should make the most of the stdlib where possible.
This should be significantly faster than LoadingCache (2.5x in my
benchmarks, but not sure they're representative). This isn't super
important - a lookup only takes 6us - but still worth using!
Previously we had the invariant that if we had a server monitor, we also
had a terminal. When a monitor shrank into a place, we deleted the
monitor, and then recreated it when a peripheral was requested.
As of ab785a0906 this has changed
slightly, and we now just delete the terminal (keeping the ServerMonitor
around). However, we didn't adjust the peripheral code accordingly,
meaning we didn't recreate the /terminal/ when a peripheral was
requested.
The fix for this is very simple - most of the rest of this commit is
some additional code for ensuring monitor invariants hold, so we can
write tests with a little more confidence.
I'm not 100% sold on this approach. It's tricky having a double layer of
nullable state (ServerMonitor, and then the terminal). However, I think
this is reasonable - the ServerMonitor is a reference to the multiblock,
and the Terminal is part of the multiblock's state.
Even after all the refactors, monitor code is still nastier than I'd
like :/.
Fixes#1608
Currently redirects would be returned from the proxy, and then
immediately followed by XMLHTTPRequest. The proxy now follows requests
(when requested), so that should no longer happen.
We should probably switch over to fetch(...) here, to allow setting
follow_redirects to false, but that's a job for another day.
Haha, so many web emulator related commits of late. This'll die down
soon.
We can't use FriendlyByte.readCollection to read to a
pre-allocated/array-backed NonNullList, as that doesn't implement
List.add. Instead, we just need to do a normal loop.
We add a couple of tests to round-trip our recipe specs. Unfortunately
we can't test the recipes themselves as our own registries aren't set
up, so this'll have to do for now.
- Update to Rollup 4.x
- Replace terser and postcss with swc and lightningcss. This is
definitely more code for us to write (maybe I should turn them into
proper plugins we can depend on), but both speedier and fewer
dependencies.
- Drop dependency on glob - we can get away with fs.readdir for what we
needed it for.
Oh, this was a really nasty bug to reproduce. I'm not sure why - it's
very simple - I guess I've only just seen screenshots of it, and never
sat down to try myself. Reminder to actually report your bugs folks!
In this case:
1. Place down three down three monitors and then a computer.
2. Display something on the monitor (monitor left paint a) is my go-to.
3. Break the middle monitor.
We'd expect the left most monitor to be cleared, however it actually
preserves the monitor contents, resizing (and skewing it) to fit on its
new size!
This is because we clear the server monitor, but never sync that over to
the client, so the client monitor retains the old contents. To fix that,
instead of nulling out the server monitor, we null out the underlying
Terminal. This causes the change to be synced, fixing the bug.
Paint implements its menu slightly differently to edit, in that it takes
control of the event loop until the menu is closed. This means that the
term_resize event is ignored, and so the canvas not redrawn when the
menu is open.
Historically we've used copy-cat to provide a web-based emulator for
running example code on our documentation site. However, copy-cat is
often out-of-date with CC:T, which means example snippets fail when you
try to run them!
This commit vendors in copy-cat (or rather an updated version of it)
into CC:T itself, allowing us to ensure the emulator is always in sync
with the mod.
While the ARCHITECTURE.md documentation goes into a little bit more
detail here, the general implementation is as follows
- In project/src/main we implement the core of the emulator. This
includes a basic reimplementation of some of CC's classes to work on
the web (mostly the HTTP API and ComputerThread), and some additional
code to expose the computers to Javascript.
- This is all then compiled to Javascript using [TeaVM][1] (we actually
use a [personal fork of it][2] as there's a couple of changes I've
not upstreamed yet).
- The Javascript side then pulls in the these compiled classes (and
the CC ROM) and hooks them up to [cc-web-term][3] to display the
actual computer.
- As we're no longer pulling in copy-cat, we can simplify our bundling
system a little - we now just compile to ESM modules directly.
[1]: https://github.com/konsoletyper/teavm
[2]: https://github.com/SquidDev/teavm/tree/squid-patches
[3]: https://github.com/squiddev-cc/cc-web-term
Had an issue last week where testFixtures had a couple of issues which I
didn't pick up on, as the pre-commit hooks only check the main and test
source set.
We now add a per-project "checkstyle" task, which dependes on the
per-source-set checkstyle tasks.
This moves MemoryMount to the main core module, and converts it to be a
"proper" WritableMount. It's still naively implemented - definitely
would be good to flesh out our tests in the future - but enough for what
we need it for.
We also do the following:
- Remove the FileEntry.path variable, and instead pass the path around
as a variable.
- Clean up BinaryReadableHandle to use ByteBuffers in a more idiomatic
way.
- Add a couple more tests to our FS tests. These are in a bit of an odd
place, where we want both Lua tests (for emulator compliance) and
Java tests (for testing different implementations) - something to
think about in the future.
- Move the frontend code into src/frontend
- Move our custom element SSR system into src/htmlTransform.
This is mostly in prep for merging in copy-cat's core, as that's a whole
bunch of extra code.
This attempts to reduce some duplication in recipe serialisation (and
deserialisation) by moving the structure of a recipe (group, category,
ingredients, result) into seprate types.
- Add ShapedRecipeSpec and ShapelessRecipeSpec, which store the core
properties of shaped and shapeless recipes. There's a couple of
additional classes here for handling some of the other shared or
complex logic.
- These classes are now used by two new Custom{Shaped,Shapeless}Recipe
classes, which are (mostly) equivalent to Minecraft's
shaped/shapeless recipes, just with support for nbt in results.
- All the other similar recipes now inherit from these base classes,
which allows us to reuse a lot of this serialisation code. Alas, the
total code size has still gone up - maybe there's too much
abstraction here :).
- Mostly unrelated, but fix the skull recipes using the wrong UUID
format.
This allows us to remove our mixin for nbt in recipes (as we just use
our custom recipe now) and simplify serialisation a bit - hopefully
making the switch to codecs a little easier.
- Add AbstractInMemoryMount, which contains all of ArchiveMount's file
tree logic, but not the caching functionality.
- Convert MemoryMount to inherit from AbstractInMemoryMount.
- Add a helper method to add a file to an AbstractInMemoryMount, and
use that within {Resource,Jar}Mount.
There's definitely more work to be done here - it might be nice to split
FileEntry into separate Directory and File interfaces, or at least make
them slightly more immutable, but that's definitely a future job.
- Add a new WebsocketClient interface, which WebsocketHandle uses for
sending messages and closing. This reduces coupling between Websocket
and WebsocketHandle, which is nice, though admitedly only use for
copy-cat :).
- WebsocketHandle now uses Websocket(Client).isClosed(), rather than
tracking the closed state itself - this makes the class mostly a thin
Lua wrapper over the client, which is nice.
- Convert Options into a record.
- Clarify the behaviour of ws.close() and the websocket_closed event.
Our previous test was incorrect as it called WebsocketHandle.close
(rather than WebsocketHandle.doClose), which had slightly different
semantics in whether the event is queued.
We already use preact for the copy-cat integration, so it makes sense to
use it during the static pass too. This allows us to drop a dependency
on react.
description:What version of Minecraft are you using?
description:|
What version of Minecraft are you using? If your version is not listed, please try to reproduce on one of the supported versions.
options:
- 1.16.x
- 1.18.x
- 1.19.x
- 1.20.x
- 1.20.1
- 1.21.1
- 1.21.7
validations:
required:true
- type:input
@@ -28,8 +29,7 @@ body:
label:Details
description:|
Description of the bug. Please include the following:
- Logs: These will be located in the `logs/` directory of your Minecraft
instance. Please upload them as a gist or directly into this editor.
- Detailed reproduction steps: sometimes I can spot a bug pretty easily,
but often it's much more obscure. The more information I have to help
reproduce it, the quicker it'll get fixed.
- Logs: These will be located in the `logs/` directory of your Minecraft instance. This is always useful, even if it doesn't include errors, so please upload this!
- Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed.

[][CurseForge]
[][Modrinth]
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of
new features.
CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It runs on both [Minecraft Forge] and [Fabric].
CC: Tweaked can be installed from [Modrinth]. It runs on both [Minecraft Forge] and [Fabric].
## Contributing
Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you want to get started
@@ -26,8 +25,9 @@ developing the mod, [check out the instructions here](CONTRIBUTING.md#developing
## Community
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
ComputerCraft, do check out our [forum] and [GitHub discussions page][GitHub discussions]! There's also a fairly
populated, albeit quiet [IRC channel][irc], if that's more your cup of tea.
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
desktop client, or online using [KiwiIRC].
We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
// See https://github.com/JetBrains/intellij-community/blob/9ba394021dc73a3926f13d6d6cdf434f9ee7046d/plugins/junit/src/com/intellij/execution/junit/JUnitRunConfigurationImporter.kt#L39
CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However, Cobalt and CC:T implement additional features from Lua 5.2 and 5.3 (as well as some deprecated 5.0 features) that are not available in base 5.1. This page lists all of the compatibility for these newer versions.
CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.2. However, Cobalt and CC:T implement additional
features from Lua 5.2 and 5.3 (as well as some deprecated 5.0 and 5.1 features). This page lists all of the
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.