Commit Graph
32 Commits
Author SHA1 Message Date
Jonathan Coates 4cd6cdb69c Enable JSpecify support in NullAway
Except in the common main classes: there's a couple of bugs in the
current version which crash the type checker.
2026-10-19 20:15:53 +01:00
Jonathan Coates be75b7357f Ensure we have @NullMarked package-info files
- Add a new task to check/generate package-info files.
 - Add missing ones, and write some basic package summaries where
   appropriate.
2026-10-19 20:06:09 +01:00
Jonathan Coates 34631dd410 Centralise HTTP URL parsing
- Moves us a bit closer to a parse-don't-validate model which is
   always nice.

 - Fixes some nullability issues: URI.getHost and URI.getScheme are
   *technically* nullable. We reject this case within our parser, but
   that's obviously not propagated to other functions.
2026-10-19 16:32:53 +01:00
Jonathan Coates 832800babe Change Resource to explicitly encode a state machine
- Add a new LockedReference class that models Rusts' Mutex class,
   making the @GuardedBy and a ReentrantLock pattern a little less
   verbose.

   Of course, we don't get any of the lifetime checking here. Gosh, I
   miss Rust's ownership model so much when writing concurrent code.

 - Rewrite Resource to work with explicit (immutable) states, and
   transitions between them. Each transition runs within the mutex
   (rather than using atomics), so it's slightly easier to reason about
   possible behaviours.

 - Update our various resources (HTTPRequest, Websocket, CheckURL) to
   use this new state machine system.

 - Merge ResourceQueue and ResourceGroup into one class.
2026-10-09 18:35:17 +01:00
Jonathan Coates 8730963ea8 Detect cycles in locks
- Add a new ThreadUtils.newReentrantLock/newReentrantReadWriteLock
   functions. These use Guava's CycleDetectingLockFactory to create a
   cycle-detecting lock in a development environment.

 - Rename ForbiddenImports to ForbiddenAPIs, and forbid constructing
   these instances directly, requiring you to go through ThreadUtils.
2026-10-09 14:39:02 +01:00
Jonathan Coates 5f69f7acd6 Fix several bugs with websocket closing
- Correctly send a close websocket frame when a websocket is manually
   closed (status code 1000) or is garbage collected (1001). Computer
   shutdowns still just close the socket, as we don't/can't wait for the
   socket to cleanly shutdown.

 - After a user calls close(), don't mark the websocket as closed until
   we receive the server's close response. This means we now have a
   slightly awkward interstitial state where we can receive messages but
   not send them, but I don't think there's much we can do about that.

Fixes #2096
2026-09-29 20:52:10 +01:00
Jonathan Coates 5dcbdced10 Replace ResourceQueue with a Cleaner
A bit simpler, means we don't have to poll it every tick.

I had one test failure in the websocket code where the websocket was
GCed and closed too early, so adding some reference fences in here. I've
not been able to reproduce this, so fingers crossed it works.
2026-09-29 16:59:14 +01:00
Jonathan Coates 7c60ed109f Abstract our HTTP implementation away
Add a new HttpHandler interface, which handles checkUrl, request and
websocket calls for a computer. The default implementation is provided
by a NettyHttp class, which also stores any related HTTP config and
state.

This is a little over-engineered, but has two advantages:
 - Our Javascript/TeaVM implementation can now just implement this
   interface, rather than patching/overwriting classes.
 - This removes the final (large) bits of global state/config from
   CC:T's core. Our tests no longer need to temporarily override global
   address rules, and the traffic shaping handler no longer requires
   resetting.

It might be a bit nicer to move all the netty-specific code into a
dan200.computercraft.apis.http.netty package, so there's some cleaner
separation from the common code, but I'm trying to avoid moving code
about any more than I have to.
2026-09-24 15:31:57 +01:00
Jonathan Coates 14e8dc93f0 Make ComputerContext ctor's public
Within our TeaVM code we currently do some class rewriting magic to
replace individual classes, such as ComputerThread. However, all our
code is written to allow changing these implementations already, it's
just TeaVM isn't smart enough to look through our builder and realise
it's dead code!

We now make the ComputerContext's constructor public (though
deprecated), which allows our TeaVM code to call it directly.
2026-09-24 12:51:19 +01:00
Jonathan Coates 16ede90191 Restrict GlobalEnvironment for the actual files we use
These technically supported any mod ID and file, but in practice only
ever were used for the Lua ROM and bios.
2026-09-24 10:32:05 +01:00
Jonathan Coates a4d172bcbe Gradle cleanup/overhaul.
- Fix a whole bunch of ErrorProne warnings. It's still quite noisy, but
   most of the remaining ones now come from Java.

 - Move some config from java-conventions to a new base-conventions,
   which can be used for non-Java projects. As part of this, we now kill
   the CCTweaked plugin, and just install the cct extension in
   base-conventions instead.

 - Kill inlineProject/externalSources. We now:
    - Add an embeddedProject configuration which is used to define
      projects that are embedded within the final jar. We do some fun
      variant resolution in ExternalProjectArtifacts to extract the
      class/resource/source directories from the configuration.
    - Add a new verifyClassesMatch task which recompiles the game and
      ensures compiling everything against the loader's version of
      Minecraft gives us the same result.
2026-09-11 20:10:58 +01:00
Jonathan Coates 3ecc17ec90 Update dependencies
- Update Loom — required for MC 26.2.
 - Update TeaVM, using the new reflection API.
2026-06-15 20:33:55 +01:00
Jonathan Coates 4b1541154b Add Websocket.getResponseHeaders method
Closes #1387
2025-12-17 23:11:12 +00:00
Jonathan Coates 6702ab8f9d Enable TeaVM WASM GC Backend
I'd tried this ages ago, and had some issues I utterly failed to find a
minimal reproducer for. Now it Just Works.
2025-12-15 14:17:03 +00:00
Jonathan Coates 16d934265a Update TeaVM version
We no longer require any patches against TeaVM. Still shipping our own
for now, until 0.14 is stable.

<3 konsoletyper
2025-12-15 14:07:11 +00:00
Jonathan Coates 569de7fafb Update to Gradle 9.2
- Update to Loom 1.12. This requires Java 21, so we now build with Java
   21, but targetting 17. The new "-release" flag does make this much
   easier, so hopefully shouldn't cause too many issues.

 - Bump versions of a lot of other things.

 - Fix various errorprone/checkstyle grumbles.
2025-10-31 19:50:44 +00:00
Jonathan Coates 0998acaa82 Switch to JSpecify annotations
Now, hear me out, what if instead of having three @Nullable annotations,
we had *four*?

I've been wanting to switch away from javax.annoations for a while. The
library has been deprecated for ever and, unlike other @Nullable
annotations, the annotation is attached to the parameter/function
itself, rather than the type.

We use JSpecify rather than one of the alternatives (JetBrains,
CheckerFramework) mostly because it's what NullAway recommends. We keep
CheckerFramework around for @DefaultQualifier, and JB's for @Contract.

There are some ugly changes here — for instance, `@Nullable byte[]` is
replace by `byte @Nullable`, and `@Nullable ILuaMachine.Factory` is
`ILuaMachine.@Nullable Factory`. Ughr, I understand why, but it does not
spark joy :).
2025-02-16 18:09:15 +00:00
Jonathan Coates 4f66ac79d3 Add redstone relay block (#2002)
- Move redstone methods out of the IAPIEnvironment, and into a new
   RedstoneAccess. We similarly move the implementation from Environment
   into a new RedstoneState class.

   The interface is possibly a little redundant (interfaces with a
   single implementation are always a little suspect), but it's nice to
   keep the consumer/producer interfaces separate.

 - Abstract most redstone API methods into a separate shared class, that
   can be used by both the rs API and the new redstone relay.

 - Add the new redstone relay block.

The docs are probably a little lacking here, but I really struggled to
write anything which wasn't just "look, it's the same as the redstone
API".
2024-11-12 09:05:27 +00:00
Jonathan Coates ffb62dfa02 Bump checkstyle, fix warnings from TeaVM upgrade 2024-03-13 21:52:09 +00:00
Jonathan Coates d38b1da974 Don't propagate redstone when blink/label changes
Historically, computers tracked whether any world-visible state
(on/off/blinking, label and redstone outputs) had changed with a single
"has changed" flag. While this is simple to use, this has the curious
side effect of that term.setCursorBlink() or os.setComputerLabel() would
cause a block update!

This isn't really a problem in practice - it just means slightly more
block updates. However, the redstone propagation sometimes causes the
computer to invalidate/recheck peripherals, which masks several other
(yet unfixed) bugs.
2024-03-06 18:59:38 +00:00
Jonathan Coates 84a799d27a Add abstract classes for our generic peripherals
This commit adds abstract classes to describe the interface for our
mod-loader-specific generic peripherals (inventories, fluid storage,
item storage).

This offers several advantages:
 - Javadoc to illuaminate conversion no longer needs the Forge project
   (just core and common).

 - Ensures we have a consistent interface between Forge and Fabric.

Note, this does /not/ implement fluid or energy storage for Fabric. We
probably could do fluid without issue, but not something worth doing
right now.
2023-11-22 18:20:15 +00:00
Jonathan Coates f8b7422294 Fix several issues with the web emulator
- Bump TeaVM version to fix issues with locales and our "export"
   generation.
 - Fix TComputerThread not requeuing correctly.
2023-11-15 13:12:31 +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 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 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 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 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 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 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 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 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