While this does now involve a little more indirection, by having a
single location for all hooks it's much easier to keep event listeners
consistent between loaders.
The diff is pretty noisy (I've inlined some classes, and ClientRegistry
got a big restructure), but functionality should be the same.
Previously we overrode getCapability on our BlockEntity implementations.
While this is the Proper way to do things, it's obviously impossible to
do in a multi-loader environment.
We now subscribe to the AttachCapabilitiesEvent and add our caps that
way. This does require[^1] some nasty invalidation of caps in a couple
of places, which I'm not wild about.
[^1]: I'm not actually sure it does: we invalidate peripherals and wired
elements when neighbours change, so the explicit invalidation probably
isn't useful.
NetworkMessage follows vanilla's Packet much more closely now: the
handler takes a context argument, which varies between the client and
server.
- The server handler just returns the ServerPlayer who sent the
message.
- The client context provides handleXyz(...) methods for each
clientbound packet. Our packet subclasses call the appropriate
method - the implementation of which contains the actual
implementation.
Doing this allows us to move a whole bunch of client-specific code into
the main client package, dropping several @OnlyIn annotations and
generally reducing the risk of accidentally loading client classes on
the server.
We also abstract out some packet sending and general networking code to
make it easier to use in a multi-loader context.
- Replace our usages of DeferredRegistry with a custom
RegistrationHelper. This effectively behaves the same, just without
any references to Forge code!
- Replace any references to ForgeRegistries with a home-grown
Registries class. This just fetches the underlying registry and
proxies the method calls.
- Change recipe serialisers and loot item conditions to be registered
the same way as other entries.
- Move any Forge specific registration code off to the main
ComputerCraft class.
This adds two new modules: common-api and forge-api, which contain the
common and Forge-specific interfaces for CC's Minecraft-specific API.
We add a new PlatformHelper interface, which abstracts over some of the
loader-specific functionality, such as reading registries[^1] or calling
Forge-specific methods. This interface is then implemented in the main
mod, and loaded via ServiceLoaders.
Some other notes on this:
- We now split shared and client-specific source code into separate
modules. This is to make it harder to reference client code on the
server, thus crashing the game.
Eventually we'll split the main mod up too into separate source sets
- this is, of course, a much bigger problem!
- There's currently some nastiness here due to wanting to preserve
binary compatibility of the API. We'll hopefully be able to remove
this when 1.19.3 releases.
- In order to build a separate Forge-specific API jar, we compile the
common sources twice: once for the common jar and once for the Forge
jar.
Getting this to play nicely with IDEs is a little tricky and so we
provide a cct.inlineProject(...) helper to handle everything.
[^1]: We /can/ do this with vanilla's APIs, but it gives a lot of
deprecation warnings. It just ends up being nicer to abstract over it.
We'll do this everywhere eventually, but much easier to do it
incrementally:
- Use checker framework to default all field/methods/parameters to
@Nonnull.
- Start using ErrorProne[1] and NullAway[2] to check for possible null
pointer issues. I did look into using CheckerFramework, but it's much
stricter (i.e. it's actually Correct). This is technically good, but
is a much steeper migration path, which I'm not sure we're prepared
for yet!
[1]: https://github.com/google/error-prone
[2]: https://github.com/uber/NullAway
It should be possible to consume the ComputerCraft's core (i.e.
non-Minecraft code) in other projects, such as emulators. While this
has been possible for years, it's somewhat tricky from a maintenance
perspective - it's very easy to accidentally add an MC dependency
somewhere!
By publishing a separate "core" jar, we can better distinguish the
boundaries between our Lua runtime and the Minecraft-specific code.
Ideally we could have one core project (rather than separate core and
core-api modules), and publish a separate "api" jar, like we do for the
main mod. However, this isn't really possible to express using Maven
dependencies, and so we must resort to this system.
Of course, this is kinda what the Java module system is meant to solve,
but unfortunately getting that working with Minecraft is infeasible.
- Move core-specific config options to a separate CoreConfig class.
- Use class-specific loggers, instead of a global one.
- Use log markers instead of a logComputerErrors option.
- Switch to a fairly standard code format. This is largely based on
IntelliJ defaults, with some minor tweaks applied via editor config.
Should mean people don't need to import a config!
- Use "var" everywhere instead of explicit types. Type inference is a
joy, and I intend to use it to its fullest.
- Start using switch expressions: we couldn't use them before because
IntelliJ does silly things with our previous brace style, but now we
have the luxury of them!
- Switch to using OptionalInt/OptionalLong instead of @Nullable
Long/Integers. I know IntelliJ complains, but it avoids the risk of
implicit unboxing.
- Instead of mutating PartialOptions, we now define a merge() function
which returns the new options. This simplifies the logic in
AddressRule a whole bunch.
This is a noisier diff than I'd like as this is just a direct copy from
the multi-loader branch.
- Rename "ingame" package to "gametest"
- Don't chain GameTestSequence methods - it's actually much cleaner if
we just use Kotlin's implicit this syntax.
- Use our work in 71f81e1201 to write
computer tests using Kotlin instead of Lua. This means all the logic
is in one place, which is nice!
- Add a couple more tests for some of the more error-prone bits of
functionality.
This is an initial step before refactoring this into a separate module.
It's definitely not complete - there's a lot of work needed to remove
referneces to the main ComputerCraft class for instance - but is a
useful first step.
I still don't really understand why the ROOT locale is wrong here, but
there we go. We'll need to remember to uncomment the tests on the 1.18
branch!
Also add some code to map tests back to their definition side. Alas,
this only links to the file right now, not the correct line :/.
This offers very few advantages now, but helps support the following in
the future:
- Reuse test support code across multiple projects (useful for
multi-loader).
- Allow using test fixture code in testMod. We've got a version of our
gametest which use Kotlin instead of Lua for asserting computer
behaviour.
We can't use java-test-fixtures here for Forge reasons, so have to roll
our own version. Alas.
- Add an ILuaMachine implementation which runs Kotlin coroutines
instead. We can use this for testing asynchronous APIs. This also
replaces the FakeComputerManager.
- Move most things in the .support module to .test.core. We need to use
a separate package in order to cope with Java 9 modules (again,
thanks Forge).
This allows other mods to create wired-modem alike blocks, which expose
peripherals on the wired network, without having to reimplement the main
modem interface.
This is not currently documented, but a peripheral_hub should provide
the following methods:
- isPresentRemote
- getTypeRemote
- hasTypeRemote
- getMethodsRemote
- callRemote
- Add a new file_transfer event. This has the signature
"file_transfer", TransferredFiles.
TransferredFiles has a single method getFiles(), which returns a list
of all transferred files.
- Add a new "import" program which waits for a file_transfer event and
writes files to the current directory.
- If a file_transfer event is not handled (i.e. its getFiles() method
is not called) within 5 seconds on the client, we display a toast
informing the user on how to upload a file.