1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-17 23:17:38 +00:00

Compare commits

...

77 Commits

Author SHA1 Message Date
Jonathan Coates
b343c01216 Bump CC:T to 1.109.0 2023-11-15 09:39:52 +00:00
Jonathan Coates
76968f2f28 Track allocations while executing computers
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.
2023-11-09 18:36:35 +00:00
Jonathan Coates
1d365f5a0b Add option to allow repetition in JSON serialiser
Closes #1588
2023-11-08 21:29:43 +00:00
Jonathan Coates
7b240cbf7e Merge pull request #1615 from cc-tweaked/feature/much-breakage-very-wow
Remove text mode, update Cobalt
2023-11-08 20:05:49 +00:00
Jonathan Coates
d272a327c7 Update CraftOS version to 1.9 2023-11-08 19:40:14 +00:00
Jonathan Coates
0c0556a5bc Always use raw bytes in file handles
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.
2023-11-08 19:40:14 +00:00
Jonathan Coates
87345c6b2e Add pasting support to the standalone emulator
- Move paste normalisation code to StringUtil, so it can be shared by
   emulators.
 - Add paste support to the emulator.
2023-11-08 19:40:14 +00:00
Jonathan Coates
784e623776 Update Cobalt to 0.8.0
- 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.
2023-11-08 18:42:17 +00:00
Jonathan Coates
bcb3e9bd53 Bump CC:T to 1.108.4 2023-10-29 12:02:11 +00:00
Jonathan Coates
c30bffbd0f Add additional tests for filesystems/mounts
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.
2023-10-29 12:01:26 +00:00
Jonathan Coates
91c41856c5 Add an "Incompatibilities between versions" page
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.
2023-10-28 20:00:56 +01:00
Jonathan Coates
18c9723308 Add a standalone CC:T UI
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.
2023-10-28 17:58:11 +01:00
Jonathan Coates
aee382ed70 Replace Fabric JUnit hacks with fabric-loader-junit
Also configure some of our common JUnit run configurations via Gradle -
I end up setting these up in every worktree anyway - so let's just do it
once.
2023-10-26 22:06:40 +01:00
Jonathan Coates
6656da5877 Remove disable_lua51_features config option
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?
2023-10-25 08:59:55 +01:00
Jonathan Coates
09e521727f Make mount error messages a bit more consistent
- 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.
2023-10-22 13:13:07 +01:00
Jonathan Coates
cab66a2d6e Replace Collections methods with {List,Map,Set}.of
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
2023-10-21 10:37:43 +01:00
Jonathan Coates
8eabd4f303 Fix signs being empty when placed
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.
2023-10-20 13:32:38 +01:00
Jonathan Coates
e3ced84885 Merge branch 'feature/split-computer-thread' into mc-1.20.x 2023-10-19 22:54:56 +01:00
Jonathan Coates
0929ab577d Split ComputerThread/ComputerExecutor up a little
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.
2023-10-19 22:50:11 +01:00
Jonathan Coates
2228733abc Move ComputerThread to its own package
This is entirely broken - we rely a lot on package locals right now -
but makes the next commit a little cleaner.
2023-10-19 18:31:02 +01:00
Jonathan Coates
e67c94d1bd Fix a couple of future deprecations in Gradle 2023-10-19 18:28:15 +01:00
Jonathan Coates
ae5a661a47 Add a discarding MetricsObserver
This is useful for test code where we don't care about the metrics
gathered.
2023-10-19 18:27:58 +01:00
Jonathan Coates
0ff58cdc3e Unify the generic peirpheral system a litte
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.
2023-10-17 21:59:16 +01:00
Jonathan Coates
1747c74770 Relicense some translations
One issue with Weblate is tracking down the provenance of who has
touched what file. I'm fairly sure this is accurate though.
2023-10-16 22:12:41 +01:00
Jonathan Coates
71669cf49c Replace ASM generation with MethodHandles
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.
2023-10-11 20:05:37 +01:00
Jonathan Coates
bd327e37eb Fix common jar not actually being published
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!
2023-10-11 19:15:36 +01:00
Jonathan Coates
bdce9a8170 Replace several Guava classes with Java stdlib
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.
2023-10-11 09:59:55 +01:00
Jonathan Coates
7e5598d084 Use ClassValue instead of LoadingCache
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!
2023-10-11 09:30:29 +01:00
Jonathan Coates
440fca6535 Relicense a couple of more files
We've got in touch with Brady, so can now do the last of the docs \o/!

Also pick up a couple of stragglers that I'd missed from before.
2023-10-11 08:00:07 +01:00
Weblate
6635edd35c Translations for French
Translations for German

Translations for German

Co-authored-by: Sammy <SammyKoch@pm.me>
Co-authored-by: SquidDev <git@squiddev.cc>
2023-10-09 22:33:03 +00:00
Jonathan Coates
93ad40efbb Ensure the terminal exists when creating a monitor peripheral
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
2023-10-09 22:09:01 +01:00
Jonathan Coates
27dc8b5b2c Pass follow_redirects flag to the CORS proxy
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.
2023-10-08 20:12:47 +01:00
Jonathan Coates
3ebdf7ef5e Bump CC:T to 1.108.3 2023-10-08 15:25:45 +01:00
Jonathan Coates
905d4cb091 Fix crash when joining a dedicated server
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.
2023-10-08 15:22:32 +01:00
Jonathan Coates
e7ab05d064 Bump CC:T to 1.108.2 2023-10-08 13:27:24 +01:00
Jonathan Coates
6ec34b42e5 Small cleanup to our web build scripts
- 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.
2023-10-08 13:14:02 +01:00
Jonathan Coates
ab785a0906 Fix monitors being warped after a resize
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.
2023-10-03 18:20:44 +01:00
Jonathan Coates
4541decd40 Fix canvas not always being redrawn on term resize
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.
2023-10-03 18:18:33 +01:00
Spongecade
747a5a53b4 Update Minecraft wiki links to new domain (#1601) 2023-10-03 15:55:20 +00:00
Jonathan Coates
c0643fadca Build a web-based emulator for the documentation site (#1597)
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
2023-10-03 09:19:19 +01:00
Jonathan Coates
0a31de43c2 Run checkstyle on all source sets
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.
2023-10-03 09:06:17 +01:00
Jonathan Coates
96b6947ef2 Flesh out MemoryMount into a writable mount
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.
2023-09-29 22:15:23 +01:00
Jonathan Coates
e7a1065bfc Move file transfer API to the code library
This is useful for emulators, which might want to emulate the event.
2023-09-29 21:09:23 +01:00
Jonathan Coates
663eecff0c Relocate our existing web code to subdirectories
- 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.
2023-09-28 21:00:07 +01:00
Jonathan Coates
e6125bcf60 Try to make recipe serialisers more reusable
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.
2023-09-23 18:24:02 +01:00
Jonathan Coates
0d6c6e7ae7 Hoist some ArchiveMount logic into a new superclass
- 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.
2023-09-22 07:46:39 +01:00
Jonathan Coates
ae71eb3cae Reduce coupling in websocket code
- 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.
2023-09-21 18:59:15 +01:00
Jonathan Coates
3188197447 Use Preact for static rendering of components
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.
2023-09-20 22:09:58 +01:00
Jonathan Coates
6c8b391dab Some web tooling changes
- Switch to tsx from ts-node, fixing issues on Node 20
 - Update rehype
2023-09-18 17:15:03 +01:00
Jonathan Coates
b1248e4901 Add a tag for blocks wired modems should ignore
Includes wired modems (as before), but can be extended by other mods if
needed.
2023-09-11 21:29:17 +01:00
Jonathan Coates
56d97630e8 Bump CC:T to 1.108.1 2023-09-06 09:51:16 +01:00
Jonathan Coates
e660192f08 Make command computer permission checks stricter
- Placing a command computer requires the player to be in creative and
   opped.
 - Breaking a command computer now requires the player to be opped, as
   well as in creative.

As we've now got a dedicated item class for command comptuers, we move
the command-specific IMedia override to that class.

Fixes #1582.
2023-09-05 18:44:16 +01:00
Jonathan Coates
4e82bd352d Bump the priority of the computer thread
As this is responsible for interrupting computers, we should make sure
its priority is higher than the background threads. It spends most of
its time sleeping, so should be fine.
2023-09-05 18:39:55 +01:00
Jonathan Coates
07113c3e9b Move command actions to their own methods
Rather than having a mess of lambdas, we now move the bulk of the
implemetation to their own methods. The lambdas now just do argument
extraction - it's all stringly typed, so good to keep that with the
argument definition.

This also removes a couple of exception keys (and thus their translation
keys) as we no longer use them.
2023-09-05 18:37:10 +01:00
Jonathan Coates
d562a051c7 Use method handlees in our generated Lua methods (#1579)
When the target method is in a different class loader to CC, our
generated method fails, as it cannot find the target class. To get
around that, we create a MethodHandle to the target method, and then
inject that into the generated class (with Java's new dynamic constant
system). We can then invoke the MethodHandle in our generated code,
avoiding any references to the target class/method.
2023-09-03 16:12:37 +00:00
Jonathan Coates
6ac09742fc Fix errors from the typescript bump
Looks like ./gradlew docWebsite didn't rebuild here.
2023-08-31 20:49:53 +01:00
Jonathan Coates
5dd6b9a637 Generic dependency update
A couple of changes caused by checkstyle being a little more strict.
2023-08-31 19:14:37 +01:00
Jonathan Coates
8fb1dd346c Merge branch 'mc-1.19.x' into mc-1.20.x 2023-08-28 12:25:32 +01:00
Jonathan Coates
0f6ea3deaf Add back MoreRed support
I removed this in aa0d544bba, way back in
late 2021. 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 new capability
helpers, but otherwise it's almost exactly the same :D.
2023-08-28 00:04:46 +01:00
Jonathan Coates
13ed422bd5 Remove getBundledRedstoneConnectivity
Wow, this is old. It looks like it's a legacy of when this method was on
TileGeneric (and so returned false by default). As all implementations
now return true (turtle tools no longer block redstone), we don't really
need this any more.
2023-08-27 23:54:27 +01:00
Jonathan Coates
5b58271b92 Fix computer sidebar buttons being invisible 2023-08-27 19:25:04 +01:00
Jonathan Coates
4e42394f33 Merge branch 'mc-1.19.x' into mc-1.20.x 2023-08-27 19:08:59 +01:00
Jonathan Coates
3371c4651c Merge branch 'mc-1.19.x' into mc-1.20.x 2023-08-13 08:38:13 +01:00
Jonathan Coates
e6bc1e4e27 Merge branch 'mc-1.19.x' into mc-1.20.x 2023-08-05 10:36:37 +01:00
Jonathan Coates
ae50f900af Update library versions
Everything has updated to 1.20.1 now, so we can start depending on
things again :).
2023-07-10 20:50:32 +01:00
Jonathan Coates
48889ceb89 Merge branch 'mc-1.19.x' into mc-1.20.x 2023-07-10 20:38:25 +01:00
Jonathan Coates
8f1bf4341c Merge branch 'mc-1.19.x' into mc-1.20.x 2023-07-08 09:38:17 +01:00
Jonathan Coates
9a48b53a83 Merge branch 'mc-1.19.x' into mc-1.20.x 2023-07-07 18:10:04 +01:00
Jonathan Coates
9519448e43 Don't render toast text with a shadow 2023-07-07 18:08:47 +01:00
Jonathan Coates
915b6f9d81 Switch away from Forge's loot modifiers
We switched to Forge's loot modifier system in the 1.20 update, as
LootTable.addPool had been removed. Turns out this was by accident, and
so we switch back to the previous implementation, as it's much simpler
and efficient.
2023-07-07 10:18:16 +01:00
Jonathan Coates
a98f3b2a4c Merge branch 'mc-1.19.x' into mc-1.20.x 2023-07-07 00:18:50 +01:00
Jonathan Coates
ebaf49508f Merge branch 'mc-1.19.x' into mc-1.20.x 2023-06-20 08:59:06 +01:00
Jonathan Coates
c45fc94752 Remove support for clicking in off-hand pocket computers
This was broken/commented out already as part of the 1.20 update. I
don't think this is really going to be easy to add back without a lot of
reflection, so going to remove this feature instead.

Closes #1471.
2023-06-17 11:44:51 +01:00
Jonathan Coates
fd1f6dda32 Allow using printed books in chiseled bookshelves 2023-06-14 20:42:58 +01:00
Jonathan Coates
5d6389dc50 Update to Minecraft 1.20.1 2023-06-13 18:07:07 +01:00
Jonathan Coates
1ece2aa23b Merge branch 'mc-1.19.x' into mc-1.20.x 2023-06-10 09:07:41 +01:00
Jonathan Coates
ff1e5f6823 Update to 1.20
- Use GuiGraphics for rendering UI elements. Almost definitely some
   z-fighting issues slipped in here.

 - Use Forge's loot modifier system for handling treasure disks. I have
   mixed feelings about this - it's a nice system, but also is far less
   efficient than the previous approach.

 - Regenerate data. This is the brunt of the commit, but nothing
   especially interesting here.
2023-06-08 09:52:00 +01:00
627 changed files with 13414 additions and 6936 deletions

View File

@@ -44,7 +44,7 @@ repos:
name: Check Java codestyle
files: ".*\\.java$"
language: system
entry: ./gradlew checkstyleMain checkstyleTest
entry: ./gradlew checkstyle
pass_filenames: false
require_serial: true
- id: illuaminate

View File

@@ -10,8 +10,8 @@ Files:
projects/common/src/testMod/resources/data/cctest/structures/*
projects/fabric/src/generated/*
projects/forge/src/generated/*
projects/web/src/export/index.json
projects/web/src/export/items/minecraft/*
projects/web/src/htmlTransform/export/index.json
projects/web/src/htmlTransform/export/items/minecraft/*
Comment: Generated/data files are CC0.
Copyright: The CC: Tweaked Developers
License: CC0-1.0
@@ -37,10 +37,10 @@ Files:
projects/fabric/src/testMod/resources/computercraft-gametest.fabric.mixins.json
projects/fabric/src/testMod/resources/fabric.mod.json
projects/forge/src/client/resources/computercraft-client.forge.mixins.json
projects/web/src/mount/.settings
projects/web/src/mount/example.nfp
projects/web/src/mount/example.nft
projects/web/src/mount/expr_template.lua
projects/web/src/frontend/mount/.settings
projects/web/src/frontend/mount/example.nfp
projects/web/src/frontend/mount/example.nft
projects/web/src/frontend/mount/expr_template.lua
projects/web/tsconfig.json
Comment: Several assets where it's inconvenient to create a .license file.
Copyright: The CC: Tweaked Developers
@@ -53,19 +53,32 @@ Files:
projects/common/src/main/resources/assets/computercraft/textures/*
projects/common/src/main/resources/pack.mcmeta
projects/common/src/main/resources/pack.png
projects/core/src/main/resources/assets/computercraft/textures/gui/term_font.png
projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
projects/core/src/main/resources/data/computercraft/lua/rom/help/*
projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
projects/web/src/export/items/computercraft/*
projects/web/src/htmlTransform/export/items/computercraft/*
Comment: Bulk-license original assets as CCPL.
Copyright: 2011 Daniel Ratcliffe
License: LicenseRef-CCPL
Files:
projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json
projects/common/src/main/resources/assets/computercraft/lang/ko_kr.json
projects/common/src/main/resources/assets/computercraft/lang/pl_pl.json
projects/common/src/main/resources/assets/computercraft/lang/pt_br.json
projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json
projects/common/src/main/resources/assets/computercraft/lang/uk_ua.json
projects/common/src/main/resources/assets/computercraft/lang/zh_cn.json
Comment: Community-contributed license files
Copyright: 2017 The CC: Tweaked Developers
License: LicenseRef-CCPL
Files:
projects/common/src/main/resources/assets/computercraft/lang/*
Comment: Community-contributed license files
Copyright: 2017 The CC: Tweaked Developers
License: LicenseRef-CCPL
License: MPL-2.0
Files:
.github/*

View File

@@ -100,7 +100,6 @@ about how you can build on that until you've covered everything!
[new-issue]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose "Create a new issue"
[community]: README.md#community "Get in touch with the community."
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
[checkstyle]: https://checkstyle.org/
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
[docs]: https://tweaked.cc/ "CC: Tweaked documentation"

View File

@@ -2,7 +2,11 @@
//
// SPDX-License-Identifier: MPL-2.0
import cc.tweaked.gradle.JUnitExt
import net.fabricmc.loom.api.LoomGradleExtensionAPI
import net.fabricmc.loom.util.gradle.SourceSetHelper
import org.jetbrains.gradle.ext.compiler
import org.jetbrains.gradle.ext.runConfigurations
import org.jetbrains.gradle.ext.settings
plugins {
@@ -38,6 +42,50 @@ githubRelease {
tasks.publish { dependsOn(tasks.githubRelease) }
idea.project.settings.runConfigurations {
register<JUnitExt>("Core Tests") {
vmParameters = "-ea"
moduleName = "${idea.project.name}.core.test"
packageName = ""
}
register<JUnitExt>("CraftOS Tests") {
vmParameters = "-ea"
moduleName = "${idea.project.name}.core.test"
className = "dan200.computercraft.core.ComputerTestDelegate"
}
register<JUnitExt>("CraftOS Tests (Fast)") {
vmParameters = "-ea -Dcc.skip_keywords=slow"
moduleName = "${idea.project.name}.core.test"
className = "dan200.computercraft.core.ComputerTestDelegate"
}
register<JUnitExt>("Common Tests") {
vmParameters = "-ea"
moduleName = "${idea.project.name}.common.test"
packageName = ""
}
register<JUnitExt>("Fabric Tests") {
val fabricProject = evaluationDependsOn(":fabric")
val classPathGroup = fabricProject.extensions.getByType<LoomGradleExtensionAPI>().mods
.joinToString(File.pathSeparator + File.pathSeparator) { modSettings ->
SourceSetHelper.getClasspath(modSettings, project).joinToString(File.pathSeparator) { it.absolutePath }
}
vmParameters = "-ea -Dfabric.classPathGroups=$classPathGroup"
moduleName = "${idea.project.name}.fabric.test"
packageName = ""
}
register<JUnitExt>("Forge Tests") {
vmParameters = "-ea"
moduleName = "${idea.project.name}.forge.test"
packageName = ""
}
}
idea.project.settings.compiler.javac {
// We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings
// and errors. Loop through our source sets and find the appropriate flags.

View File

@@ -50,10 +50,11 @@ dependencies {
implementation(libs.curseForgeGradle)
implementation(libs.fabric.loom)
implementation(libs.forgeGradle)
implementation(libs.ideaExt)
implementation(libs.librarian)
implementation(libs.minotaur)
implementation(libs.quiltflower)
implementation(libs.vanillaGradle)
implementation(libs.vineflower)
}
gradlePlugin {

View File

@@ -12,7 +12,7 @@ import cc.tweaked.gradle.MinecraftConfigurations
plugins {
`java-library`
id("fabric-loom")
id("io.github.juuxel.loom-quiltflower")
id("io.github.juuxel.loom-vineflower")
id("cc-tweaked.java-convention")
}

View File

@@ -57,14 +57,15 @@ repositories {
filter {
includeGroup("cc.tweaked")
includeModule("org.squiddev", "Cobalt")
// Things we mirror
includeGroup("commoble.morered")
includeGroup("dev.architectury")
includeGroup("dev.emi")
includeGroup("maven.modrinth")
includeGroup("me.shedaniel.cloth")
includeGroup("me.shedaniel")
includeGroup("mezz.jei")
includeGroup("org.teavm")
includeModule("com.terraformersmc", "modmenu")
includeModule("me.lucko", "fabric-permissions-api")
}
@@ -98,7 +99,10 @@ sourceSets.all {
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
check("NullAway", CheckSeverity.ERROR)
option("NullAway:AnnotatedPackages", listOf("dan200.computercraft", "net.fabricmc.fabric.api").joinToString(","))
option(
"NullAway:AnnotatedPackages",
listOf("dan200.computercraft", "cc.tweaked", "net.fabricmc.fabric.api").joinToString(","),
)
option("NullAway:ExcludedFieldAnnotations", listOf("org.spongepowered.asm.mixin.Shadow").joinToString(","))
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
option("NullAway:CheckOptionalEmptiness")
@@ -173,6 +177,12 @@ project.plugins.withType(CCTweakedPlugin::class.java) {
}
}
tasks.register("checkstyle") {
description = "Run Checkstyle on all sources"
group = LifecycleBasePlugin.VERIFICATION_GROUP
dependsOn(tasks.withType(Checkstyle::class.java))
}
spotless {
encoding = StandardCharsets.UTF_8
lineEndings = LineEnding.UNIX
@@ -190,6 +200,7 @@ spotless {
val ktlintConfig = mapOf(
"ktlint_standard_no-wildcard-imports" to "disabled",
"ktlint_standard_class-naming" to "disabled",
"ij_kotlin_allow_trailing_comma" to "true",
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
)

View File

@@ -173,7 +173,7 @@ abstract class CCTweakedExtension(
}
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}")
val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
val reportTaskName = "jacoco${task.name.capitalized()}Report"
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
@@ -185,7 +185,7 @@ abstract class CCTweakedExtension(
jacoco.applyTo(this)
extensions.configure(JacocoTaskExtension::class.java) {
includes = listOf("dan200.computercraft.*")
classDumpDir = classDump
classDumpDir = classDump.get().asFile
// Older versions of modlauncher don't include a protection domain (and thus no code
// source). Jacoco skips such classes by default, so we need to explicitly include them.

View File

@@ -9,6 +9,10 @@ import org.gradle.api.Project
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.plugins.ide.idea.model.IdeaModel
import org.jetbrains.gradle.ext.IdeaExtPlugin
import org.jetbrains.gradle.ext.runConfigurations
import org.jetbrains.gradle.ext.settings
/**
* Configures projects to match a shared configuration.
@@ -21,6 +25,20 @@ class CCTweakedPlugin : Plugin<Project> {
val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets
cct.sourceDirectories.add(SourceSetReference.internal(sourceSets.getByName("main")))
}
project.plugins.withType(IdeaExtPlugin::class.java) { extendIdea(project) }
}
/**
* Extend the [IdeaExtPlugin] plugin's `runConfiguration` container to also support [JUnitExt].
*/
private fun extendIdea(project: Project) {
val ideaModel = project.extensions.findByName("idea") as IdeaModel? ?: return
val ideaProject = ideaModel.project ?: return
ideaProject.settings.runConfigurations {
registerFactory(JUnitExt::class.java) { name -> project.objects.newInstance(JUnitExt::class.java, name) }
}
}
companion object {

View File

@@ -4,6 +4,7 @@
package cc.tweaked.gradle
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.AbstractExecTask
import org.gradle.api.tasks.OutputDirectory
@@ -11,5 +12,5 @@ import java.io.File
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
@get:OutputDirectory
abstract val output: Property<File>
abstract val output: DirectoryProperty
}

View File

@@ -5,6 +5,8 @@
package cc.tweaked.gradle
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.file.FileSystemLocation
import org.gradle.api.file.FileSystemLocationProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.JavaExec
@@ -124,3 +126,6 @@ class CloseScope : AutoCloseable {
/** Proxy method to avoid overload ambiguity. */
fun <T> Property<T>.setProvider(provider: Provider<out T>) = set(provider)
/** Short-cut method to get the absolute path of a [FileSystemLocation] provider. */
fun Provider<out FileSystemLocation>.getAbsolutePath(): String = get().asFile.absolutePath

View File

@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package cc.tweaked.gradle
import org.jetbrains.gradle.ext.JUnit
import javax.inject.Inject
/**
* A version of [JUnit] with a functional [className].
*
* See [#92](https://github.com/JetBrains/gradle-idea-ext-plugin/issues/92).
*/
open class JUnitExt @Inject constructor(nameParam: String) : JUnit(nameParam) {
override fun toMap(): MutableMap<String, *> {
val map = HashMap(super.toMap())
// Should be "class" instead of "className".
// See https://github.com/JetBrains/intellij-community/blob/9ba394021dc73a3926f13d6d6cdf434f9ee7046d/plugins/junit/src/com/intellij/execution/junit/JUnitRunConfigurationImporter.kt#L39
map["class"] = className
return map
}
}

View File

@@ -58,7 +58,7 @@ abstract class ClientJavaExec : JavaExec() {
if (!clientDebug) systemProperty("cctest.client", "")
if (renderdoc) environment("LD_PRELOAD", "/usr/lib/librenderdoc.so")
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
workingDir(project.buildDir.resolve("gametest").resolve(name))
workingDir(project.layout.buildDirectory.dir("gametest/$name"))
}
init {

View File

@@ -112,7 +112,9 @@ SPDX-License-Identifier: MPL-2.0
<module name="LambdaParameterName" />
<module name="LocalFinalVariableName" />
<module name="LocalVariableName" />
<module name="MemberName" />
<module name="MemberName">
<property name="format" value="^\$?[a-z][a-zA-Z0-9]*$" />
</module>
<module name="MethodName">
<property name="format" value="^(computercraft\$)?[a-z][a-zA-Z0-9]*$" />
</module>
@@ -122,7 +124,7 @@ SPDX-License-Identifier: MPL-2.0
</module>
<module name="ParameterName" />
<module name="StaticVariableName">
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
<property name="format" value="^[a-z][a-zA-Z0-9]*$" />
</module>
<module name="TypeName" />

View File

@@ -16,4 +16,10 @@ SPDX-License-Identifier: MPL-2.0
<!-- The commands API is documented in Lua. -->
<suppress checks="SummaryJavadocCheck" files=".*[\\/]CommandAPI.java" />
<!-- Allow putting files in other packages if they look like our TeaVM stubs. -->
<suppress checks="PackageName" files=".*[\\/]T[A-Za-z]+.java" />
<!-- Allow underscores in our test classes. -->
<suppress checks="MethodName" files=".*Contract.java" />
</suppressions>

View File

@@ -6,7 +6,7 @@ see: key To listen to any key press.
<!--
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
SPDX-License-Identifier: LicenseRef-CCPL
SPDX-License-Identifier: MPL-2.0
-->
The [`char`] event is fired when a character is typed on the keyboard.

View File

@@ -12,7 +12,7 @@ SPDX-License-Identifier: MPL-2.0
The [`file_transfer`] event is queued when a user drags-and-drops a file on an open computer.
This event contains a single argument of type [`TransferredFiles`], which can be used to [get the files to be
transferred][`TransferredFiles.getFiles`]. Each file returned is a [binary file handle][`fs.BinaryReadHandle`] with an
transferred][`TransferredFiles.getFiles`]. Each file returned is a [binary file handle][`fs.ReadHandle`] with an
additional [getName][`TransferredFile.getName`] method.
## Return values
@@ -29,7 +29,7 @@ for _, file in ipairs(files.getFiles()) do
local size = file.seek("end")
file.seek("set", 0)
print(file.getName() .. " " .. file.getSize())
print(file.getName() .. " " .. size)
end
```

View File

@@ -5,7 +5,7 @@ module: [kind=event] key
<!--
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
SPDX-License-Identifier: LicenseRef-CCPL
SPDX-License-Identifier: MPL-2.0
-->
This event is fired when any key is pressed while the terminal is focused.

View File

@@ -6,7 +6,7 @@ see: keys For a lookup table of the given keys.
<!--
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
SPDX-License-Identifier: LicenseRef-CCPL
SPDX-License-Identifier: MPL-2.0
-->
Fired whenever a key is released (or the terminal is closed while a key was being pressed).

View File

@@ -5,7 +5,7 @@ module: [kind=event] mouse_click
<!--
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
SPDX-License-Identifier: LicenseRef-CCPL
SPDX-License-Identifier: MPL-2.0
-->
This event is fired when the terminal is clicked with a mouse. This event is only fired on advanced computers (including

View File

@@ -6,7 +6,7 @@ see: mouse_click For when a mouse button is initially pressed.
<!--
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
SPDX-License-Identifier: LicenseRef-CCPL
SPDX-License-Identifier: MPL-2.0
-->
This event is fired every time the mouse is moved while a mouse button is being held.

View File

@@ -5,7 +5,7 @@ module: [kind=event] mouse_scroll
<!--
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
SPDX-License-Identifier: LicenseRef-CCPL
SPDX-License-Identifier: MPL-2.0
-->
This event is fired when a mouse wheel is scrolled in the terminal.

View File

@@ -5,7 +5,7 @@ module: [kind=event] mouse_up
<!--
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
SPDX-License-Identifier: LicenseRef-CCPL
SPDX-License-Identifier: MPL-2.0
-->
This event is fired when a mouse button is released or a held mouse leaves the computer's terminal.

View File

@@ -134,7 +134,7 @@ accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, whic
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
[`fs.BinaryReadHandle.read`] if you prefer.
[`fs.ReadHandle.read`] if you prefer.
## Processing audio
As mentioned near the beginning of this guide, PCM audio is pretty easy to work with as it's just a list of amplitudes.

View File

@@ -0,0 +1,80 @@
---
module: [kind=reference] breaking_changes
---
<!--
SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
SPDX-License-Identifier: MPL-2.0
-->
# Incompatibilities between versions
CC: Tweaked tries to remain as compatible between versions as possible, meaning most programs written for older version
of the mod should run fine on later versions.
> [External peripherals][!WARNING]
>
> While CC: Tweaked is relatively stable across versions, this may not be true for other mods which add their own
> peripherals. Older programs which interact with external blocks may not work on newer versions of the game.
However, some changes to the underlying game, or CC: Tweaked's own internals may break some programs. This page serves
as documentation for breaking changes and "gotchas" one should look out for between versions.
## CC: Tweaked 1.109.0 {#cct-1.109}
- Update to Lua 5.2:
- Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
- Environments are no longer baked into the runtime, and instead use the `_ENV` local or upvalue. `getfenv`/`setfenv`
now only work on Lua functions with an `_ENV` upvalue. `getfenv` will return the global environment when called
with other functions, and `setfenv` will have no effect.
- `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
environment.
- Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
- File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
## Minecraft 1.13 {#mc-1.13}
- The "key code" for [`key`] and [`key_up`] events has changed, due to Minecraft updating to LWJGL 3. Make sure you're
using the constants provided by the [`keys`] API, rather than hard-coding numerical values.
Related to this change, the numpad enter key now has a different key code to the enter key. You may need to adjust
your programs to handle both. (Note, the `keys.numpadEnter` constant was defined in pre-1.13 versions of CC, but the
`keys.enter` constant was queued when the key was pressed)
- Minecraft 1.13 removed the concept of item damage and block metadata (see ["The Flattening"][flattening]). As a
result `turtle.inspect` no longer provides block metadata, and `turtle.getItemDetail` no longer provides damage.
- Block states (`turtle.inspect().state`) should provide all the same information as block metadata, but in a much
more understandable format.
- Item and block names now represent a unique item type. For instance, wool is split into 16 separate items
(`minecraft:white_wool`, etc...) rather than a single `minecraft:wool` with each meta/damage value specifying the
colour.
- Custom ROMs are now provided using data packs rather than resource packs. This should mostly be a matter of renaming
the "assets" folder to "data", and placing it in "datapacks", but there are a couple of other gotchas to look out
for:
- Data packs [impose some restrictions on file names][legal_data_pack]. As a result, your programs and directories
must all be lower case.
- Due to how data packs are read by CC: Tweaked, you may need to use the `/reload` command to see changes to your
pack show up on the computer.
See [the example datapack][datapack-example] for how to get started.
- Turtles can now be waterlogged and move "through" water sources rather than breaking them.
## CC: Tweaked 1.88.0 {#cc-1.88}
- Unlabelled computers and turtles now keep their ID when broken, meaning that unlabelled computers/items do not stack.
## ComputerCraft 1.80pr1 {#cc-1.80}
- Programs run via `shell.run` are now started in their own isolated environment. This means globals set by programs
will not be accessible outside of this program.
- Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`.
[flattening]: https://minecraft.wiki.com/w/Java_Edition_1.13/Flattening
[legal_data_pack]: https://minecraft.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"

View File

@@ -9,17 +9,19 @@ SPDX-License-Identifier: MPL-2.0
-->
# Lua 5.2/5.3 features in CC: Tweaked
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
compatibility for these newer versions.
## Lua 5.2
| Feature | Supported? | Notes |
|---------------------------------------------------------------|------------|-------------------------------------------------------------------|
| `goto`/labels | | |
| `_ENV` | 🔶 | The `_ENV` global points to `getfenv()`, but it cannot be set. |
| `goto`/labels | | |
| `_ENV` | | |
| `\z` escape | ✔ | |
| `\xNN` escape | ✔ | |
| Hex literal fractional/exponent parts | ✔ | |
| Empty statements | | |
| Empty statements | | |
| `__len` metamethod | ✔ | |
| `__ipairs` metamethod | ❌ | Deprecated in Lua 5.3. `ipairs` uses `__len`/`__index` instead. |
| `__pairs` metamethod | ✔ | |
@@ -27,12 +29,12 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
| `collectgarbage` isrunning, generational, incremental options | ❌ | `collectgarbage` does not exist in CC:T. |
| New `load` syntax | ✔ | |
| `loadfile` mode parameter | ✔ | Supports both 5.1 and 5.2+ syntax. |
| Removed `loadstring` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
| Removed `getfenv`, `setfenv` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
| Removed `loadstring` | | |
| Removed `getfenv`, `setfenv` | 🔶 | Only supports closures with an `_ENV` upvalue. |
| `rawlen` function | ✔ | |
| Negative index to `select` | ✔ | |
| Removed `unpack` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
| Arguments to `xpcall` | ✔ | |
| Removed `unpack` | | |
| Arguments to `xpcall` | ✔ | |
| Second return value from `coroutine.running` | ✔ | |
| Removed `module` | ✔ | |
| `package.loaders` -> `package.searchers` | ❌ | |
@@ -40,14 +42,14 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
| `package.config` | ✔ | |
| `package.searchpath` | ✔ | |
| Removed `package.seeall` | ✔ | |
| `string.dump` on functions with upvalues (blanks them out) | | |
| `string.rep` separator | ✔ | |
| `string.dump` on functions with upvalues (blanks them out) | | `string.dump` is not supported |
| `string.rep` separator | ✔ | |
| `%g` match group | ❌ | |
| Removal of `%z` match group | ❌ | |
| Removed `table.maxn` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
| Removed `table.maxn` | | |
| `table.pack`/`table.unpack` | ✔ | |
| `math.log` base argument | ✔ | |
| Removed `math.log10` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
| Removed `math.log10` | | |
| `*L` mode to `file:read` | ✔ | |
| `os.execute` exit type + return value | ❌ | `os.execute` does not exist in CC:T. |
| `os.exit` close argument | ❌ | `os.exit` does not exist in CC:T. |
@@ -61,7 +63,7 @@ CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However,
| Tail call hooks | ❌ | |
| `=` prefix for chunks | ✔ | |
| Yield across C boundary | ✔ | |
| Removal of ambiguity error | | |
| Removal of ambiguity error | | |
| Identifiers may no longer use locale-dependent letters | ✔ | |
| Ephemeron tables | ❌ | |
| Identical functions may be reused | ❌ | Removed in Lua 5.4 |

View File

@@ -95,10 +95,10 @@ function pullEventRaw(filter) end
-- nearest multiple of 0.05.
function sleep(time) end
--- Get the current CraftOS version (for example, `CraftOS 1.8`).
--- Get the current CraftOS version (for example, `CraftOS 1.9`).
--
-- This is defined by `bios.lua`. For the current version of CC:Tweaked, this
-- should return `CraftOS 1.8`.
-- should return `CraftOS 1.9`.
--
-- @treturn string The current CraftOS version.
-- @usage os.version()

View File

@@ -9,8 +9,8 @@ kotlin.stdlib.default.dependency=false
kotlin.jvm.target.validation.mode=error
# Mod properties
isUnstable=false
modVersion=1.108.0
isUnstable=true
modVersion=1.109.0
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
mcVersion=1.19.4
mcVersion=1.20.1

View File

@@ -7,20 +7,21 @@
# Minecraft
# MC version is specified in gradle.properties, as we need that in settings.gradle.
# Remember to update corresponding versions in fabric.mod.json/mods.toml
fabric-api = "0.86.1+1.19.4"
fabric-api = "0.86.1+1.20.1"
fabric-loader = "0.14.21"
forge = "45.0.42"
forge = "47.1.0"
forgeSpi = "6.0.0"
mixin = "0.8.5"
parchment = "2023.06.26"
parchmentMc = "1.19.4"
parchment = "2023.08.20"
parchmentMc = "1.20.1"
# Normal dependencies
asm = "9.3"
autoService = "1.0.1"
asm = "9.5"
autoService = "1.1.1"
checkerFramework = "3.32.0"
cobalt = "0.7.3"
cobalt-next = "0.7.4" # Not a real version, used to constrain the version we accept.
cobalt = "0.8.0"
cobalt-next = "0.8.1" # Not a real version, used to constrain the version we accept.
commonsCli = "1.3.1"
fastutil = "8.5.9"
guava = "31.1-jre"
jetbrainsAnnotations = "24.0.1"
@@ -29,52 +30,56 @@ jzlib = "1.1.3"
kotlin = "1.8.10"
kotlin-coroutines = "1.6.4"
netty = "4.1.82.Final"
nightConfig = "3.6.5"
slf4j = "1.7.36"
nightConfig = "3.6.7"
slf4j = "2.0.1"
# Minecraft mods
emi = "1.0.8+1.19.4"
fabricPermissions = "0.2.20221016"
iris = "1.5.2+1.19.4"
jei = "13.1.0.11"
modmenu = "6.1.0-rc.1"
emi = "1.0.8+1.20.1"
fabricPermissions = "0.3.20230723"
iris = "1.6.4+1.20"
jei = "15.2.0.22"
modmenu = "7.1.0"
moreRed = "4.0.0.4"
oculus = "1.2.5"
rei = "10.0.578"
rei = "12.0.626"
rubidium = "0.6.1"
sodium = "mc1.19.4-0.4.10"
sodium = "mc1.20-0.4.10"
# Testing
byteBuddy = "1.14.2"
hamcrest = "2.2"
jqwik = "1.7.2"
junit = "5.9.2"
jqwik = "1.7.4"
junit = "5.10.0"
# Build tools
cctJavadoc = "1.8.0"
checkstyle = "10.3.4"
cctJavadoc = "1.8.1"
checkstyle = "10.12.3"
curseForgeGradle = "1.0.14"
errorProne-core = "2.18.0"
errorProne-plugin = "3.0.1"
errorProne-core = "2.21.1"
errorProne-plugin = "3.1.0"
fabric-loom = "1.3.7"
forgeGradle = "6.0.8"
githubRelease = "2.2.12"
ideaExt = "1.1.6"
illuaminate = "0.1.0-40-g975cbc3"
githubRelease = "2.4.1"
ideaExt = "1.1.7"
illuaminate = "0.1.0-44-g9ee0055"
librarian = "1.+"
lwjgl = "3.3.1"
minotaur = "2.+"
mixinGradle = "0.7.+"
nullAway = "0.9.9"
quiltflower = "1.10.0"
spotless = "6.17.0"
spotless = "6.21.0"
taskTree = "2.1.1"
teavm = "0.10.0-SQUID.1"
vanillaGradle = "0.2.1-SNAPSHOT"
vineflower = "1.11.0"
[libraries]
# Normal dependencies
asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" }
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
guava = { module = "com.google.guava:guava", version.ref = "guava" }
@@ -94,14 +99,16 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
# Minecraft mods
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
jei-api = { module = "mezz.jei:jei-1.19.4-common-api", version.ref = "jei" }
jei-fabric = { module = "mezz.jei:jei-1.19.4-fabric", version.ref = "jei" }
jei-forge = { module = "mezz.jei:jei-1.19.4-forge", version.ref = "jei" }
jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" }
jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" }
jei-forge = { module = "mezz.jei:jei-1.20.1-forge", version.ref = "jei" }
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
@@ -110,8 +117,6 @@ rubidium = { module = "maven.modrinth:rubidium", version.ref = "rubidium" }
sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
# Testing
byteBuddyAgent = { module = "net.bytebuddy:byte-buddy-agent", version.ref = "byteBuddy" }
byteBuddy = { module = "net.bytebuddy:byte-buddy", version.ref = "byteBuddy" }
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
@@ -120,6 +125,12 @@ junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", vers
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
# LWJGL
lwjgl-bom = { module = "org.lwjgl:lwjgl-bom", version.ref = "lwjgl" }
lwjgl-core = { module = "org.lwjgl:lwjgl" }
lwjgl-opengl = { module = "org.lwjgl:lwjgl-opengl" }
lwjgl-glfw = { module = "org.lwjgl:lwjgl-glfw" }
# Build tools
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
@@ -131,29 +142,38 @@ errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", versi
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
quiltflower = { module = "io.github.juuxel:loom-quiltflower", version.ref = "quiltflower" }
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" }
teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" }
teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" }
teavm-metaprogramming-api = { module = "org.teavm:teavm-metaprogramming-api", version.ref = "teavm" }
teavm-metaprogramming-impl = { module = "org.teavm:teavm-metaprogramming-impl", version.ref = "teavm" }
teavm-platform = { module = "org.teavm:teavm-platform", version.ref = "teavm" }
teavm-tooling = { module = "org.teavm:teavm-tooling", version.ref = "teavm" }
vanillaGradle = { module = "org.spongepowered:vanillagradle", version.ref = "vanillaGradle" }
vineflower = { module = "io.github.juuxel:loom-vineflower", version.ref = "vineflower" }
[plugins]
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
ideaExt = { id = "org.jetbrains.gradle.plugin.idea-ext", version.ref = "ideaExt" }
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
[bundles]
annotations = ["jsr305", "checkerFramework", "jetbrainsAnnotations"]
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
# Minecraft
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
externalMods-forge-compile = ["oculus", "jei-api"]
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
externalMods-forge-runtime = ["jei-forge"]
externalMods-fabric = ["nightConfig-core", "nightConfig-toml"]
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
@@ -162,3 +182,7 @@ externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
# Testing
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
# Build tools
teavm-api = [ "teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api" ]
teavm-tooling = [ "teavm-tooling", "teavm-metaprogramming-impl", "teavm-jso-impl" ]

View File

@@ -2,7 +2,7 @@
; SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
;
; SPDX-License-Identifier: LicenseRef-CCPL
; SPDX-License-Identifier: MPL-2.0
(sources
/doc/
@@ -10,7 +10,7 @@
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
/projects/core/src/main/resources/data/computercraft/lua/rom/
/projects/core/src/test/resources/test-rom
/projects/web/src/mount)
/projects/web/src/frontend/mount)
(doc
; Also defined in projects/web/build.gradle.kts
@@ -23,7 +23,7 @@
(url https://tweaked.cc/)
(source-link https://github.com/cc-tweaked/CC-Tweaked/blob/${commit}/${path}#L${line})
(styles /projects/web/src/styles.css)
(styles /projects/web/build/rollup/index.css)
(scripts /projects/web/build/rollup/index.js)
(head doc/head.html))
@@ -77,7 +77,6 @@
(globals
:max
_CC_DEFAULT_SETTINGS
_CC_DISABLE_LUA51_FEATURES
_HOST
;; Ideally we'd pick these up from bios.lua, but illuaminate currently
;; isn't smart enough.
@@ -115,4 +114,4 @@
:max sleep write
cct_test describe expect howlci fail it pending stub before_each)))
(at /projects/web/src/mount/expr_template.lua (lint (globals :max __expr__)))
(at /projects/web/src/frontend/mount/expr_template.lua (lint (globals :max __expr__)))

3351
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,24 +6,24 @@
"license": "BSD-3-Clause",
"type": "module",
"dependencies": {
"@squid-dev/cc-web-term": "^2.0.0",
"preact": "^10.5.5",
"setimmediate": "^1.0.5",
"tslib": "^2.0.3"
},
"devDependencies": {
"@rollup/plugin-terser": "^0.4.0",
"@rollup/plugin-node-resolve": "^15.2.1",
"@rollup/plugin-typescript": "^11.0.0",
"@rollup/plugin-url": "^8.0.1",
"@types/glob": "^8.1.0",
"@types/react-dom": "^18.0.5",
"glob": "^9.3.0",
"react-dom": "^18.1.0",
"react": "^18.1.0",
"rehype-highlight": "^6.0.0",
"rehype-react": "^7.1.1",
"rehype": "^12.0.1",
"requirejs": "^2.3.6",
"rollup": "^3.19.1",
"ts-node": "^10.8.0",
"typescript": "^4.0.5"
"@swc/core": "^1.3.92",
"@types/node": "^20.8.3",
"lightningcss": "^1.22.0",
"preact-render-to-string": "^6.2.1",
"rehype": "^13.0.0",
"rehype-highlight": "^7.0.0",
"rehype-react": "^8.0.0",
"rollup": "^4.0.0",
"tsx": "^3.12.10",
"typescript": "^5.2.2"
}
}

View File

@@ -62,6 +62,9 @@ mentioning:
- `lints`: This defines an [ErrorProne] plugin which adds a couple of compile-time checks to our code. This is what
enforces that no client-specific code is used inside the `main` source set (and a couple of other things!).
- `standalone`: This contains a standalone UI for computers, allowing debugging and development of CraftOS without
launching Minecraft.
- `web`: This contains the additional tooling for building [the documentation website][tweaked.cc], such as support for
rendering recipes

View File

@@ -17,7 +17,7 @@ import org.joml.Matrix4f;
import javax.annotation.Nullable;
class TurtleUpgradeModellers {
final class TurtleUpgradeModellers {
private static final Transformation leftTransform = getMatrixFor(-0.4065f);
private static final Transformation rightTransform = getMatrixFor(0.4065f);
@@ -35,7 +35,7 @@ class TurtleUpgradeModellers {
static final TurtleUpgradeModeller<ITurtleUpgrade> UPGRADE_ITEM = new UpgradeItemModeller();
private static class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
@Override
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side);

View File

@@ -46,6 +46,14 @@ public class ComputerCraftTags {
public static final TagKey<Block> WIRED_MODEM = make("wired_modem");
public static final TagKey<Block> MONITOR = make("monitor");
/**
* Blocks which should be ignored by a {@code peripheral_hub} peripheral.
* <p>
* This should include blocks which themselves expose a peripheral hub (such as {@linkplain #WIRED_MODEM wired
* modems}).
*/
public static final TagKey<Block> PERIPHERAL_HUB_IGNORE = make("peripheral_hub_ignore");
/**
* Blocks which can be broken by any turtle tool.
*/

View File

@@ -4,7 +4,6 @@
package dan200.computercraft.api.pocket;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.impl.ComputerCraftAPIService;
@@ -30,14 +29,6 @@ import java.util.function.Function;
* @see PocketUpgradeDataProvider
*/
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> {
/**
* The ID for the associated registry.
*
* @deprecated Use {@link #registryId()} instead.
*/
@Deprecated(forRemoval = true)
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser"));
/**
* The ID for the associated registry.
*

View File

@@ -16,7 +16,6 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.Container;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable;
@@ -71,29 +70,6 @@ public interface ITurtleAccess {
*/
boolean teleportTo(Level world, BlockPos pos);
/**
* Returns a vector containing the floating point co-ordinates at which the turtle is rendered.
* This will shift when the turtle is moving.
*
* @param f The subframe fraction.
* @return A vector containing the floating point co-ordinates at which the turtle resides.
* @see #getVisualYaw(float)
* @deprecated Will be removed in 1.20.
*/
@Deprecated(forRemoval = true)
Vec3 getVisualPosition(float f);
/**
* Returns the yaw the turtle is facing when it is rendered.
*
* @param f The subframe fraction.
* @return The yaw the turtle is facing.
* @see #getVisualPosition(float)
* @deprecated Will be removed in 1.20.
*/
@Deprecated(forRemoval = true)
float getVisualYaw(float f);
/**
* Returns the world direction the turtle is currently facing.
*

View File

@@ -4,7 +4,6 @@
package dan200.computercraft.api.turtle;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.impl.ComputerCraftAPIService;
@@ -65,14 +64,6 @@ import java.util.function.Function;
* @see dan200.computercraft.api.client.turtle.TurtleUpgradeModeller
*/
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> {
/**
* The ID for the associated registry.
*
* @deprecated Use {@link #registryId()} instead.
*/
@Deprecated(forRemoval = true)
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser"));
/**
* The ID for the associated registry.
*

View File

@@ -19,8 +19,6 @@ import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.util.*;
@@ -36,8 +34,6 @@ import java.util.function.Function;
* @param <R> The upgrade serialiser to register for.
*/
public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends UpgradeSerialiser<? extends T>> implements DataProvider {
private static final Logger LOGGER = LogManager.getLogger();
private final PackOutput output;
private final String name;
private final String folder;

View File

@@ -7,9 +7,9 @@ import cc.tweaked.gradle.clientClasses
import cc.tweaked.gradle.commonClasses
plugins {
id("cc-tweaked.publishing")
id("cc-tweaked.vanilla")
id("cc-tweaked.gametest")
id("cc-tweaked.publishing")
}
minecraft {
@@ -39,4 +39,6 @@ dependencies {
testModImplementation(testFixtures(project(":core")))
testModImplementation(testFixtures(project(":common")))
testModImplementation(libs.bundles.kotlin)
testFixturesImplementation(testFixtures(project(":core")))
}

View File

@@ -4,7 +4,6 @@
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.DynamicImageButton;
import dan200.computercraft.client.gui.widgets.TerminalWidget;
@@ -19,6 +18,7 @@ import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.network.server.UploadFileMessage;
import net.minecraft.ChatFormatting;
import net.minecraft.Util;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
@@ -33,7 +33,6 @@ import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -124,10 +123,10 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
}
@Override
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
renderBackground(stack);
super.render(stack, mouseX, mouseY, partialTicks);
renderTooltip(stack, mouseX, mouseY);
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
renderBackground(graphics);
super.render(graphics, mouseX, mouseY, partialTicks);
renderTooltip(graphics, mouseX, mouseY);
}
@Override
@@ -147,7 +146,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
@Override
protected void renderLabels(PoseStack transform, int mouseX, int mouseY) {
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
// Skip rendering labels.
}
@@ -219,7 +218,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
private void alert(Component title, Component message) {
OptionScreen.show(minecraft, title, message,
Collections.singletonList(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
() -> minecraft.setScreen(this)
);
}

View File

@@ -4,15 +4,13 @@
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.TerminalWidget;
import dan200.computercraft.client.render.ComputerBorderRenderer;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.SpriteRenderer;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
@@ -39,12 +37,10 @@ public final class ComputerScreen<T extends AbstractComputerMenu> extends Abstra
}
@Override
public void renderBg(PoseStack stack, float partialTicks, int mouseX, int mouseY) {
public void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
// Draw a border around the terminal
var terminal = getTerminal();
var buffers = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
var spriteRenderer = SpriteRenderer.createForGui(stack, buffers.getBuffer(RenderTypes.GUI_SPRITES));
var spriteRenderer = SpriteRenderer.createForGui(graphics, RenderTypes.GUI_SPRITES);
var computerTextures = GuiSprites.getComputerTextures(family);
ComputerBorderRenderer.render(
@@ -52,6 +48,6 @@ public final class ComputerScreen<T extends AbstractComputerMenu> extends Abstra
terminal.getX(), terminal.getY(), terminal.getWidth(), terminal.getHeight(), false
);
ComputerSidebar.renderBackground(spriteRenderer, computerTextures, leftPos, topPos + sidebarYOffset);
buffers.endBatch();
graphics.flush(); // Flush to ensure background textures are drawn before foreground.
}
}

View File

@@ -4,9 +4,8 @@
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveMenu;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
@@ -23,16 +22,14 @@ public class DiskDriveScreen extends AbstractContainerScreen<DiskDriveMenu> {
}
@Override
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.setShaderTexture(0, BACKGROUND);
blit(transform, leftPos, topPos, 0, 0, imageWidth, imageHeight);
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
graphics.blit(BACKGROUND, leftPos, topPos, 0, 0, imageWidth, imageHeight);
}
@Override
public void render(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
renderBackground(transform);
super.render(transform, mouseX, mouseY, partialTicks);
renderTooltip(transform, mouseX, mouseY);
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
renderBackground(graphics);
super.render(graphics, mouseX, mouseY, partialTicks);
renderTooltip(graphics, mouseX, mouseY);
}
}

View File

@@ -4,10 +4,8 @@
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.toasts.Toast;
import net.minecraft.client.gui.components.toasts.ToastComponent;
import net.minecraft.network.chat.Component;
@@ -73,55 +71,52 @@ public class ItemToast implements Toast {
}
@Override
public Visibility render(PoseStack transform, ToastComponent component, long time) {
public Visibility render(GuiGraphics graphics, ToastComponent component, long time) {
if (isNew) {
firstDisplay = time;
isNew = false;
}
RenderSystem.setShaderTexture(0, TEXTURE);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
if (width == 160 && message.size() <= 1) {
GuiComponent.blit(transform, 0, 0, 0, 64, width, height());
graphics.blit(TEXTURE, 0, 0, 0, 64, width, height());
} else {
var height = height();
var bottom = Math.min(4, height - 28);
renderBackgroundRow(transform, component, width, 0, 0, 28);
renderBackgroundRow(graphics, width, 0, 0, 28);
for (var i = 28; i < height - bottom; i += 10) {
renderBackgroundRow(transform, component, width, 16, i, Math.min(16, height - i - bottom));
renderBackgroundRow(graphics, width, 16, i, Math.min(16, height - i - bottom));
}
renderBackgroundRow(transform, component, width, 32 - bottom, height - bottom, bottom);
renderBackgroundRow(graphics, width, 32 - bottom, height - bottom, bottom);
}
var textX = MARGIN;
if (!stack.isEmpty()) {
textX += MARGIN + IMAGE_SIZE;
component.getMinecraft().getItemRenderer().renderAndDecorateFakeItem(transform, stack, MARGIN, MARGIN + height() / 2 - IMAGE_SIZE);
graphics.renderFakeItem(stack, MARGIN, MARGIN + height() / 2 - IMAGE_SIZE);
}
component.getMinecraft().font.draw(transform, title, textX, MARGIN, 0xff500050);
graphics.drawString(component.getMinecraft().font, title, textX, MARGIN, 0xff500050, false);
for (var i = 0; i < message.size(); ++i) {
component.getMinecraft().font.draw(transform, message.get(i), textX, (float) (LINE_SPACING + (i + 1) * LINE_SPACING), 0xff000000);
graphics.drawString(component.getMinecraft().font, message.get(i), textX, LINE_SPACING + (i + 1) * LINE_SPACING, 0xff000000, false);
}
return time - firstDisplay < DISPLAY_TIME ? Visibility.SHOW : Visibility.HIDE;
}
private static void renderBackgroundRow(PoseStack transform, ToastComponent component, int x, int u, int y, int height) {
private static void renderBackgroundRow(GuiGraphics graphics, int x, int u, int y, int height) {
var leftOffset = 5;
var rightOffset = Math.min(60, x - leftOffset);
GuiComponent.blit(transform, 0, y, 0, 32 + u, leftOffset, height);
graphics.blit(TEXTURE, 0, y, 0, 32 + u, leftOffset, height);
for (var k = leftOffset; k < x - rightOffset; k += 64) {
GuiComponent.blit(transform, k, y, 32, 32 + u, Math.min(64, x - k - rightOffset), height);
graphics.blit(TEXTURE, k, y, 32, 32 + u, Math.min(64, x - k - rightOffset), height);
}
GuiComponent.blit(transform, x - rightOffset, y, 160 - rightOffset, 32 + u, rightOffset, height);
graphics.blit(TEXTURE, x - rightOffset, y, 160 - rightOffset, 32 + u, rightOffset, height);
}
}

View File

@@ -1,14 +1,14 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
//
// SPDX-License-Identifier: LicenseRef-CCPL
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.client.gui.widgets.TerminalWidget;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.MenuAccess;
import net.minecraft.network.chat.Component;
@@ -42,7 +42,6 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
@Override
protected void init() {
passEvents = true; // Pass mouse vents through to the game's mouse handler.
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
// grabbing unsets.
minecraft.mouseHandler.grabMouse();
@@ -91,15 +90,15 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
}
@Override
public void render(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
super.render(transform, mouseX, mouseY, partialTicks);
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
super.render(graphics, mouseX, mouseY, partialTicks);
var font = minecraft.font;
var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8));
var y = 10.0f;
var y = 10;
for (var line : lines) {
font.drawShadow(transform, line, (float) ((width / 2) - (minecraft.font.width(line) / 2)), y, 0xFFFFFF);
y += 9.0f;
graphics.drawString(font, line, (width / 2) - (minecraft.font.width(line) / 2), y, 0xFFFFFF, true);
y += 9;
}
}
}

View File

@@ -4,9 +4,8 @@
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.MultiLineLabel;
@@ -86,20 +85,19 @@ public final class OptionScreen extends Screen {
}
@Override
public void render(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
renderBackground(transform);
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
renderBackground(graphics);
// Render the actual texture.
RenderSystem.setShaderTexture(0, BACKGROUND);
blit(transform, x, y, 0, 0, innerWidth, PADDING);
blit(transform,
graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING);
graphics.blit(BACKGROUND,
x, y + PADDING, 0, PADDING, innerWidth, innerHeight - PADDING * 2,
innerWidth, PADDING
);
blit(transform, x, y + innerHeight - PADDING, 0, 256 - PADDING, innerWidth, PADDING);
graphics.blit(BACKGROUND, x, y + innerHeight - PADDING, 0, 256 - PADDING, innerWidth, PADDING);
assertNonNull(messageRenderer).renderLeftAlignedNoShadow(transform, x + PADDING, y + PADDING, FONT_HEIGHT, 0x404040);
super.render(transform, mouseX, mouseY, partialTicks);
assertNonNull(messageRenderer).renderLeftAlignedNoShadow(graphics, x + PADDING, y + PADDING, FONT_HEIGHT, 0x404040);
super.render(graphics, mouseX, mouseY, partialTicks);
}
@Override

View File

@@ -4,9 +4,8 @@
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.shared.peripheral.printer.PrinterMenu;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
@@ -23,18 +22,16 @@ public class PrinterScreen extends AbstractContainerScreen<PrinterMenu> {
}
@Override
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.setShaderTexture(0, BACKGROUND);
blit(transform, leftPos, topPos, 0, 0, imageWidth, imageHeight);
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
graphics.blit(BACKGROUND, leftPos, topPos, 0, 0, imageWidth, imageHeight);
if (getMenu().isPrinting()) blit(transform, leftPos + 34, topPos + 21, 176, 0, 25, 45);
if (getMenu().isPrinting()) graphics.blit(BACKGROUND, leftPos + 34, topPos + 21, 176, 0, 25, 45);
}
@Override
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
renderBackground(stack);
super.render(stack, mouseX, mouseY, partialTicks);
renderTooltip(stack, mouseX, mouseY);
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
renderBackground(graphics);
super.render(graphics, mouseX, mouseY, partialTicks);
renderTooltip(graphics, mouseX, mouseY);
}
}

View File

@@ -4,12 +4,11 @@
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.common.HeldItemMenu;
import dan200.computercraft.shared.media.items.PrintoutItem;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.network.chat.Component;
@@ -83,30 +82,27 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
}
@Override
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
// Draw the printout
RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
RenderSystem.enableDepthTest();
var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
drawBorder(transform, renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
drawText(transform, renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
renderer.endBatch();
}
@Override
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
// We must take the background further back in order to not overlap with our printed pages.
stack.pushPose();
stack.translate(0, 0, -1);
renderBackground(stack);
stack.popPose();
graphics.pose().pushPose();
graphics.pose().translate(0, 0, -1);
renderBackground(graphics);
graphics.pose().popPose();
super.render(stack, mouseX, mouseY, partialTicks);
super.render(graphics, mouseX, mouseY, partialTicks);
}
@Override
protected void renderLabels(PoseStack transform, int mouseX, int mouseY) {
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
// Skip rendering labels.
}
}

View File

@@ -4,9 +4,6 @@
package dan200.computercraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.TerminalWidget;
@@ -15,7 +12,7 @@ import dan200.computercraft.client.render.SpriteRenderer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Inventory;
@@ -47,26 +44,25 @@ public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
}
@Override
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
var advanced = family == ComputerFamily.ADVANCED;
RenderSystem.setShaderTexture(0, advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL);
blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE);
var texture = advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL;
graphics.blit(texture, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE);
// Render selected slot
var slot = getMenu().getSelectedSlot();
if (slot >= 0) {
var slotX = slot % 4;
var slotY = slot / 4;
blit(transform,
graphics.blit(texture,
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0,
0, 217, 24, 24, FULL_TEX_SIZE, FULL_TEX_SIZE
);
}
// Render sidebar
var buffers = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
var spriteRenderer = SpriteRenderer.createForGui(transform, buffers.getBuffer(RenderTypes.GUI_SPRITES));
var spriteRenderer = SpriteRenderer.createForGui(graphics, RenderTypes.GUI_SPRITES);
ComputerSidebar.renderBackground(spriteRenderer, GuiSprites.getComputerTextures(family), leftPos, topPos + sidebarYOffset);
buffers.endBatch();
graphics.flush(); // Flush to ensure background textures are drawn before foreground.
}
}

View File

@@ -5,9 +5,9 @@
package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import it.unimi.dsi.fastutil.booleans.Boolean2ObjectFunction;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
@@ -42,21 +42,20 @@ public class DynamicImageButton extends Button {
}
@Override
public void renderWidget(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
var texture = this.texture.get(isHoveredOrFocused());
RenderSystem.setShaderTexture(0, texture.atlasLocation());
RenderSystem.disableDepthTest();
blit(stack, getX(), getY(), 0, width, height, texture);
RenderSystem.disableDepthTest();
graphics.blit(getX(), getY(), 0, width, height, texture);
RenderSystem.enableDepthTest();
}
@Override
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
var message = this.message.get();
setMessage(message.message());
setTooltip(message.tooltip());
super.render(stack, mouseX, mouseY, partialTicks);
super.render(graphics, mouseX, mouseY, partialTicks);
}
public record HintedMessage(Component message, Tooltip tooltip) {

View File

@@ -4,14 +4,14 @@
package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.util.StringUtil;
import dan200.computercraft.shared.computer.core.InputHandler;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.narration.NarratedElementType;
import net.minecraft.client.gui.narration.NarrationElementOutput;
@@ -112,26 +112,8 @@ public class TerminalWidget extends AbstractWidget {
}
private void paste() {
var clipboard = Minecraft.getInstance().keyboardHandler.getClipboard();
// Clip to the first occurrence of \r or \n
var newLineIndex1 = clipboard.indexOf('\r');
var newLineIndex2 = clipboard.indexOf('\n');
if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
clipboard = clipboard.substring(0, Math.min(newLineIndex1, newLineIndex2));
} else if (newLineIndex1 >= 0) {
clipboard = clipboard.substring(0, newLineIndex1);
} else if (newLineIndex2 >= 0) {
clipboard = clipboard.substring(0, newLineIndex2);
}
// Filter the string
clipboard = SharedConstants.filterText(clipboard);
if (!clipboard.isEmpty()) {
// Clip to 512 characters and queue the event
if (clipboard.length() > 512) clipboard = clipboard.substring(0, 512);
computer.queueEvent("paste", new Object[]{ clipboard });
}
var clipboard = StringUtil.normaliseClipboardString(Minecraft.getInstance().keyboardHandler.getClipboard());
if (!clipboard.isEmpty()) computer.queueEvent("paste", new Object[]{ clipboard });
}
@Override
@@ -274,11 +256,11 @@ public class TerminalWidget extends AbstractWidget {
}
@Override
public void renderWidget(PoseStack transform, int mouseX, int mouseY, float partialTicks) {
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
if (!visible) return;
var bufferSource = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
var emitter = FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL));
var emitter = FixedWidthFontRenderer.toVertexConsumer(graphics.pose(), bufferSource.getBuffer(RenderTypes.TERMINAL));
FixedWidthFontRenderer.drawTerminal(
emitter,

View File

@@ -47,7 +47,7 @@ public class ShaderMod {
Optional<ShaderMod> get();
}
private static class Storage {
private static final class Storage {
static final ShaderMod INSTANCE = ServiceLoader.load(Provider.class)
.stream()
.flatMap(x -> x.get().get().stream())

View File

@@ -52,7 +52,7 @@ public abstract class AbstractClientNetworkContext implements ClientNetworkConte
var player = Minecraft.getInstance().player;
if (player == null) return;
var te = player.level.getBlockEntity(pos);
var te = player.level().getBlockEntity(pos);
if (!(te instanceof MonitorBlockEntity monitor)) return;
monitor.read(terminal);

View File

@@ -4,14 +4,14 @@
package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import org.joml.Matrix4f;
/**
* A {@link GuiComponent}-equivalent which is suitable for both rendering in to a GUI and in-world (as part of an entity
* A {@link GuiGraphics}-equivalent which is suitable for both rendering in to a GUI and in-world (as part of an entity
* renderer).
* <p>
* This batches all render calls together, though requires that all {@link TextureAtlasSprite}s are on the same sprite
@@ -34,8 +34,11 @@ public class SpriteRenderer {
this.b = b;
}
public static SpriteRenderer createForGui(PoseStack stack, VertexConsumer builder) {
return new SpriteRenderer(stack.last().pose(), builder, 0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255);
public static SpriteRenderer createForGui(GuiGraphics graphics, RenderType renderType) {
return new SpriteRenderer(
graphics.pose().last().pose(), graphics.bufferSource().getBuffer(renderType),
0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255
);
}
/**
@@ -52,7 +55,7 @@ public class SpriteRenderer {
}
/**
* Render a horizontal 3-sliced texture (i.e. split into left, middle and right). Unlike {@link GuiComponent#blitNineSliced},
* Render a horizontal 3-sliced texture (i.e. split into left, middle and right). Unlike {@link GuiGraphics#blitNineSliced},
* the middle texture is stretched rather than repeated.
*
* @param sprite The texture to draw.
@@ -77,7 +80,7 @@ public class SpriteRenderer {
}
/**
* Render a vertical 3-sliced texture (i.e. split into top, middle and bottom). Unlike {@link GuiComponent#blitNineSliced},
* Render a vertical 3-sliced texture (i.e. split into top, middle and bottom). Unlike {@link GuiGraphics#blitNineSliced},
* the middle texture is stretched rather than repeated.
*
* @param sprite The texture to draw.

View File

@@ -7,10 +7,7 @@ package dan200.computercraft.client.render.monitor;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.blaze3d.vertex.*;
import com.mojang.math.Axis;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.integration.ShaderMod;
@@ -170,7 +167,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
tboVertex(buffer, matrix, -xMargin, pixelHeight + yMargin);
tboVertex(buffer, matrix, pixelWidth + xMargin, -yMargin);
tboVertex(buffer, matrix, pixelWidth + xMargin, pixelHeight + yMargin);
RenderTypes.MONITOR_TBO.end(buffer, 0, 0, 0);
RenderTypes.MONITOR_TBO.end(buffer, VertexSorting.DISTANCE_TO_ORIGIN);
}
case VBO -> {
var backgroundBuffer = assertNonNull(renderState.backgroundBuffer);

View File

@@ -14,10 +14,10 @@ import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.core.util.Colour;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.server.packs.resources.ResourceProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL31;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.IOException;
@@ -36,12 +36,12 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.get
* @see RenderTypes#getMonitorTextureBufferShader()
*/
public class MonitorTextureBufferShader extends ShaderInstance {
private static final Logger LOG = LoggerFactory.getLogger(MonitorTextureBufferShader.class);
public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4;
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
private static final Logger LOGGER = LogManager.getLogger();
private final int monitorData;
private int uniformBuffer = 0;
@@ -75,7 +75,7 @@ public class MonitorTextureBufferShader extends ShaderInstance {
private Uniform getUniformChecked(String name) {
var uniform = getUniform(name);
if (uniform == null) {
LOGGER.warn("Monitor shader {} should have uniform {}, but it was not present.", getName(), name);
LOG.warn("Monitor shader {} should have uniform {}, but it was not present.", getName(), name);
}
return uniform;

View File

@@ -25,6 +25,7 @@ public class DirectVertexBuffer extends VertexBuffer {
private int actualIndexCount;
public DirectVertexBuffer() {
super(Usage.STATIC);
if (DirectBuffers.HAS_DSA) {
RenderSystem.glDeleteBuffers(vertexBufferId);
if (DirectBuffers.ON_LINUX) BufferUploader.reset(); // See comment on DirectBuffers.deleteBuffer.

View File

@@ -139,8 +139,6 @@ public final class LanguageProvider implements DataProvider {
add("commands.computercraft.tp.synopsis", "Teleport to a specific computer.");
add("commands.computercraft.tp.desc", "Teleport to the location of a computer. You can either specify the computer's instance id (e.g. 123) or computer id (e.g #123).");
add("commands.computercraft.tp.action", "Teleport to this computer");
add("commands.computercraft.tp.not_player", "Cannot open terminal for non-player");
add("commands.computercraft.tp.not_there", "Cannot locate computer in the world");
add("commands.computercraft.view.synopsis", "View the terminal of a computer.");
add("commands.computercraft.view.desc", "Open the terminal of a computer, allowing remote control of a computer. This does not provide access to turtle's inventories. You can either specify the computer's instance id (e.g. 123) or computer id (e.g #123).");
add("commands.computercraft.view.action", "View this computer");
@@ -176,6 +174,7 @@ public final class LanguageProvider implements DataProvider {
// Metrics
add(Metrics.COMPUTER_TASKS, "Tasks");
add(Metrics.SERVER_TASKS, "Server tasks");
add(Metrics.JAVA_ALLOCATION, "Java Allocations");
add(Metrics.PERIPHERAL_OPS, "Peripheral calls");
add(Metrics.FS_OPS, "Filesystem operations");
add(Metrics.HTTP_REQUESTS, "HTTP requests");
@@ -215,7 +214,6 @@ public final class LanguageProvider implements DataProvider {
addConfigEntry(ConfigSpec.floppySpaceLimit, "Floppy Disk space limit (bytes)");
addConfigEntry(ConfigSpec.uploadMaxSize, "File upload size limit (bytes)");
addConfigEntry(ConfigSpec.maximumFilesOpen, "Maximum files open per computer");
addConfigEntry(ConfigSpec.disableLua51Features, "Disable Lua 5.1 features");
addConfigEntry(ConfigSpec.defaultComputerSettings, "Default Computer settings");
addConfigEntry(ConfigSpec.logComputerErrors, "Log computer errors");
addConfigEntry(ConfigSpec.commandRequireCreative, "Command computers require creative");

View File

@@ -23,7 +23,7 @@ import net.minecraft.world.level.storage.loot.entries.LootItem;
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
import net.minecraft.world.level.storage.loot.functions.CopyNameFunction;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.predicates.AlternativeLootItemCondition;
import net.minecraft.world.level.storage.loot.predicates.AnyOfCondition;
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
@@ -79,7 +79,7 @@ class LootTableProvider {
}
private static void registerGeneric(BiConsumer<ResourceLocation, LootTable.Builder> add) {
add.accept(CommonHooks.LOOT_TREASURE_DISK, LootTable.lootTable());
add.accept(CommonHooks.TREASURE_DISK_LOOT, LootTable.lootTable());
}
private static void selfDrop(BiConsumer<ResourceLocation, LootTable.Builder> add, Supplier<? extends Block> wrapper) {
@@ -98,7 +98,7 @@ class LootTableProvider {
blockDrop(
add, block,
DynamicLoot.dynamicEntry(new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer")),
AlternativeLootItemCondition.alternative(
AnyOfCondition.anyOf(
BlockNamedEntityLootCondition.BUILDER,
HasComputerIdLootCondition.BUILDER,
PlayerCreativeLootCondition.BUILDER.invert()

View File

@@ -5,6 +5,7 @@
package dan200.computercraft.data;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
@@ -25,15 +26,12 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.data.PackOutput;
import net.minecraft.data.recipes.*;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.DyeItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.*;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.ShapedRecipe;
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
import net.minecraft.world.level.ItemLike;
@@ -41,6 +39,7 @@ import net.minecraft.world.level.block.Blocks;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.function.Consumer;
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
@@ -443,7 +442,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.requires(ModRegistry.Items.MONITOR_NORMAL.get())
.unlockedBy("has_monitor", inventoryChange(ModRegistry.Items.MONITOR_NORMAL.get()))
.save(
RecipeWrapper.wrap(RecipeSerializer.SHAPELESS_RECIPE, add)
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.SHAPELESS.get(), add)
.withResultTag(playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c")),
new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy")
);
@@ -454,7 +453,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.requires(ModRegistry.Items.COMPUTER_ADVANCED.get())
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_ADVANCED.get()))
.save(
RecipeWrapper.wrap(RecipeSerializer.SHAPELESS_RECIPE, add)
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.SHAPELESS.get(), add)
.withResultTag(playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb")),
new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200")
);
@@ -513,17 +512,15 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
}
private static CompoundTag playerHead(String name, String uuid) {
var owner = new CompoundTag();
owner.putString("Name", name);
owner.putString("Id", uuid);
var owner = NbtUtils.writeGameProfile(new CompoundTag(), new GameProfile(UUID.fromString(uuid), name));
var tag = new CompoundTag();
tag.put("SkullOwner", owner);
tag.put(PlayerHeadItem.TAG_SKULL_OWNER, owner);
return tag;
}
private static Consumer<JsonObject> family(ComputerFamily family) {
return json -> json.addProperty("family", family.toString());
return json -> json.addProperty("family", family.getSerializedName());
}
private static void addSpecial(Consumer<FinishedRecipe> add, SimpleCraftingRecipeSerializer<?> special) {

View File

@@ -35,6 +35,8 @@ class TagProvider {
tags.tag(ComputerCraftTags.Blocks.WIRED_MODEM).add(ModRegistry.Blocks.CABLE.get(), ModRegistry.Blocks.WIRED_MODEM_FULL.get());
tags.tag(ComputerCraftTags.Blocks.MONITOR).add(ModRegistry.Blocks.MONITOR_NORMAL.get(), ModRegistry.Blocks.MONITOR_ADVANCED.get());
tags.tag(ComputerCraftTags.Blocks.PERIPHERAL_HUB_IGNORE).addTag(ComputerCraftTags.Blocks.WIRED_MODEM);
tags.tag(ComputerCraftTags.Blocks.TURTLE_ALWAYS_BREAKABLE).addTag(BlockTags.LEAVES).add(
Blocks.BAMBOO, Blocks.BAMBOO_SAPLING // Bamboo isn't instabreak for some odd reason.
);
@@ -88,6 +90,8 @@ class TagProvider {
ModRegistry.Items.MONITOR_ADVANCED.get()
);
tags.tag(ItemTags.BOOKSHELF_BOOKS).add(ModRegistry.Items.PRINTED_BOOK.get());
tags.tag(ComputerCraftTags.Items.TURTLE_CAN_PLACE)
.add(Items.GLASS_BOTTLE)
.addTag(ItemTags.BOATS);

View File

@@ -18,8 +18,8 @@ import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
import net.minecraft.util.GsonHelper;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.item.ItemStack;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.Collection;
@@ -37,7 +37,7 @@ import java.util.stream.Collectors;
* @see PocketUpgrades
*/
public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends UpgradeBase> extends SimpleJsonResourceReloadListener {
private static final Logger LOGGER = LogManager.getLogger();
private static final Logger LOG = LoggerFactory.getLogger(UpgradeManager.class);
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
public record UpgradeWrapper<R extends UpgradeSerialiser<? extends T>, T extends UpgradeBase>(
@@ -48,8 +48,8 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
private final String kind;
private final ResourceKey<Registry<R>> registry;
private Map<String, UpgradeWrapper<R, T>> current = Collections.emptyMap();
private Map<T, UpgradeWrapper<R, T>> currentWrappers = Collections.emptyMap();
private Map<String, UpgradeWrapper<R, T>> current = Map.of();
private Map<T, UpgradeWrapper<R, T>> currentWrappers = Map.of();
public UpgradeManager(String kind, String path, ResourceKey<Registry<R>> registry) {
super(GSON, path);
@@ -103,13 +103,13 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
try {
loadUpgrade(newUpgrades, element.getKey(), element.getValue());
} catch (IllegalArgumentException | JsonParseException e) {
LOGGER.error("Error loading {} {} from JSON file", kind, element.getKey(), e);
LOG.error("Error loading {} {} from JSON file", kind, element.getKey(), e);
}
}
current = Collections.unmodifiableMap(newUpgrades);
currentWrappers = newUpgrades.values().stream().collect(Collectors.toUnmodifiableMap(UpgradeWrapper::upgrade, x -> x));
LOGGER.info("Loaded {} {}s", current.size(), kind);
LOG.info("Loaded {} {}s", current.size(), kind);
}
private void loadUpgrade(Map<String, UpgradeWrapper<R, T>> current, ResourceLocation id, JsonElement json) {

View File

@@ -12,7 +12,7 @@ import java.util.HashMap;
import java.util.Map;
final class WiredNetworkChangeImpl implements WiredNetworkChange {
private static final WiredNetworkChangeImpl EMPTY = new WiredNetworkChangeImpl(Collections.emptyMap(), Collections.emptyMap());
private static final WiredNetworkChangeImpl EMPTY = new WiredNetworkChangeImpl(Map.of(), Map.of());
private final Map<String, IPeripheral> removed;
private final Map<String, IPeripheral> added;
@@ -27,11 +27,11 @@ final class WiredNetworkChangeImpl implements WiredNetworkChange {
}
static WiredNetworkChangeImpl added(Map<String, IPeripheral> added) {
return added.isEmpty() ? EMPTY : new WiredNetworkChangeImpl(Collections.emptyMap(), Collections.unmodifiableMap(added));
return added.isEmpty() ? EMPTY : new WiredNetworkChangeImpl(Map.of(), Collections.unmodifiableMap(added));
}
static WiredNetworkChangeImpl removed(Map<String, IPeripheral> removed) {
return removed.isEmpty() ? EMPTY : new WiredNetworkChangeImpl(Collections.unmodifiableMap(removed), Collections.emptyMap());
return removed.isEmpty() ? EMPTY : new WiredNetworkChangeImpl(Collections.unmodifiableMap(removed), Map.of());
}
static WiredNetworkChangeImpl changeOf(Map<String, IPeripheral> oldPeripherals, Map<String, IPeripheral> newPeripherals) {
@@ -39,9 +39,9 @@ final class WiredNetworkChangeImpl implements WiredNetworkChange {
if (oldPeripherals.isEmpty() && newPeripherals.isEmpty()) {
return EMPTY;
} else if (oldPeripherals.isEmpty()) {
return new WiredNetworkChangeImpl(Collections.emptyMap(), newPeripherals);
return new WiredNetworkChangeImpl(Map.of(), newPeripherals);
} else if (newPeripherals.isEmpty()) {
return new WiredNetworkChangeImpl(oldPeripherals, Collections.emptyMap());
return new WiredNetworkChangeImpl(oldPeripherals, Map.of());
}
Map<String, IPeripheral> added = new HashMap<>(newPeripherals);

View File

@@ -4,7 +4,6 @@
package dan200.computercraft.impl.network.wired;
import com.google.common.collect.ImmutableMap;
import dan200.computercraft.api.network.Packet;
import dan200.computercraft.api.network.wired.WiredNetwork;
import dan200.computercraft.api.network.wired.WiredNode;
@@ -24,7 +23,7 @@ final class WiredNetworkImpl implements WiredNetwork {
nodes.add(node);
}
private WiredNetworkImpl(HashSet<WiredNodeImpl> nodes) {
private WiredNetworkImpl(Set<WiredNodeImpl> nodes) {
this.nodes = nodes;
}
@@ -57,10 +56,10 @@ final class WiredNetworkImpl implements WiredNetwork {
// Move all nodes across into this network, destroying the original nodes.
nodes.addAll(otherNodes);
for (var node : otherNodes) node.network = this;
other.nodes = Collections.emptySet();
other.nodes = Set.of();
// Move all peripherals across,
other.peripherals = Collections.emptyMap();
other.peripherals = Map.of();
peripherals.putAll(otherPeripherals);
if (!thisPeripherals.isEmpty()) {
@@ -217,7 +216,7 @@ final class WiredNetworkImpl implements WiredNetwork {
try {
// We special case the original node: detaching all peripherals when needed.
wired.network = wiredNetwork;
wired.peripherals = Collections.emptyMap();
wired.peripherals = Map.of();
// Ensure every network is finalised
for (var network : maximals) {
@@ -260,7 +259,7 @@ final class WiredNetworkImpl implements WiredNetwork {
var change = WiredNetworkChangeImpl.changeOf(oldPeripherals, newPeripherals);
if (change.isEmpty()) return;
wired.peripherals = ImmutableMap.copyOf(newPeripherals);
wired.peripherals = Map.copyOf(newPeripherals);
// Detach the old peripherals then remove them.
peripherals.keySet().removeAll(change.peripheralsRemoved().keySet());
@@ -333,7 +332,7 @@ final class WiredNetworkImpl implements WiredNetwork {
// Detach the old peripherals then remove them from the old network
wired.network = wiredNetwork;
wired.neighbours.clear();
wired.peripherals = Collections.emptyMap();
wired.peripherals = Map.of();
// Broadcast the change
if (!peripherals.isEmpty()) WiredNetworkChangeImpl.removed(peripherals).broadcast(wired);
@@ -375,7 +374,7 @@ final class WiredNetworkImpl implements WiredNetwork {
}
}
private static HashSet<WiredNodeImpl> reachableNodes(WiredNodeImpl start) {
private static Set<WiredNodeImpl> reachableNodes(WiredNodeImpl start) {
Queue<WiredNodeImpl> enqueued = new ArrayDeque<>();
var reachable = new HashSet<WiredNodeImpl>();

View File

@@ -13,13 +13,16 @@ import dan200.computercraft.api.network.wired.WiredSender;
import dan200.computercraft.api.peripheral.IPeripheral;
import javax.annotation.Nullable;
import java.util.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public final class WiredNodeImpl implements WiredNode {
private @Nullable Set<PacketReceiver> receivers;
final WiredElement element;
Map<String, IPeripheral> peripherals = Collections.emptyMap();
Map<String, IPeripheral> peripherals = Map.of();
final HashSet<WiredNodeImpl> neighbours = new HashSet<>();
volatile WiredNetworkImpl network;

View File

@@ -28,8 +28,6 @@ import net.minecraft.world.level.storage.loot.entries.LootTableReference;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -75,9 +73,9 @@ public final class CommonHooks {
MonitorWatcher.onWatch(chunk, player);
}
public static final ResourceLocation LOOT_TREASURE_DISK = new ResourceLocation(ComputerCraftAPI.MOD_ID, "treasure_disk");
public static final ResourceLocation TREASURE_DISK_LOOT = new ResourceLocation(ComputerCraftAPI.MOD_ID, "treasure_disk");
private static final Set<ResourceLocation> TABLES = new HashSet<>(Arrays.asList(
private static final Set<ResourceLocation> TREASURE_DISK_LOOT_TABLES = Set.of(
BuiltInLootTables.SIMPLE_DUNGEON,
BuiltInLootTables.ABANDONED_MINESHAFT,
BuiltInLootTables.STRONGHOLD_CORRIDOR,
@@ -88,14 +86,15 @@ public final class CommonHooks {
BuiltInLootTables.IGLOO_CHEST,
BuiltInLootTables.WOODLAND_MANSION,
BuiltInLootTables.VILLAGE_CARTOGRAPHER
));
);
public static @Nullable LootPool.Builder getExtraLootPool(ResourceLocation lootTable) {
if (!lootTable.getNamespace().equals("minecraft") || !TABLES.contains(lootTable)) return null;
if (!lootTable.getNamespace().equals("minecraft") || !TREASURE_DISK_LOOT_TABLES.contains(lootTable)) {
return null;
}
return LootPool.lootPool()
.add(LootTableReference.lootTableReference(LOOT_TREASURE_DISK))
.add(LootTableReference.lootTableReference(TREASURE_DISK_LOOT))
.setRolls(ConstantValue.exactly(1));
}

View File

@@ -24,12 +24,14 @@ import dan200.computercraft.shared.common.ClearColourRecipe;
import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
import dan200.computercraft.shared.common.HeldItemMenu;
import dan200.computercraft.shared.computer.blocks.CommandComputerBlock;
import dan200.computercraft.shared.computer.blocks.CommandComputerBlockEntity;
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
import dan200.computercraft.shared.computer.items.CommandComputerItem;
import dan200.computercraft.shared.computer.items.ComputerItem;
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
@@ -68,6 +70,10 @@ import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
import dan200.computercraft.shared.recipe.CustomShapedRecipe;
import dan200.computercraft.shared.recipe.CustomShapelessRecipe;
import dan200.computercraft.shared.recipe.ImpostorShapedRecipe;
import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe;
import dan200.computercraft.shared.turtle.FurnaceRefuelHandler;
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
@@ -77,8 +83,6 @@ import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe;
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
import dan200.computercraft.shared.turtle.upgrades.*;
import dan200.computercraft.shared.util.ImpostorRecipe;
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
@@ -97,7 +101,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.level.material.MapColor;
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
import java.util.function.BiFunction;
@@ -117,7 +121,7 @@ public final class ModRegistry {
static final RegistrationHelper<Block> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK);
private static BlockBehaviour.Properties properties() {
return BlockBehaviour.Properties.of(Material.STONE).strength(2);
return BlockBehaviour.Properties.of().strength(2);
}
private static BlockBehaviour.Properties computerProperties() {
@@ -127,45 +131,45 @@ public final class ModRegistry {
}
private static BlockBehaviour.Properties turtleProperties() {
return BlockBehaviour.Properties.of(Material.STONE).strength(2.5f);
return BlockBehaviour.Properties.of().strength(2.5f);
}
private static BlockBehaviour.Properties modemProperties() {
return BlockBehaviour.Properties.of(Material.STONE).strength(1.5f);
return BlockBehaviour.Properties.of().strength(1.5f);
}
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_NORMAL = REGISTRY.register("computer_normal",
() -> new ComputerBlock<>(computerProperties(), ComputerFamily.NORMAL, BlockEntities.COMPUTER_NORMAL));
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.COMPUTER_NORMAL));
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced",
() -> new ComputerBlock<>(computerProperties(), ComputerFamily.ADVANCED, BlockEntities.COMPUTER_ADVANCED));
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.COMPUTER_ADVANCED));
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command", () -> new ComputerBlock<>(
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command", () -> new CommandComputerBlock<>(
computerProperties().strength(-1, 6000000.0F),
ComputerFamily.COMMAND, BlockEntities.COMPUTER_COMMAND
));
public static final RegistryEntry<TurtleBlock> TURTLE_NORMAL = REGISTRY.register("turtle_normal",
() -> new TurtleBlock(turtleProperties(), ComputerFamily.NORMAL, BlockEntities.TURTLE_NORMAL));
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.TURTLE_NORMAL));
public static final RegistryEntry<TurtleBlock> TURTLE_ADVANCED = REGISTRY.register("turtle_advanced",
() -> new TurtleBlock(turtleProperties(), ComputerFamily.ADVANCED, BlockEntities.TURTLE_ADVANCED));
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.TURTLE_ADVANCED));
public static final RegistryEntry<SpeakerBlock> SPEAKER = REGISTRY.register("speaker", () -> new SpeakerBlock(properties()));
public static final RegistryEntry<DiskDriveBlock> DISK_DRIVE = REGISTRY.register("disk_drive", () -> new DiskDriveBlock(properties()));
public static final RegistryEntry<PrinterBlock> PRINTER = REGISTRY.register("printer", () -> new PrinterBlock(properties()));
public static final RegistryEntry<SpeakerBlock> SPEAKER = REGISTRY.register("speaker", () -> new SpeakerBlock(properties().mapColor(MapColor.STONE)));
public static final RegistryEntry<DiskDriveBlock> DISK_DRIVE = REGISTRY.register("disk_drive", () -> new DiskDriveBlock(properties().mapColor(MapColor.STONE)));
public static final RegistryEntry<PrinterBlock> PRINTER = REGISTRY.register("printer", () -> new PrinterBlock(properties().mapColor(MapColor.STONE)));
public static final RegistryEntry<MonitorBlock> MONITOR_NORMAL = REGISTRY.register("monitor_normal",
() -> new MonitorBlock(properties(), BlockEntities.MONITOR_NORMAL));
() -> new MonitorBlock(properties().mapColor(MapColor.STONE), BlockEntities.MONITOR_NORMAL));
public static final RegistryEntry<MonitorBlock> MONITOR_ADVANCED = REGISTRY.register("monitor_advanced",
() -> new MonitorBlock(properties(), BlockEntities.MONITOR_ADVANCED));
() -> new MonitorBlock(properties().mapColor(MapColor.GOLD), BlockEntities.MONITOR_ADVANCED));
public static final RegistryEntry<WirelessModemBlock> WIRELESS_MODEM_NORMAL = REGISTRY.register("wireless_modem_normal",
() -> new WirelessModemBlock(properties(), BlockEntities.WIRELESS_MODEM_NORMAL));
() -> new WirelessModemBlock(properties().mapColor(MapColor.STONE), BlockEntities.WIRELESS_MODEM_NORMAL));
public static final RegistryEntry<WirelessModemBlock> WIRELESS_MODEM_ADVANCED = REGISTRY.register("wireless_modem_advanced",
() -> new WirelessModemBlock(properties(), BlockEntities.WIRELESS_MODEM_ADVANCED));
() -> new WirelessModemBlock(properties().mapColor(MapColor.GOLD), BlockEntities.WIRELESS_MODEM_ADVANCED));
public static final RegistryEntry<WiredModemFullBlock> WIRED_MODEM_FULL = REGISTRY.register("wired_modem_full",
() -> new WiredModemFullBlock(modemProperties()));
public static final RegistryEntry<CableBlock> CABLE = REGISTRY.register("cable", () -> new CableBlock(modemProperties()));
() -> new WiredModemFullBlock(modemProperties().mapColor(MapColor.STONE)));
public static final RegistryEntry<CableBlock> CABLE = REGISTRY.register("cable", () -> new CableBlock(modemProperties().mapColor(MapColor.STONE)));
}
public static class BlockEntities {
@@ -222,7 +226,7 @@ public final class ModRegistry {
public static final RegistryEntry<ComputerItem> COMPUTER_NORMAL = ofBlock(Blocks.COMPUTER_NORMAL, ComputerItem::new);
public static final RegistryEntry<ComputerItem> COMPUTER_ADVANCED = ofBlock(Blocks.COMPUTER_ADVANCED, ComputerItem::new);
public static final RegistryEntry<ComputerItem> COMPUTER_COMMAND = ofBlock(Blocks.COMPUTER_COMMAND, ComputerItem::new);
public static final RegistryEntry<ComputerItem> COMPUTER_COMMAND = ofBlock(Blocks.COMPUTER_COMMAND, CommandComputerItem::new);
public static final RegistryEntry<PocketComputerItem> POCKET_COMPUTER_NORMAL = REGISTRY.register("pocket_computer_normal",
() -> new PocketComputerItem(properties().stacksTo(1), ComputerFamily.NORMAL));
@@ -357,17 +361,21 @@ public final class ModRegistry {
return REGISTRY.register(name, () -> new SimpleCraftingRecipeSerializer<>(factory));
}
public static final RegistryEntry<RecipeSerializer<CustomShapedRecipe>> SHAPED = REGISTRY.register("shaped", () -> CustomShapedRecipe.serialiser(CustomShapedRecipe::new));
public static final RegistryEntry<RecipeSerializer<CustomShapelessRecipe>> SHAPELESS = REGISTRY.register("shapeless", () -> CustomShapelessRecipe.serialiser(CustomShapelessRecipe::new));
public static final RegistryEntry<RecipeSerializer<ImpostorShapedRecipe>> IMPOSTOR_SHAPED = REGISTRY.register("impostor_shaped", () -> CustomShapedRecipe.serialiser(ImpostorShapedRecipe::new));
public static final RegistryEntry<RecipeSerializer<ImpostorShapelessRecipe>> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", () -> CustomShapelessRecipe.serialiser(ImpostorShapelessRecipe::new));
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ColourableRecipe>> DYEABLE_ITEM = simple("colour", ColourableRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ClearColourRecipe>> DYEABLE_ITEM_CLEAR = simple("clear_colour", ClearColourRecipe::new);
public static final RegistryEntry<TurtleRecipe.Serializer> TURTLE = REGISTRY.register("turtle", TurtleRecipe.Serializer::new);
public static final RegistryEntry<RecipeSerializer<TurtleRecipe>> TURTLE = REGISTRY.register("turtle", () -> TurtleRecipe.validatingSerialiser(TurtleRecipe::of));
public static final RegistryEntry<SimpleCraftingRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new);
public static final RegistryEntry<TurtleOverlayRecipe.Serializer> TURTLE_OVERLAY = REGISTRY.register("turtle_overlay", TurtleOverlayRecipe.Serializer::new);
public static final RegistryEntry<RecipeSerializer<TurtleOverlayRecipe>> TURTLE_OVERLAY = REGISTRY.register("turtle_overlay", TurtleOverlayRecipe.Serializer::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PrintoutRecipe>> PRINTOUT = simple("printout", PrintoutRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<DiskRecipe>> DISK = simple("disk", DiskRecipe::new);
public static final RegistryEntry<ComputerUpgradeRecipe.Serializer> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", ComputerUpgradeRecipe.Serializer::new);
public static final RegistryEntry<ImpostorRecipe.Serializer> IMPOSTOR_SHAPED = REGISTRY.register("impostor_shaped", ImpostorRecipe.Serializer::new);
public static final RegistryEntry<ImpostorShapelessRecipe.Serializer> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", ImpostorShapelessRecipe.Serializer::new);
public static final RegistryEntry<RecipeSerializer<ComputerUpgradeRecipe>> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", ComputerUpgradeRecipe.Serializer::new);
}
public static class Permissions {
@@ -382,51 +390,11 @@ public final class ModRegistry {
public static final Predicate<CommandSourceStack> PERMISSION_VIEW = REGISTRY.registerCommand("view", UserLevel.OP);
}
/**
* Register any objects which don't have to be done on the main thread.
*/
public static void register() {
Blocks.REGISTRY.register();
BlockEntities.REGISTRY.register();
Items.REGISTRY.register();
TurtleSerialisers.REGISTRY.register();
PocketUpgradeSerialisers.REGISTRY.register();
Menus.REGISTRY.register();
ArgumentTypes.REGISTRY.register();
LootItemConditionTypes.REGISTRY.register();
RecipeSerializers.REGISTRY.register();
Permissions.REGISTRY.register();
static class CreativeTabs {
static final RegistrationHelper<CreativeModeTab> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.CREATIVE_MODE_TAB);
// Register bundled power providers
ComputerCraftAPI.registerBundledRedstoneProvider(new DefaultBundledRedstoneProvider());
ComputerCraftAPI.registerRefuelHandler(new FurnaceRefuelHandler());
ComputerCraftAPI.registerMediaProvider(stack -> {
var item = stack.getItem();
if (item instanceof IMedia media) return media;
if (item instanceof RecordItem) return RecordMedia.INSTANCE;
return null;
});
VanillaDetailRegistries.ITEM_STACK.addProvider(ItemDetails::fill);
VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider(BlockDetails::fill);
}
/**
* Register any objects which must be done on the main thread.
*/
public static void registerMainThread() {
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
}
/**
* Configure a {@link CreativeModeTab.Builder} to contain all of ComputerCraft's items.
*
* @param builder The builder to configure.
* @return The same building, for calling {@link CreativeModeTab.Builder#build()} on.
*/
public static CreativeModeTab.Builder registerCreativeTab(CreativeModeTab.Builder builder) {
return builder
@SuppressWarnings("unused")
private static final RegistryEntry<CreativeModeTab> TAB = REGISTRY.register("tab", () -> PlatformHelper.get().newCreativeModeTab()
.icon(() -> new ItemStack(Items.COMPUTER_NORMAL.get()))
.title(Component.translatable("itemGroup.computercraft"))
.displayItems((context, out) -> {
@@ -458,7 +426,46 @@ public final class ModRegistry {
for (var colour = 0; colour < 16; colour++) {
out.accept(DiskItem.createFromIDAndColour(-1, null, Colour.VALUES[colour].getHex()));
}
});
})
.build());
}
/**
* Register any objects which don't have to be done on the main thread.
*/
public static void register() {
Blocks.REGISTRY.register();
BlockEntities.REGISTRY.register();
Items.REGISTRY.register();
TurtleSerialisers.REGISTRY.register();
PocketUpgradeSerialisers.REGISTRY.register();
Menus.REGISTRY.register();
ArgumentTypes.REGISTRY.register();
LootItemConditionTypes.REGISTRY.register();
RecipeSerializers.REGISTRY.register();
Permissions.REGISTRY.register();
CreativeTabs.REGISTRY.register();
// Register bundled power providers
ComputerCraftAPI.registerBundledRedstoneProvider(new DefaultBundledRedstoneProvider());
ComputerCraftAPI.registerRefuelHandler(new FurnaceRefuelHandler());
ComputerCraftAPI.registerMediaProvider(stack -> {
var item = stack.getItem();
if (item instanceof IMedia media) return media;
if (item instanceof RecordItem) return RecordMedia.INSTANCE;
return null;
});
VanillaDetailRegistries.ITEM_STACK.addProvider(ItemDetails::fill);
VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider(BlockDetails::fill);
}
/**
* Register any objects which must be done on the main thread.
*/
public static void registerMainThread() {
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
}
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {

View File

@@ -26,7 +26,6 @@ import dan200.computercraft.shared.network.container.ComputerContainerData;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.RelativeMovement;
import net.minecraft.world.entity.player.Inventory;
@@ -34,13 +33,15 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nullable;
import java.io.File;
import java.util.*;
import static dan200.computercraft.shared.command.CommandUtils.isPlayer;
import static dan200.computercraft.shared.command.Exceptions.*;
import static dan200.computercraft.shared.command.Exceptions.NOT_TRACKING_EXCEPTION;
import static dan200.computercraft.shared.command.Exceptions.NO_TIMINGS_EXCEPTION;
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.getComputerArgument;
import static dan200.computercraft.shared.command.arguments.ComputerArgumentType.oneComputer;
import static dan200.computercraft.shared.command.arguments.ComputersArgumentType.*;
@@ -62,114 +63,25 @@ public final class CommandComputerCraft {
dispatcher.register(choice("computercraft")
.then(literal("dump")
.requires(ModRegistry.Permissions.PERMISSION_DUMP)
.executes(context -> {
var table = new TableBuilder("DumpAll", "Computer", "On", "Position");
var source = context.getSource();
List<ServerComputer> computers = new ArrayList<>(ServerContext.get(source.getServer()).registry().getComputers());
Level world = source.getLevel();
var pos = BlockPos.containing(source.getPosition());
computers.sort((a, b) -> {
if (a.getLevel() == b.getLevel() && a.getLevel() == world) {
return Double.compare(a.getPosition().distSqr(pos), b.getPosition().distSqr(pos));
} else if (a.getLevel() == world) {
return -1;
} else if (b.getLevel() == world) {
return 1;
} else {
return Integer.compare(a.getInstanceID(), b.getInstanceID());
}
});
for (var computer : computers) {
table.row(
linkComputer(source, computer, computer.getID()),
bool(computer.isOn()),
linkPosition(source, computer)
);
}
table.display(context.getSource());
return computers.size();
})
.executes(c -> dump(c.getSource()))
.then(args()
.arg("computer", oneComputer())
.executes(context -> {
var computer = getComputerArgument(context, "computer");
var table = new TableBuilder("Dump");
table.row(header("Instance"), text(Integer.toString(computer.getInstanceID())));
table.row(header("Id"), text(Integer.toString(computer.getID())));
table.row(header("Label"), text(computer.getLabel()));
table.row(header("On"), bool(computer.isOn()));
table.row(header("Position"), linkPosition(context.getSource(), computer));
table.row(header("Family"), text(computer.getFamily().toString()));
for (var side : ComputerSide.values()) {
var peripheral = computer.getPeripheral(side);
if (peripheral != null) {
table.row(header("Peripheral " + side.getName()), text(peripheral.getType()));
}
}
table.display(context.getSource());
return 1;
})))
.executes(c -> dumpComputer(c.getSource(), getComputerArgument(c, "computer")))))
.then(command("shutdown")
.requires(ModRegistry.Permissions.PERMISSION_SHUTDOWN)
.argManyValue("computers", manyComputers(), s -> ServerContext.get(s.getServer()).registry().getComputers())
.executes((context, computerSelectors) -> {
var shutdown = 0;
var computers = unwrap(context.getSource(), computerSelectors);
for (var computer : computers) {
if (computer.isOn()) shutdown++;
computer.shutdown();
}
context.getSource().sendSuccess(Component.translatable("commands.computercraft.shutdown.done", shutdown, computers.size()), false);
return shutdown;
}))
.executes((c, a) -> shutdown(c.getSource(), unwrap(c.getSource(), a))))
.then(command("turn-on")
.requires(ModRegistry.Permissions.PERMISSION_TURN_ON)
.argManyValue("computers", manyComputers(), s -> ServerContext.get(s.getServer()).registry().getComputers())
.executes((context, computerSelectors) -> {
var on = 0;
var computers = unwrap(context.getSource(), computerSelectors);
for (var computer : computers) {
if (!computer.isOn()) on++;
computer.turnOn();
}
context.getSource().sendSuccess(Component.translatable("commands.computercraft.turn_on.done", on, computers.size()), false);
return on;
}))
.executes((c, a) -> turnOn(c.getSource(), unwrap(c.getSource(), a))))
.then(command("tp")
.requires(ModRegistry.Permissions.PERMISSION_TP)
.arg("computer", oneComputer())
.executes(context -> {
var computer = getComputerArgument(context, "computer");
var world = computer.getLevel();
var pos = computer.getPosition();
var entity = context.getSource().getEntityOrException();
if (!(entity instanceof ServerPlayer player)) throw TP_NOT_PLAYER.create();
if (player.getCommandSenderWorld() == world) {
player.connection.teleport(
pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0,
EnumSet.noneOf(RelativeMovement.class)
);
} else {
player.teleportTo(world,
pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0
);
}
return 1;
}))
.executes(c -> teleport(c.getSource(), getComputerArgument(c, "computer"))))
.then(command("queue")
.requires(ModRegistry.Permissions.PERMISSION_QUEUE)
@@ -177,80 +89,244 @@ public final class CommandComputerCraft {
RequiredArgumentBuilder.<CommandSourceStack, ComputersArgumentType.ComputersSupplier>argument("computer", manyComputers())
.suggests((context, builder) -> Suggestions.empty())
)
.argManyValue("args", StringArgumentType.string(), Collections.emptyList())
.executes((ctx, args) -> {
var computers = getComputersArgument(ctx, "computer");
var rest = args.toArray();
var queued = 0;
for (var computer : computers) {
if (computer.getFamily() == ComputerFamily.COMMAND && computer.isOn()) {
computer.queueEvent("computer_command", rest);
queued++;
}
}
return queued;
}))
.argManyValue("args", StringArgumentType.string(), List.of())
.executes((c, a) -> queue(getComputersArgument(c, "computer"), a)))
.then(command("view")
.requires(ModRegistry.Permissions.PERMISSION_VIEW)
.arg("computer", oneComputer())
.executes(context -> {
var player = context.getSource().getPlayerOrException();
var computer = getComputerArgument(context, "computer");
new ComputerContainerData(computer, ItemStack.EMPTY).open(player, new MenuProvider() {
@Override
public Component getDisplayName() {
return Component.translatable("gui.computercraft.view_computer");
}
@Override
public AbstractContainerMenu createMenu(int id, Inventory player, Player entity) {
return new ViewComputerMenu(id, player, computer);
}
});
return 1;
}))
.executes(c -> view(c.getSource(), getComputerArgument(c, "computer"))))
.then(choice("track")
.requires(ModRegistry.Permissions.PERMISSION_TRACK)
.then(command("start")
.executes(context -> {
getMetricsInstance(context.getSource()).start();
var stopCommand = "/computercraft track stop";
context.getSource().sendSuccess(Component.translatable(
"commands.computercraft.track.start.stop",
link(text(stopCommand), stopCommand, Component.translatable("commands.computercraft.track.stop.action"))
), false);
return 1;
}))
.then(command("stop")
.executes(context -> {
var timings = getMetricsInstance(context.getSource());
if (!timings.stop()) throw NOT_TRACKING_EXCEPTION.create();
displayTimings(context.getSource(), timings.getSnapshot(), new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.AVG), DEFAULT_FIELDS);
return 1;
}))
.then(command("start").executes(c -> trackStart(c.getSource())))
.then(command("stop").executes(c -> trackStop(c.getSource())))
.then(command("dump")
.argManyValue("fields", metric(), DEFAULT_FIELDS)
.executes((context, fields) -> {
AggregatedMetric sort;
if (fields.size() == 1 && DEFAULT_FIELDS.contains(fields.get(0))) {
sort = fields.get(0);
fields = DEFAULT_FIELDS;
} else {
sort = fields.get(0);
}
return displayTimings(context.getSource(), sort, fields);
})))
.executes((c, f) -> trackDump(c.getSource(), f))))
);
}
/**
* Display loaded computers to a table.
*
* @param source The thing that executed this command.
* @return The number of loaded computers.
*/
private static int dump(CommandSourceStack source) {
var table = new TableBuilder("DumpAll", "Computer", "On", "Position");
List<ServerComputer> computers = new ArrayList<>(ServerContext.get(source.getServer()).registry().getComputers());
Level world = source.getLevel();
var pos = BlockPos.containing(source.getPosition());
// Sort by nearby computers.
computers.sort((a, b) -> {
if (a.getLevel() == b.getLevel() && a.getLevel() == world) {
return Double.compare(a.getPosition().distSqr(pos), b.getPosition().distSqr(pos));
} else if (a.getLevel() == world) {
return -1;
} else if (b.getLevel() == world) {
return 1;
} else {
return Integer.compare(a.getInstanceID(), b.getInstanceID());
}
});
for (var computer : computers) {
table.row(
linkComputer(source, computer, computer.getID()),
bool(computer.isOn()),
linkPosition(source, computer)
);
}
table.display(source);
return computers.size();
}
/**
* Display additional information about a single computer.
*
* @param source The thing that executed this command.
* @param computer The computer we're dumping.
* @return The constant {@code 1}.
*/
private static int dumpComputer(CommandSourceStack source, ServerComputer computer) {
var table = new TableBuilder("Dump");
table.row(header("Instance"), text(Integer.toString(computer.getInstanceID())));
table.row(header("Id"), text(Integer.toString(computer.getID())));
table.row(header("Label"), text(computer.getLabel()));
table.row(header("On"), bool(computer.isOn()));
table.row(header("Position"), linkPosition(source, computer));
table.row(header("Family"), text(computer.getFamily().toString()));
for (var side : ComputerSide.values()) {
var peripheral = computer.getPeripheral(side);
if (peripheral != null) {
table.row(header("Peripheral " + side.getName()), text(peripheral.getType()));
}
}
table.display(source);
return 1;
}
/**
* Shutdown a list of computers.
*
* @param source The thing that executed this command.
* @param computers The computers to shutdown.
* @return The constant {@code 1}.
*/
private static int shutdown(CommandSourceStack source, Collection<ServerComputer> computers) {
var shutdown = 0;
for (var computer : computers) {
if (computer.isOn()) shutdown++;
computer.shutdown();
}
var didShutdown = shutdown;
source.sendSuccess(() -> Component.translatable("commands.computercraft.shutdown.done", didShutdown, computers.size()), false);
return shutdown;
}
/**
* Turn on a list of computers.
*
* @param source The thing that executed this command.
* @param computers The computers to turn on.
* @return The constant {@code 1}.
*/
private static int turnOn(CommandSourceStack source, Collection<ServerComputer> computers) {
var on = 0;
for (var computer : computers) {
if (!computer.isOn()) on++;
computer.turnOn();
}
var didOn = on;
source.sendSuccess(() -> Component.translatable("commands.computercraft.turn_on.done", didOn, computers.size()), false);
return on;
}
/**
* Teleport to a computer.
*
* @param source The thing that executed this command. This must be an entity, other types will throw an exception.
* @param computer The computer to teleport to.
* @return The constant {@code 1}.
*/
private static int teleport(CommandSourceStack source, ServerComputer computer) throws CommandSyntaxException {
var world = computer.getLevel();
var pos = Vec3.atBottomCenterOf(computer.getPosition());
source.getEntityOrException().teleportTo(world, pos.x(), pos.y(), pos.z(), EnumSet.noneOf(RelativeMovement.class), 0, 0);
return 1;
}
/**
* Queue a {@code computer_command} event on a command computer.
*
* @param computers The list of computers to queue on.
* @param args The arguments for this event.
* @return The number of computers this event was queued on.
*/
private static int queue(Collection<ServerComputer> computers, List<String> args) {
var rest = args.toArray();
var queued = 0;
for (var computer : computers) {
if (computer.getFamily() == ComputerFamily.COMMAND && computer.isOn()) {
computer.queueEvent("computer_command", rest);
queued++;
}
}
return queued;
}
/**
* Open a terminal for a computer.
*
* @param source The thing that executed this command.
* @param computer The computer to view.
* @return The constant {@code 1}.
*/
private static int view(CommandSourceStack source, ServerComputer computer) throws CommandSyntaxException {
var player = source.getPlayerOrException();
new ComputerContainerData(computer, ItemStack.EMPTY).open(player, new MenuProvider() {
@Override
public Component getDisplayName() {
return Component.translatable("gui.computercraft.view_computer");
}
@Override
public AbstractContainerMenu createMenu(int id, Inventory player, Player entity) {
return new ViewComputerMenu(id, player, computer);
}
});
return 1;
}
/**
* Start tracking metrics for the current player.
*
* @param source The thing that executed this command.
* @return The constant {@code 1}.
*/
private static int trackStart(CommandSourceStack source) {
getMetricsInstance(source).start();
var stopCommand = "/computercraft track stop";
source.sendSuccess(() -> Component.translatable(
"commands.computercraft.track.start.stop",
link(text(stopCommand), stopCommand, Component.translatable("commands.computercraft.track.stop.action"))
), false);
return 1;
}
/**
* Stop tracking metrics for the current player, displaying a table with the results.
*
* @param source The thing that executed this command.
* @return The constant {@code 1}.
*/
private static int trackStop(CommandSourceStack source) throws CommandSyntaxException {
var metrics = getMetricsInstance(source);
if (!metrics.stop()) throw NOT_TRACKING_EXCEPTION.create();
displayTimings(source, metrics.getSnapshot(), new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.AVG), DEFAULT_FIELDS);
return 1;
}
private static final List<AggregatedMetric> DEFAULT_FIELDS = List.of(
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.COUNT),
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.NONE),
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.AVG)
);
/**
* Display the latest metrics for the current player.
*
* @param source The thing that executed this command.
* @param fields The fields to display in this table, defaulting to {@link #DEFAULT_FIELDS}.
* @return The constant {@code 1}.
*/
private static int trackDump(CommandSourceStack source, List<AggregatedMetric> fields) throws CommandSyntaxException {
AggregatedMetric sort;
if (fields.size() == 1 && DEFAULT_FIELDS.contains(fields.get(0))) {
sort = fields.get(0);
fields = DEFAULT_FIELDS;
} else {
sort = fields.get(0);
}
return displayTimings(source, getMetricsInstance(source).getTimings(), sort, fields);
}
// Additional helper functions.
private static Component linkComputer(CommandSourceStack source, @Nullable ServerComputer serverComputer, int computerId) {
var out = Component.literal("");
@@ -323,16 +399,6 @@ public final class CommandComputerCraft {
return ServerContext.get(source.getServer()).metrics().getMetricsInstance(entity instanceof Player ? entity.getUUID() : SYSTEM_UUID);
}
private static final List<AggregatedMetric> DEFAULT_FIELDS = Arrays.asList(
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.COUNT),
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.NONE),
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.AVG)
);
private static int displayTimings(CommandSourceStack source, AggregatedMetric sortField, List<AggregatedMetric> fields) throws CommandSyntaxException {
return displayTimings(source, getMetricsInstance(source).getTimings(), sortField, fields);
}
private static int displayTimings(CommandSourceStack source, List<ComputerMetrics> timings, AggregatedMetric sortField, List<AggregatedMetric> fields) throws CommandSyntaxException {
if (timings.isEmpty()) throw NO_TIMINGS_EXCEPTION.create();

View File

@@ -18,9 +18,6 @@ public final class Exceptions {
static final SimpleCommandExceptionType NOT_TRACKING_EXCEPTION = translated("commands.computercraft.track.stop.not_enabled");
static final SimpleCommandExceptionType NO_TIMINGS_EXCEPTION = translated("commands.computercraft.track.dump.no_timings");
static final SimpleCommandExceptionType TP_NOT_THERE = translated("commands.computercraft.tp.not_there");
static final SimpleCommandExceptionType TP_NOT_PLAYER = translated("commands.computercraft.tp.not_player");
public static final SimpleCommandExceptionType ARGUMENT_EXPECTED = translated("argument.computercraft.argument_expected");
private static SimpleCommandExceptionType translated(String key) {

View File

@@ -32,7 +32,7 @@ public final class ComputersArgumentType implements ArgumentType<ComputersArgume
private static final ComputersArgumentType MANY = new ComputersArgumentType(false);
private static final ComputersArgumentType SOME = new ComputersArgumentType(true);
private static final List<String> EXAMPLES = Arrays.asList(
private static final List<String> EXAMPLES = List.of(
"0", "#0", "@Label", "~Advanced"
);
@@ -75,7 +75,7 @@ public final class ComputersArgumentType implements ArgumentType<ComputersArgume
var instance = reader.readInt();
computers = s -> {
var computer = ServerContext.get(s.getServer()).registry().get(instance);
return computer == null ? Collections.emptyList() : Collections.singletonList(computer);
return computer == null ? List.of() : List.of(computer);
};
}

View File

@@ -15,7 +15,6 @@ import net.minecraft.commands.CommandSourceStack;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -63,7 +62,7 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>> {
}
public <T> CommandNodeBuilder<S, ArgCommand<S, List<T>>> argManyValue(String name, ArgumentType<T> type, T defaultValue) {
return argManyValue(name, type, Collections.singletonList(defaultValue));
return argManyValue(name, type, List.of(defaultValue));
}
public <T> CommandNodeBuilder<S, ArgCommand<S, List<T>>> argMany(String name, ArgumentType<T> type, Supplier<List<T>> empty) {

View File

@@ -147,14 +147,14 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
@Override
public int run(CommandContext<CommandSourceStack> context) {
context.getSource().sendSuccess(getHelp(context, assertNonNull(node), id, command), false);
context.getSource().sendSuccess(() -> getHelp(context, assertNonNull(node), id, command), false);
return 0;
}
}
private static Command<CommandSourceStack> helpForChild(CommandNode<CommandSourceStack> node, String id, String command) {
return context -> {
context.getSource().sendSuccess(getHelp(context, node, id + "." + node.getName().replace('-', '_'), command + " " + node.getName()), false);
context.getSource().sendSuccess(() -> getHelp(context, node, id + "." + node.getName().replace('-', '_'), command + " " + node.getName()), false);
return 0;
};
}

View File

@@ -37,6 +37,6 @@ public class ServerTableFormatter implements TableFormatter {
@Override
public void writeLine(String label, Component component) {
source.sendSuccess(component, false);
source.sendSuccess(() -> component, false);
}
}

View File

@@ -18,11 +18,6 @@ public class DefaultBundledRedstoneProvider implements BundledRedstoneProvider {
public static int getDefaultBundledRedstoneOutput(Level world, BlockPos pos, Direction side) {
var block = world.getBlockState(pos).getBlock();
if (block instanceof IBundledRedstoneBlock generic) {
if (generic.getBundledRedstoneConnectivity(world, pos, side)) {
return generic.getBundledRedstoneOutput(world, pos, side);
}
}
return -1;
return block instanceof IBundledRedstoneBlock bundledBlock ? bundledBlock.getBundledRedstoneOutput(world, pos, side) : -1;
}
}

View File

@@ -9,7 +9,5 @@ import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
public interface IBundledRedstoneBlock {
boolean getBundledRedstoneConnectivity(Level world, BlockPos pos, Direction side);
int getBundledRedstoneOutput(Level world, BlockPos pos, Direction side);
}

View File

@@ -134,12 +134,12 @@ public class CommandAPI implements ILuaAPI {
public final List<String> list(IArguments args) throws LuaException {
var server = computer.getLevel().getServer();
if (server == null) return Collections.emptyList();
if (server == null) return List.of();
CommandNode<CommandSourceStack> node = server.getCommands().getDispatcher().getRoot();
for (var j = 0; j < args.count(); j++) {
var name = args.getString(j);
node = node.getChild(name);
if (!(node instanceof LiteralCommandNode)) return Collections.emptyList();
if (!(node instanceof LiteralCommandNode)) return List.of();
}
List<String> result = new ArrayList<>();

View File

@@ -32,7 +32,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
@@ -92,11 +92,6 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
return getDirectSignal(state, world, pos, incomingSide);
}
@Override
public boolean getBundledRedstoneConnectivity(Level world, BlockPos pos, Direction side) {
return true;
}
@Override
public int getBundledRedstoneOutput(Level world, BlockPos pos, Direction side) {
var entity = world.getBlockEntity(pos);
@@ -138,13 +133,12 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
var tile = world.getBlockEntity(pos);
if (tile instanceof AbstractComputerBlockEntity computer) {
var context = new LootContext.Builder(serverWorld)
.withRandom(world.random)
var context = new LootParams.Builder(serverWorld)
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos))
.withParameter(LootContextParams.TOOL, player.getMainHandItem())
.withParameter(LootContextParams.THIS_ENTITY, player)
.withParameter(LootContextParams.BLOCK_ENTITY, tile)
.withDynamicDrop(DROP, (ctx, out) -> out.accept(getItem(computer)));
.withDynamicDrop(DROP, out -> out.accept(getItem(computer)));
for (var item : state.getDrops(context)) {
popResource(world, pos, item);
}

View File

@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.platform.RegistryEntry;
import net.minecraft.world.level.block.GameMasterBlock;
import net.minecraft.world.level.block.entity.BlockEntityType;
/**
* A subclass of {@link ComputerBlock} which implements {@link GameMasterBlock}, to prevent players breaking it without
* permission.
*
* @param <T> The type of the computer block entity.
* @see dan200.computercraft.shared.computer.items.CommandComputerItem
*/
public class CommandComputerBlock<T extends CommandComputerBlockEntity> extends ComputerBlock<T> implements GameMasterBlock {
public CommandComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
super(settings, family, type);
}
}

View File

@@ -104,11 +104,15 @@ public class CommandComputerBlockEntity extends ComputerBlockEntity {
if (server == null || !server.isCommandBlockEnabled()) {
player.displayClientMessage(Component.translatable("advMode.notEnabled"), true);
return false;
} else if (Config.commandRequireCreative ? !player.canUseGameMasterBlocks() : !server.getPlayerList().isOp(player.getGameProfile())) {
} else if (!canUseCommandBlock(player)) {
player.displayClientMessage(Component.translatable("advMode.notAllowed"), true);
return false;
}
return true;
}
private static boolean canUseCommandBlock(Player player) {
return Config.commandRequireCreative ? player.canUseGameMasterBlocks() : player.hasPermissions(2);
}
}

View File

@@ -4,8 +4,33 @@
package dan200.computercraft.shared.computer.core;
public enum ComputerFamily {
NORMAL,
ADVANCED,
COMMAND
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import net.minecraft.util.GsonHelper;
import net.minecraft.util.StringRepresentable;
public enum ComputerFamily implements StringRepresentable {
NORMAL("normal"),
ADVANCED("advanced"),
COMMAND("command");
private final String name;
ComputerFamily(String name) {
this.name = name;
}
public static ComputerFamily getFamily(JsonObject json, String name) {
var familyName = GsonHelper.getAsString(json, name);
for (var family : values()) {
if (family.getSerializedName().equalsIgnoreCase(familyName)) return family;
}
throw new JsonSyntaxException("Unknown computer family '" + familyName + "' for field " + name);
}
@Override
public String getSerializedName() {
return name;
}
}

View File

@@ -21,6 +21,8 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static dan200.computercraft.api.filesystem.MountConstants.NO_SUCH_FILE;
/**
* A mount backed by Minecraft's {@link ResourceManager}.
*
@@ -29,8 +31,6 @@ import java.util.Map;
public final class ResourceMount extends ArchiveMount<ResourceMount.FileEntry> {
private static final Logger LOG = LoggerFactory.getLogger(ResourceMount.class);
private static final byte[] TEMP_BUFFER = new byte[8192];
/**
* Maintain a cache of currently loaded resource mounts. This cache is invalidated when currentManager changes.
*/
@@ -60,7 +60,7 @@ public final class ResourceMount extends ArchiveMount<ResourceMount.FileEntry> {
var hasAny = false;
String existingNamespace = null;
var newRoot = new FileEntry("", new ResourceLocation(namespace, subPath));
var newRoot = new FileEntry(new ResourceLocation(namespace, subPath));
for (var file : manager.listResources(subPath, s -> true).keySet()) {
existingNamespace = file.getNamespace();
@@ -68,7 +68,11 @@ public final class ResourceMount extends ArchiveMount<ResourceMount.FileEntry> {
if (!FileSystem.contains(subPath, file.getPath())) continue; // Some packs seem to include the parent?
var localPath = FileSystem.toLocal(file.getPath(), subPath);
create(newRoot, localPath);
try {
getOrCreateChild(newRoot, localPath, this::createEntry);
} catch (ResourceLocationException e) {
LOG.warn("Cannot create resource location for {} ({})", localPath, e.getMessage());
}
hasAny = true;
}
@@ -83,65 +87,24 @@ public final class ResourceMount extends ArchiveMount<ResourceMount.FileEntry> {
}
}
private void create(FileEntry lastEntry, String path) {
var lastIndex = 0;
while (lastIndex < path.length()) {
var nextIndex = path.indexOf('/', lastIndex);
if (nextIndex < 0) nextIndex = path.length();
var part = path.substring(lastIndex, nextIndex);
if (lastEntry.children == null) lastEntry.children = new HashMap<>();
var nextEntry = lastEntry.children.get(part);
if (nextEntry == null) {
ResourceLocation childPath;
try {
childPath = new ResourceLocation(namespace, subPath + "/" + path);
} catch (ResourceLocationException e) {
LOG.warn("Cannot create resource location for {} ({})", part, e.getMessage());
return;
}
lastEntry.children.put(part, nextEntry = new FileEntry(path, childPath));
}
lastEntry = nextEntry;
lastIndex = nextIndex + 1;
}
private FileEntry createEntry(String path) {
return new FileEntry(new ResourceLocation(namespace, subPath + "/" + path));
}
@Override
public long getSize(FileEntry file) {
protected byte[] getFileContents(String path, FileEntry file) throws IOException {
var resource = manager.getResource(file.identifier).orElse(null);
if (resource == null) return 0;
try (var stream = resource.open()) {
int total = 0, read = 0;
do {
total += read;
read = stream.read(TEMP_BUFFER);
} while (read > 0);
return total;
} catch (IOException e) {
return 0;
}
}
@Override
public byte[] getContents(FileEntry file) throws IOException {
var resource = manager.getResource(file.identifier).orElse(null);
if (resource == null) throw new FileOperationException(file.path, NO_SUCH_FILE);
if (resource == null) throw new FileOperationException(path, NO_SUCH_FILE);
try (var stream = resource.open()) {
return stream.readAllBytes();
}
}
protected static class FileEntry extends ArchiveMount.FileEntry<FileEntry> {
protected static final class FileEntry extends ArchiveMount.FileEntry<FileEntry> {
final ResourceLocation identifier;
FileEntry(String path, ResourceLocation identifier) {
super(path);
FileEntry(ResourceLocation identifier) {
this.identifier = identifier;
}
}

View File

@@ -64,13 +64,7 @@ public abstract class AbstractComputerItem extends BlockItem implements ICompute
@Override
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
var family = getFamily();
if (family != ComputerFamily.COMMAND) {
var id = getComputerID(stack);
if (id >= 0) {
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id, Config.computerSpaceLimit);
}
}
return null;
var id = getComputerID(stack);
return id >= 0 ? ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id, Config.computerSpaceLimit) : null;
}
}

View File

@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.computer.items;
import dan200.computercraft.api.filesystem.Mount;
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
/**
* A {@link ComputerItem} which prevents players placing it without permission.
*
* @see net.minecraft.world.item.GameMasterBlockItem
* @see dan200.computercraft.shared.computer.blocks.CommandComputerBlock
*/
public class CommandComputerItem extends ComputerItem {
public CommandComputerItem(ComputerBlock<?> block, Properties settings) {
super(block, settings);
}
@Override
protected @Nullable BlockState getPlacementState(BlockPlaceContext context) {
// Prohibit players placing this block in survival or when not opped.
var player = context.getPlayer();
return player != null && !player.canUseGameMasterBlocks() ? null : super.getPlacementState(context);
}
@Override
public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) {
// Don't allow command computers to be mounted in disk drives.
return null;
}
}

View File

@@ -4,7 +4,12 @@
package dan200.computercraft.shared.computer.menu;
import dan200.computercraft.shared.computer.upload.*;
import dan200.computercraft.core.apis.handles.ByteBufferChannel;
import dan200.computercraft.core.apis.transfer.TransferredFile;
import dan200.computercraft.core.apis.transfer.TransferredFiles;
import dan200.computercraft.shared.computer.upload.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.client.UploadResultMessage;
import dan200.computercraft.shared.platform.PlatformHelper;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
@@ -18,7 +23,6 @@ import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* The default concrete implementation of {@link ServerInputHandler}.
@@ -150,8 +154,14 @@ public class ServerInputState<T extends AbstractContainerMenu & ComputerMenu> im
}
}
computer.queueEvent("file_transfer", new Object[]{
new TransferredFiles(player, owner, toUpload.stream().map(x -> new TransferredFile(x.getName(), x.getBytes())).collect(Collectors.toList())),
computer.queueEvent(TransferredFiles.EVENT, new Object[]{
new TransferredFiles(
toUpload.stream().map(x -> new TransferredFile(x.getName(), new ByteBufferChannel(x.getBytes()))).toList(),
() -> {
if (player.isAlive() && player.containerMenu == owner) {
PlatformHelper.get().sendToPlayer(UploadResultMessage.consumed(owner), player);
}
}),
});
return UploadResultMessage.queued(owner);
}

View File

@@ -141,7 +141,7 @@ public final class ComputerMBean implements DynamicMBean, ComputerMetricsObserve
}
}
private static class Counter {
private static final class Counter {
final AtomicLong value = new AtomicLong();
final AtomicLong count = new AtomicLong();
}

View File

@@ -5,31 +5,20 @@
package dan200.computercraft.shared.computer.recipe;
import dan200.computercraft.shared.computer.items.IComputerItem;
import net.minecraft.core.NonNullList;
import dan200.computercraft.shared.recipe.CustomShapedRecipe;
import dan200.computercraft.shared.recipe.ShapedRecipeSpec;
import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.ShapedRecipe;
import net.minecraft.world.level.Level;
/**
* Represents a recipe which converts a computer from one form into another.
* A recipe which converts a computer from one form into another.
*/
public abstract class ComputerConvertRecipe extends ShapedRecipe {
private final String group;
private final ItemStack result;
public ComputerConvertRecipe(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result) {
super(identifier, group, category, width, height, ingredients, result);
this.group = group;
this.result = result;
}
public ItemStack getResultItem() {
return result;
public abstract class ComputerConvertRecipe extends CustomShapedRecipe {
public ComputerConvertRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe) {
super(identifier, recipe);
}
protected abstract ItemStack convert(IComputerItem item, ItemStack stack);
@@ -55,9 +44,4 @@ public abstract class ComputerConvertRecipe extends ShapedRecipe {
return ItemStack.EMPTY;
}
@Override
public String getGroup() {
return group;
}
}

View File

@@ -1,72 +0,0 @@
// SPDX-FileCopyrightText: 2018 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.computer.recipe;
import com.google.gson.JsonObject;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.util.RecipeUtil;
import net.minecraft.core.NonNullList;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
public abstract class ComputerFamilyRecipe extends ComputerConvertRecipe {
private final ComputerFamily family;
public ComputerFamilyRecipe(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
super(identifier, group, category, width, height, ingredients, result);
this.family = family;
}
public ComputerFamily getFamily() {
return family;
}
public abstract static class Serializer<T extends ComputerFamilyRecipe> implements RecipeSerializer<T> {
protected abstract T create(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family);
@Override
public T fromJson(ResourceLocation identifier, JsonObject json) {
var group = GsonHelper.getAsString(json, "group", "");
var category = CraftingBookCategory.CODEC.byName(GsonHelper.getAsString(json, "category", null), CraftingBookCategory.MISC);
var family = RecipeUtil.getFamily(json, "family");
var template = RecipeUtil.getTemplate(json);
var result = itemStackFromJson(GsonHelper.getAsJsonObject(json, "result"));
return create(identifier, group, category, template.width(), template.height(), template.ingredients(), result, family);
}
@Override
public T fromNetwork(ResourceLocation identifier, FriendlyByteBuf buf) {
var width = buf.readVarInt();
var height = buf.readVarInt();
var group = buf.readUtf();
var category = buf.readEnum(CraftingBookCategory.class);
var ingredients = NonNullList.withSize(width * height, Ingredient.EMPTY);
for (var i = 0; i < ingredients.size(); i++) ingredients.set(i, Ingredient.fromNetwork(buf));
var result = buf.readItem();
var family = buf.readEnum(ComputerFamily.class);
return create(identifier, group, category, width, height, ingredients, result, family);
}
@Override
public void toNetwork(FriendlyByteBuf buf, T recipe) {
buf.writeVarInt(recipe.getWidth());
buf.writeVarInt(recipe.getHeight());
buf.writeUtf(recipe.getGroup());
buf.writeEnum(recipe.category());
for (var ingredient : recipe.getIngredients()) ingredient.toNetwork(buf);
buf.writeItem(recipe.getResultItem());
buf.writeEnum(recipe.getFamily());
}
}
}

View File

@@ -4,35 +4,57 @@
package dan200.computercraft.shared.computer.recipe;
import com.google.gson.JsonObject;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.items.IComputerItem;
import net.minecraft.core.NonNullList;
import dan200.computercraft.shared.recipe.ShapedRecipeSpec;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
public final class ComputerUpgradeRecipe extends ComputerFamilyRecipe {
private ComputerUpgradeRecipe(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
super(identifier, group, category, width, height, ingredients, result, family);
/**
* A recipe which "upgrades" a {@linkplain IComputerItem computer}, converting it from one {@linkplain ComputerFamily
* family} to another.
*/
public final class ComputerUpgradeRecipe extends ComputerConvertRecipe {
private final ComputerFamily family;
private ComputerUpgradeRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe, ComputerFamily family) {
super(identifier, recipe);
this.family = family;
}
@Override
protected ItemStack convert(IComputerItem item, ItemStack stack) {
return item.withFamily(stack, getFamily());
return item.withFamily(stack, family);
}
@Override
public RecipeSerializer<?> getSerializer() {
public RecipeSerializer<ComputerUpgradeRecipe> getSerializer() {
return ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get();
}
public static class Serializer extends ComputerFamilyRecipe.Serializer<ComputerUpgradeRecipe> {
public static class Serializer implements RecipeSerializer<ComputerUpgradeRecipe> {
@Override
protected ComputerUpgradeRecipe create(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
return new ComputerUpgradeRecipe(identifier, group, category, width, height, ingredients, result, family);
public ComputerUpgradeRecipe fromJson(ResourceLocation identifier, JsonObject json) {
var recipe = ShapedRecipeSpec.fromJson(json);
var family = ComputerFamily.getFamily(json, "family");
return new ComputerUpgradeRecipe(identifier, recipe, family);
}
@Override
public ComputerUpgradeRecipe fromNetwork(ResourceLocation identifier, FriendlyByteBuf buf) {
var recipe = ShapedRecipeSpec.fromNetwork(buf);
var family = buf.readEnum(ComputerFamily.class);
return new ComputerUpgradeRecipe(identifier, recipe, family);
}
@Override
public void toNetwork(FriendlyByteBuf buf, ComputerUpgradeRecipe recipe) {
recipe.toSpec().toNetwork(buf);
buf.writeEnum(recipe.family);
}
}
}

View File

@@ -30,7 +30,6 @@ public final class ConfigSpec {
public static final ConfigFile.Value<Integer> computerSpaceLimit;
public static final ConfigFile.Value<Integer> floppySpaceLimit;
public static final ConfigFile.Value<Integer> maximumFilesOpen;
public static final ConfigFile.Value<Boolean> disableLua51Features;
public static final ConfigFile.Value<String> defaultComputerSettings;
public static final ConfigFile.Value<Boolean> logComputerErrors;
public static final ConfigFile.Value<Boolean> commandRequireCreative;
@@ -115,12 +114,6 @@ public final class ConfigSpec {
.comment("Set how many files a computer can have open at the same time. Set to 0 for unlimited.")
.defineInRange("maximum_open_files", CoreConfig.maximumFilesOpen, 0, Integer.MAX_VALUE);
disableLua51Features = builder
.comment("""
Set this to true to disable Lua 5.1 functions that will be removed in a future
update. Useful for ensuring forward compatibility of your programs now.""")
.define("disable_lua51_features", CoreConfig.disableLua51Features);
defaultComputerSettings = builder
.comment("""
A comma separated list of default system settings to set on new computers.
@@ -395,7 +388,6 @@ public final class ConfigSpec {
Config.floppySpaceLimit = floppySpaceLimit.get();
Config.uploadMaxSize = uploadMaxSize.get();
CoreConfig.maximumFilesOpen = maximumFilesOpen.get();
CoreConfig.disableLua51Features = disableLua51Features.get();
CoreConfig.defaultComputerSettings = defaultComputerSettings.get();
Config.commandRequireCreative = commandRequireCreative.get();

View File

@@ -12,7 +12,6 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
import java.util.Collections;
import java.util.Set;
/**
@@ -33,7 +32,7 @@ public final class BlockNamedEntityLootCondition implements LootItemCondition {
@Override
public Set<LootContextParam<?>> getReferencedContextParams() {
return Collections.singleton(LootContextParams.BLOCK_ENTITY);
return Set.of(LootContextParams.BLOCK_ENTITY);
}
@Override

View File

@@ -12,7 +12,6 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
import java.util.Collections;
import java.util.Set;
/**
@@ -33,7 +32,7 @@ public final class HasComputerIdLootCondition implements LootItemCondition {
@Override
public Set<LootContextParam<?>> getReferencedContextParams() {
return Collections.singleton(LootContextParams.BLOCK_ENTITY);
return Set.of(LootContextParams.BLOCK_ENTITY);
}
@Override

View File

@@ -12,7 +12,6 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
import java.util.Collections;
import java.util.Set;
/**
@@ -33,7 +32,7 @@ public final class PlayerCreativeLootCondition implements LootItemCondition {
@Override
public Set<LootContextParam<?>> getReferencedContextParams() {
return Collections.singleton(LootContextParams.THIS_ENTITY);
return Set.of(LootContextParams.THIS_ENTITY);
}
@Override

View File

@@ -61,7 +61,7 @@ public class ItemDetails {
/*
* Used to hide some data from ItemStack tooltip.
* @see https://minecraft.gamepedia.com/Tutorials/Command_NBT_tags
* @see https://minecraft.wiki/w/Tutorials/Command_NBT_tags
* @see ItemStack#getTooltip
*/
var hideFlags = tag != null ? tag.getInt("HideFlags") : 0;
@@ -116,6 +116,7 @@ public class ItemDetails {
* @param enchants The enchantment map to add it to.
* @see EnchantmentHelper
*/
@SuppressWarnings("NonApiType")
private static void addEnchantments(ListTag rawEnchants, ArrayList<Map<String, Object>> enchants) {
if (rawEnchants.isEmpty()) return;

View File

@@ -69,7 +69,7 @@ public abstract class PermissionRegistry {
.orElseGet(DefaultPermissionRegistry::new);
}
private static class DefaultPermissionRegistry extends PermissionRegistry {
private static final class DefaultPermissionRegistry extends PermissionRegistry {
@Override
public Predicate<CommandSourceStack> registerCommand(String command, UserLevel fallback) {
checkNotFrozen();

View File

@@ -9,14 +9,12 @@ import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.impl.PocketUpgrades;
import dan200.computercraft.impl.TurtleUpgrades;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.turtle.items.TurtleItem;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
@@ -24,7 +22,6 @@ import java.util.function.Supplier;
* Utilities for recipe mod plugins (such as JEI).
*/
public final class RecipeModHelpers {
static final List<ComputerFamily> MAIN_FAMILIES = Arrays.asList(ComputerFamily.NORMAL, ComputerFamily.ADVANCED);
static final List<Supplier<TurtleItem>> TURTLES = List.of(ModRegistry.Items.TURTLE_NORMAL, ModRegistry.Items.TURTLE_ADVANCED);
static final List<Supplier<PocketComputerItem>> POCKET_COMPUTERS = List.of(ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED);

View File

@@ -114,7 +114,7 @@ public class UpgradeRecipeGenerator<T> {
// Suggest possible upgrades which can be applied to this turtle
var left = item.getUpgradeWithData(stack, TurtleSide.LEFT);
var right = item.getUpgradeWithData(stack, TurtleSide.RIGHT);
if (left != null && right != null) return Collections.emptyList();
if (left != null && right != null) return List.of();
List<T> recipes = new ArrayList<>();
var ingredient = Ingredient.of(stack);
@@ -135,7 +135,7 @@ public class UpgradeRecipeGenerator<T> {
} else if (stack.getItem() instanceof PocketComputerItem) {
// Suggest possible upgrades which can be applied to this turtle
var back = PocketComputerItem.getUpgrade(stack);
if (back != null) return Collections.emptyList();
if (back != null) return List.of();
List<T> recipes = new ArrayList<>();
var ingredient = Ingredient.of(stack);
@@ -148,7 +148,7 @@ public class UpgradeRecipeGenerator<T> {
} else {
// If this item is usable as an upgrade, find all possible recipes.
var upgrades = upgradeItemLookup.get(stack.getItem());
if (upgrades == null) return Collections.emptyList();
if (upgrades == null) return List.of();
List<T> recipes = null;
var multiple = false;
@@ -169,7 +169,7 @@ public class UpgradeRecipeGenerator<T> {
}
}
return recipes == null ? Collections.emptyList() : Collections.unmodifiableList(recipes);
return recipes == null ? List.of() : Collections.unmodifiableList(recipes);
}
}
@@ -215,7 +215,7 @@ public class UpgradeRecipeGenerator<T> {
return Collections.unmodifiableList(recipes);
} else {
return Collections.emptyList();
return List.of();
}
}

View File

@@ -22,7 +22,7 @@ import mezz.jei.api.runtime.IJeiRuntime;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import java.util.Collections;
import java.util.List;
@JeiPlugin
public class JEIComputerCraft implements IModPlugin {
@@ -61,7 +61,7 @@ public class JEIComputerCraft implements IModPlugin {
var category = registry.createRecipeLookup(RecipeTypes.CRAFTING);
category.get().forEach(wrapper -> {
if (RecipeModHelpers.shouldRemoveRecipe(wrapper.getId())) {
registry.hideRecipes(RecipeTypes.CRAFTING, Collections.singleton(wrapper));
registry.hideRecipes(RecipeTypes.CRAFTING, List.of(wrapper));
}
});
}

View File

@@ -15,7 +15,6 @@ import mezz.jei.api.recipe.category.IRecipeCategory;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingRecipe;
import java.util.Collections;
import java.util.List;
class RecipeResolver implements IRecipeManagerPlugin {
@@ -24,36 +23,36 @@ class RecipeResolver implements IRecipeManagerPlugin {
@Override
public <V> List<RecipeType<?>> getRecipeTypes(IFocus<V> focus) {
var value = focus.getTypedValue().getIngredient();
if (!(value instanceof ItemStack stack)) return Collections.emptyList();
if (!(value instanceof ItemStack stack)) return List.of();
return switch (focus.getRole()) {
case INPUT ->
stack.getItem() instanceof TurtleItem || stack.getItem() instanceof PocketComputerItem || resolver.isUpgrade(stack)
? Collections.singletonList(RecipeTypes.CRAFTING)
: Collections.emptyList();
? List.of(RecipeTypes.CRAFTING)
: List.of();
case OUTPUT -> stack.getItem() instanceof TurtleItem || stack.getItem() instanceof PocketComputerItem
? Collections.singletonList(RecipeTypes.CRAFTING)
: Collections.emptyList();
default -> Collections.emptyList();
? List.of(RecipeTypes.CRAFTING)
: List.of();
default -> List.of();
};
}
@Override
public <T, V> List<T> getRecipes(IRecipeCategory<T> recipeCategory, IFocus<V> focus) {
if (!(focus.getTypedValue().getIngredient() instanceof ItemStack stack) || recipeCategory.getRecipeType() != RecipeTypes.CRAFTING) {
return Collections.emptyList();
return List.of();
}
return switch (focus.getRole()) {
case INPUT -> cast(resolver.findRecipesWithInput(stack));
case OUTPUT -> cast(resolver.findRecipesWithOutput(stack));
default -> Collections.emptyList();
default -> List.of();
};
}
@Override
public <T> List<T> getRecipes(IRecipeCategory<T> recipeCategory) {
return Collections.emptyList();
return List.of();
}
@SuppressWarnings({ "unchecked", "rawtypes" })

View File

@@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicReference;
public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
private static final String NBT_ITEM = "Item";
private static class MountInfo {
private static final class MountInfo {
@Nullable
String mountPath;
}

View File

@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.peripheral.generic;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nullable;
/**
* Extract some component (for instance a capability on Forge, or a {@code BlockApiLookup} on Fabric) from a block and
* block entity.
*
* @param <C> A platform-specific type, used for the invalidation callback.
*/
public interface ComponentLookup<C extends Runnable> {
/**
* Extract some component from a block in the world.
*
* @param level The current level.
* @param pos The position of the block in the level.
* @param state The block state at that position.
* @param blockEntity The block entity at that position.
* @param side The side of the block to extract the component from. Implementations should try to use a
* sideless lookup first, but may fall back to a sided lookup if needed.
* @param invalidate An invalidation function to call if this component changes.
* @return The found component, or {@code null} if not present.
*/
@Nullable
Object find(Level level, BlockPos pos, BlockState state, BlockEntity blockEntity, Direction side, C invalidate);
}

Some files were not shown because too many files have changed in this diff Show More