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

Compare commits

...

132 Commits

Author SHA1 Message Date
Jonathan Coates
08d4f91c8b Bump CC:T to 1.112.0 2024-07-31 07:05:08 +01:00
Jonathan Coates
b9eac4e509 Computer components (#1915)
This adds a new mechanism for attaching additional objects to a
computer, allowing them to be queried by other mods. This is primarily
designed for mods which add external APIs, allowing them to add APIs
which depend on the computer's position or can interact with the turtle
inventory.

I will stress that the use-cases for custom APIs are few and far
between. Almost all the time a peripheral would be the better option,
and I am wary that this PR will encourage misuse of APIs. However, there
are some legitimate use-cases, and I think we should enable them.

 - Add a new "ComputerComponent" class, and several built-in components
   (for turtle, pocket and command computers).

 - Add a method to `IComputerSystem` to read a component from the
   computer. We also add methods to get the level and position of the
   computer.

 - Move all our existing APIs (built-in turtle, pocket, command) to use
   the public API.
2024-07-31 06:57:38 +01:00
Jonathan Coates
dc3d8ea198 Move API factories to the common package
We don't actually use this functionality in other projects (e.g.
emulators). In fact the method to add new APIs only exists in the mod
itself!

We still need some mechanism to remove mounts when the computer is
shutdown. We add a new ApiLifecycle interface (with startup and
shutdown hooks), and use those in the ComputerSystem impl.
2024-07-29 19:46:25 +01:00
Jonathan Coates
cbe075b001 Expose level+position in IPocketAccess
This allows pocket upgrades (modems and speakers) to read the position
directly, rather than checking whether the entity is present.
2024-07-28 21:15:25 +01:00
Jonathan Coates
ed0b156e05 Attempt at splitting up pocket computer logic
Oh, I hate the pocket computer code so much. Minecraft was really not
designed to attach this sort of behaviour to computers. This commit is
an attempt of cleaning this up[^1].

Firstly, we move the the pocket computer state (upgrades, light) out of
PocketServerComputer and into a new PocketBrain class. This now acts as
the sole source-of-truth, with all state being synced back to the
original item stack on the entity tick.

This also adds a new PocketHolder interface, which generalises over the
various types that can hold a pocket computer (players and item
entities right now, possibly lecterns in the future).

[^1]: I'd say simplifying, but this would be a lie.
2024-07-28 21:13:07 +01:00
Jonathan Coates
4dd0735066 Register modems as attached to their adjacent block
In c8eadf4011 we marked our various modems
as "brittle", which ensures they do not pop-off computers when the whole
structure moves.

However, this still requires the modem to be glued — if the modem is
outside the superglue range, it will still pop off. We can fix it by
registering a special "attached check" for the various modem blocks,
which says that the modem should be moved when the adjacent block does.

Fixes #1913
2024-07-26 18:28:13 +01:00
Jonathan Coates
38e516d7c7 Update to Reuse 4.0 2024-07-26 18:28:00 +01:00
Jonathan Coates
70a31855ac Only send pocket computer updates to players in range
Previously we sent it to all players in the current level. This updates
the check to only send it to players tracking the current chunk.
2024-07-26 10:07:26 +01:00
Jonathan Coates
6c8e64ffcd Add get/setUpgrade to IPocketAccess
We have similar methods in ITurtleAccess, so makes sense for them to be
here too.
2024-07-26 09:51:09 +01:00
Jonathan Coates
7285c32d58 Document that the speaker re-encoded audio samples 2024-07-25 20:32:08 +01:00
Jonathan Coates
99c60ac54b Remove IComputerBlockEntity
We only really made use of it in the has_computer_id loot condition, so
probably easier to remove it.
2024-07-25 09:28:00 +01:00
Jonathan Coates
63e40cf3cb Add a cc.strings.split method
This is largely copied from metis, with the documentation updated.
2024-07-24 22:18:50 +01:00
Jonathan Coates
1d45935a25 Update links to IRC
The EsperNet webchat has been decommissioned, with no plans to
replace. KiwiIRC is the recommended replacement, but I'm not comfortable
using it as a drop-in replacement[^1], so I've rephrased this section to
make it more clear.

[^1]: There seems to be ongoing issues with TLS certificates
  (https://github.com/kiwiirc/kiwiirc/issues/1870), which meant it
  wasn't usable when I first tried.
2024-07-24 21:38:14 +01:00
Jonathan Coates
f80373e7a2 Add bounds check to cc.strings.wrap
Fixes #1905, closes #1906.

Co-authored-by: Lupus590 <lupussolitarius590@gmail.com>
2024-07-24 19:40:10 +01:00
Jonathan Coates
63185629b7 Use "require" in textutils
This avoids us having to support requireless environments inside
cc.strings.

I do kinda wonder if os.loadAPI-loaded files should also have their own
shared "require", just so we're not loading 10 copies of cc.expect.
2024-07-24 19:40:10 +01:00
Jonathan Coates
4bfb9ac323 Fix/update language checker script
- Update location of the generated language file to point to common
   rather than Fabric.
 - Remove usage of OrderedDict, as dicts are ordered on recent versions
   of Python.
2024-07-24 19:40:09 +01:00
Jonathan Coates
5926b6c994 Update Gradle to 8.9
Fix several deprecation warnings, and specify the toolchain required to
launch the daemon.
2024-07-24 19:40:09 +01:00
Jonathan Coates
f5ed43584d Add tests for turtle equipping and crafting 2024-07-24 19:40:09 +01:00
csqrb
d77f5f135f Preserve item data when upgrading pocket computers (#1888) 2024-07-03 07:21:02 +00:00
Jonathan Coates
7744d2663b Fix heights of turtle flags
They were 0.5 squares too high, so the textures were a little stretched.
2024-06-27 20:41:42 +01:00
Jonathan Coates
4566cb8273 Add path-based error constructor to FileSystemException
This doesn't change any functionality, but means we only construct
"/{path}: {message}" strings in one location.
2024-06-26 21:12:44 +01:00
Jonathan Coates
052e7a7ae5 Make FileSystem.toLocal private
Use a custom to-local function in the various ArchiveMounts, which don't
faff around with sanitising paths.
2024-06-26 21:12:05 +01:00
Jonathan Coates
0895200681 Small bits of cleanup
Build system:
 - Switch to our new maven server. This has a cleaner separation between
   published packages and mirrored packages, to avoid leaking those into
   other people's builds.
 - Update Gradle and Loom versions.

Code:
 - Link to definitions instead in the breaking changes page.
 - Fix several unused variable warnings.

Other:
 - Remove unsupported Minecraft versions from the issue template.
2024-06-26 18:07:57 +01:00
Jonathan Coates
09d0f563b7 Add 1.21 to version list 2024-06-23 09:14:32 +01:00
Jonathan Coates
e188f1d3fa Link to os.time in os.setAlarm docs 2024-06-23 08:43:42 +01:00
Jonathan Coates
819a4f7231 Hide a few ServerComputer internals 2024-06-22 18:48:31 +01:00
Jonathan Coates
898cb2a95d Remove CommandComputerBlockEntity
Due to the earlier commits, the only functionality this block entity
adds is to register the command API. This commit:

 - Add the command API when constructing the ServerComputer instead.
   This is not a good long-term solution (I think we need to make API
   factories more powerful), but is sufficient for now.

 - Replace usages of CommandComputerBlockEntity with a normal
   ComputerBlockEntity.
2024-06-22 18:19:00 +01:00
Jonathan Coates
03a8f83191 Unify command computer permission checks
- Move the command permisssion checks to a new
   ComputerFamily.checkUsable method (from
   CommandComputerBlockEntity and ViewComputerMenu). I don't feel great
   about putting new functionality in ComputerFamily (trying to move
   away from it), but I think this is fine for now.

 - Use this method from within the computer menu and computer block, to
   check whether computers can be interacted with.

 - Remove ViewComputerMenu, as it now no longer needs any special
   is-usable logic.
2024-06-22 18:08:04 +01:00
Jonathan Coates
aef92c8ebc Remove pocket computer GUI
Historically we used to have separate menu types for computers and
pocket computers, as the screen had to be initialised with the correct
terminal size.

However, as of c49547b962 (which was
admittedly two years ago now), we have the terminal available when
constructing the screen, and so the code for the two is identical.

This change actually merges the two screens, replacing usages of the
pocket computer UI with the computer one.
2024-06-22 17:41:49 +01:00
Jonathan Coates
571ea794a8 Decouple CommandAPI from the command computer BE
All we really need for its implementation is a level and position, which
we can get directly from the server block entity!
2024-06-22 17:18:21 +01:00
Jonathan Coates
e81af93043 Move JEI to client folder
I hadn't realised this, but plugins are only loaded on the client. This
is useful on 1.21, as we now have easy access to a holder lookup.
2024-06-22 13:23:03 +01:00
Jonathan Coates
25b8a65c5c Fix drive.getAudioTitle returning null for no disk
Historically (and according to the docs) getAudioTitle returned "false"
when the drive was empty (or had invalid media), and "null" when the
disk had no item. This was accidentally changed in a later refactor --
this change fixes that behaviour.
2024-06-21 07:54:18 +01:00
Jonathan Coates
e4236824d7 Revert "Changed Heart Character (-3 pixels)"
This reverts commit d9b0cc7075.

I'm not sure what happened here, but the font is just entirely offset.
This is on me for not noticing during review. 🤦
2024-06-20 19:03:17 +01:00
Jonathan Coates
cfd11ffa92 Support dark and light mode logos on the website
Requires an illuaminate bump to support <picture> tags.

Fixes #1861.
2024-06-20 18:32:01 +01:00
Jonathan Coates
ce133a5e66 Update Minecraft wiki link
We didn't update this at the same time as the other links, as it pointed
to an even older wiki URL!
2024-06-19 22:01:51 +01:00
Jonathan Coates
038fbc1ed1 Merge pull request #1823 from Bluerella/mc-1.20.x
Changed Heart Character (-3 pixels)
2024-06-19 21:41:55 +01:00
Weblate
c582fb521c Translations for German
Co-authored-by: Lord Duck <maximilian.schueller@hotmail.com>
2024-06-19 14:52:42 +00:00
Jonathan Coates
af21792844 Publish docs via an artifact instead
We'll deploy this via a webhook instead.
2024-06-19 09:57:56 +01:00
Jonathan Coates
9fbb1070ef Remove redundant helper method from datagen
Vanilla has had an equivalent method for a few years now!
2024-06-14 22:12:16 +01:00
Jonathan Coates
1944995c33 Update CCF links to my mirror 2024-06-11 20:27:06 +01:00
Jonathan Coates
ac851a795b Fix command.getBlockInfos indexing expression
And add an example, to make it all a little clearer
2024-06-06 20:04:32 +01:00
Weblate
334761788a Translations for Chinese (Simplified)
Co-authored-by: Kevin Z <zyxkad@gmail.com>
2024-06-05 18:24:44 +00:00
Jonathan Coates
5af3e15dd5 Nicer lexer error for "!" 2024-05-28 20:16:32 +01:00
Jonathan Coates
209b1ddbf9 Bump CC:T to 1.111.0 2024-05-28 18:19:13 +01:00
Jonathan Coates
0c9f9a8652 Warn when Optifine is installed
We keep getting bug reports on 1.20.1 about an Optifine bug that causes
Forge's capabilities to not work (#1458). The cause of this bug is not
immediately visible to users, and can be very confusing when hit.

Optifine have not released a fix for this bug (despite it being reported
a year ago), and we continue to receive bug reports about it.

Nobody likes it when mods complain about other mods. So much Minecraft
drama can be traced back to this, and it's a slippery slope to go down.
I've tried to keep this as unobtrusive as possible — it's just a chat
message at world join, and it'll turn off if the bug is fixed.
2024-05-28 18:10:50 +01:00
Jonathan Coates
862d92785e Don't expose a menu provider for computers
We can't safely use this anyway (as custom data is not sent), so better
not to expose it at all. Fixes #1844.
2024-05-28 09:47:12 +01:00
Jonathan Coates
d48b85d50c Add r+/w+ support to io library 2024-05-26 10:16:33 +01:00
Jonathan Coates
4d619de357 Don't publish Gradle module metadata for common
Fixes #1842
2024-05-26 09:34:10 +01:00
Daniel Ratcliffe
57c289f173 Allow planks to be used for building in "adventure" 2024-05-25 10:04:45 +01:00
Jonathan Coates
f63f85921f Fix missing quotes in the settings example 2024-05-19 08:38:41 +01:00
Jonathan Coates
c7e49d1929 Use RecordItem.getDisplayName to get audio title
Rather than constructing the component manually. This should be more
compatible with mods that override getDisplayName.
2024-05-09 22:54:03 +01:00
Ella
d9b0cc7075 Changed Heart Character (-3 pixels) 2024-05-09 01:59:31 +05:30
Jonathan Coates
1e214f329e Build docs for all MC versions 2024-05-06 09:59:09 +01:00
Jonathan Coates
de930c8d09 Split up turtle textures (#1813)
Turtles currently read their textures from a single 128x128 sprite
sheet. Most of this texture is unused which means we end up wasting a
lot of the block texture atlas[^1].

This change splits up the turtle textures into individual 32x32
textures[^2], one for each side, and then an additional backpack
texture.

I'm very sorry to any resource pack artists out there. The
tools/update-resources.py script will update existing packs, but does
not (currently) handle non-standard resolutions.

[^1]: It used to be worse: https://github.com/dan200/ComputerCraft/issues/145

[^2]: Turtle textures are a bit weird, in that they mostly *look* 16x16,
  but have some detail in places.
2024-04-30 20:58:07 +00:00
Weblate
735e7ce09b Translations for Italian
Co-authored-by: Alessandro <ale.proto00@gmail.com>
2024-04-29 19:00:14 +00:00
Jonathan Coates
6e9799316a Update ErrorProne 2024-04-28 18:32:19 +01:00
Jonathan Coates
4e90240922 Bump CC:T to 1.110.3 2024-04-26 18:22:06 +01:00
Jonathan Coates
1a87d1bf45 Move shared generated resources to common project
In 1.20.1, Forge and Fabric have different "common" tag conventions (for
instance, Forge uses forge:dusts/redstone, while Fabric uses
c:redstone_dusts). This means the generated recipes (and advancements)
will be different for the two loader projects. As such, we run data
generators for each loader, and store the results separately.

However, aside from some recipes and advancements, most resources /are/
the same between the two. This means we end up with a lot of duplicate
files, which make the diff even harder to read. This gets worse in
1.20.5, when NeoForge and Fabric have (largely) unified their tag names.

This commit now merges the generated resources of the two loaders,
moving shared files to the common project.

 - Add a new MergeTrees command, to handle the de-duplication of files.
 - Change the existing runData tasks to write to
   build/generatedResources.
 - Add a new :common:runData task, that reads from the
   build/generatedResources folder and writes to the per-project
   src/generated/resources.
2024-04-26 18:09:08 +01:00
Jonathan Coates
00e2e2bd2d Switch to vanilla's stillValid implementation 2024-04-25 18:21:23 +01:00
Jonathan Coates
7c1f40031b Some cleanup to network messages
- Use enums for key and mouse actions, rather than integer ids.
 - Change TerminalState to always contain a terminal. We now make
   TerminalState nullable when we want to skip sending anything.
2024-04-25 18:19:34 +01:00
Jonathan Coates
929debd382 Don't build the webside on Windows/Mac
It seems to stall on Mac, and unlike Windows, I don't have access to a
machine to debug it :/.
2024-04-24 21:49:49 +01:00
Jonathan Coates
4980b7355d Don't share CharsetDecoders across threads
Fixes #1803
2024-04-24 21:19:30 +01:00
Jonathan Coates
925092add3 Fix build on Windows
- Force encoding to UTF-8
 - Fix npm not being found on the path
 - Test building common/web on OSX and Windows
2024-04-24 17:55:48 +01:00
Jonathan Coates
550296edc5 Fix typo in speaker docs
Closes #1799
2024-04-21 09:45:02 +01:00
Jonathan Coates
0771c4891b Various Gradle tweaks
- Update Gradle to 8.7
 - Configure IntelliJ to build internally, rather than delgating to
   Gradle. We've seen some weird issues with using delegated builds, so
   best avoided.
 - Remove gitpod config. This has been broken for a while (used Java 16
   rather than 17) and nobody noticed, so I suspect nobody uses this.
2024-04-19 18:14:51 +01:00
Jonathan Coates
776fa00b94 Update to latest TeaVM
- Add the core TeaVM jar to the runtime the classpath, to ensure
   various runtime classes are present.
 - Fix computer initialisation errors not being displayed on the screen.
   The terminal was set to the default 0x0 size when logging the error,
   and so never displayed anything!
2024-04-17 21:57:11 +01:00
Jonathan Coates
03bb279206 Move computer right click code to the block
Rather than handling right clicks within the block entity code, we now
handle it within the block. Turtles now handle the nametagging
behaviour themselves, rather than overriding canNameWithTag.
2024-04-17 15:01:50 +01:00
Weblate
fabd77132d Translations for Czech
Co-authored-by: Patriik <apatriik0@gmail.com>
2024-04-09 08:54:05 +00:00
Jonathan Coates
95be0a25bf Update Cobalt to 0.9.3
- Fix some errors missing source positions
 - Poll interrupted state when parsing Lua
2024-04-08 12:18:21 +01:00
Jonathan Coates
ad49325376 Bump CC:T to 1.110.2 2024-04-07 21:20:56 +01:00
Jonathan Coates
825d45eb26 Fix NPE when rendering turtle's label
Minecraft.hitResult may /technically/ be null when rendering a turtle.
In vanilla, this doesn't appear to happen, but other mods (e.g.
Immersive Portals) may still take advantage of this.

This hitResult is then propagated to BlockEntityRenderDispatcher, where
the field was /not/ marked as nullable. This meant we didn't even notice
the potential of an NPE!

Closes #1775
2024-04-06 08:46:19 +01:00
Jonathan Coates
8b2516abb5 Update to latest NullAway
This fixes several issues with @Nullable fields not being checked. This
is great in principle, but a little annoying in practice as MC's
@Nullable annotations are sometimes a little overly strict -- we now
need to wrap a couple of things in assertNonNull checks.
2024-04-06 08:38:44 +01:00
Jonathan Coates
bce099ef32 Allow mounting folders in the standalone emulator
This theoretically allows you to use the emulator to run the test suite
(via  --mount-ro projects/core/src/test/resources/test-rom/:test-rom),
but not sure how useful this is in practice.
2024-04-03 21:27:18 +01:00
Jonathan Coates
6d14ce625f Use the correct modem in create:brittle
I tested this in-game, I swear! Just, typically, only with ender and
wired modems.
2024-04-03 09:29:31 +01:00
Jonathan Coates
c8eadf4011 Register CC's modems as brittle
This tells Create that modems will pop-off if their neighbour is moved,
and so changes the order that the block is moved in.

We possibly should use BlockMovementChecks.AttachedCheck instead, to
properly handle the direction modems are facing in. However, this
doesn't appear to be part of the public API, so probably best avoided.

Fixes #948
2024-04-03 08:44:30 +01:00
Jonathan Coates
0c1ab780bb Validate arguments in the vector API
This doesn't produce the best error messages (should "self" be argument
0 or 1?), but is better than throwing errors in vector's internals.
2024-04-01 22:25:08 +01:00
Jonathan Coates
0f623c2cca Move can-place modem logic to one place
This should make future changes easier. Closes #1769.
2024-04-01 13:55:44 +01:00
Matthew Wilbern
b9ba2534a4 speaker sound command (#1747) 2024-03-29 10:24:11 +00:00
Jonathan Coates
c764981a40 Merge pull request #1761 from cc-tweaked/feature/no-play-record
Prevent playing music discs with speaker.playSound
2024-03-29 07:55:31 +00:00
Jonathan Coates
6363164f2b Fix creating a zero-sized pocket terminal
When the terminal data is not present, width/height are set to 0, rather
than the terminal's width/height. This meant we'd create an empty
terminal, which then crashes when we try to render it.

We now make the terminal nullable and initialise it the first time we
receive the terminal data. To prevent future mistakes, we hide
width/height, and use TerminalState.create everywhere.

Fixes #1765
2024-03-26 21:59:41 +00:00
Jonathan Coates
63580b4acb Fallback to the current side when getting fluid cap
We did this for item caps in 9af1aa1ecf,
but makes sense to do this for fluid methods too.
2024-03-26 21:40:44 +00:00
Jonathan Coates
9af1aa1ecf Fallback to the current side when getting item cap
Fixes #1764
2024-03-25 08:59:08 +00:00
Jonathan Coates
ad0f551204 Merge pull request #1763 from cyberbit/patch-1
Fix cc.image.nft.draw signature
2024-03-24 15:18:44 +00:00
Jonathan Coates
0d3e00cc41 Small cleanup to OS API docs
- Mention the timer event in os.startTimer. Really we should have a
   similar example here too, but let's at least link the two for now.
 - Fix strftime link
2024-03-24 15:12:23 +00:00
cyberbit
836d6b939e Fix cc.image.nft.draw signature 2024-03-24 09:54:01 -05:00
Jonathan Coates
0e5248e5e6 Prevent playing music discs with speaker.playSound
I have mixed feelings about speaker.playSound. On one hand, it's pretty
useful to be able to play any sound. On the other, it sometimes feels
... maybe a little too magic?

One particular thing I don't like is that it allows you to play
arbitrary records, which sidesteps both a vanilla mechanic (finding
record discs) and existing CC functionality (disk.playAudio). We now
prevent playing record tracks from the speaker.
2024-03-24 12:53:57 +00:00
Jonathan Coates
e154b0db2a Fix speaker.playSound overwriting current sound
playSound should return false if we've already played a sound this tick,
rather than overwriting it.
2024-03-24 12:20:53 +00:00
Jonathan Coates
ae767eb5be Improve error when no path is passed to "speaker"
Co-authored-by: Matthew W <fatboychummy@gmail.com>
2024-03-24 11:10:36 +00:00
Jonathan Coates
777aa34bb0 Bump CC:T to 1.110.1 2024-03-23 11:09:42 +00:00
Jonathan Coates
286f969f94 Remove computers from both lookups when they timeout
In 5d8c46c7e6, we switched to using UUIDs
for looking up computers (rather than an integer ID). However, for
compatibility in some of the command code, we need to maintain the old
integer lookup map.

Most of the code was updated to handle this, *except* the code to remove
a computer from the registry. This meant that we'd fail to remove a
computer from the UUID lookup map, so computers ended up in a phantom
state where they were destroyed, but still accessible.

This is not an issue on 1.20.4, because the legacy int lookup map was
removed.

Fixes #1760
2024-03-23 10:59:47 +00:00
Jonathan Coates
57c72711bb Use a platform method to register item properties
The two mod loaders expose different methods for this (Forge's method
takes a ItemPropertyFunction, Fabric's a ClampedItemPropertyFunction).
This is fine in a Gradle build, as the methods are compatible. However,
when running from IntelliJ, we get crashes as the common code tries to
reference the wrong method.

We now pass in the method reference instead, ensuring we use the right
method on each loader.
2024-03-22 20:19:32 +00:00
Jonathan Coates
cbafbca86b Invalidate wired element when cable is added/removed
Otherwise we end up caching the old value of getWiredElement, which
might be absent if there is no cable!

Fixes #1759
2024-03-22 20:13:02 +00:00
Jonathan Coates
c9caffb10f Bump CC:T to 1.110.0
Tricky version number to type!
2024-03-21 21:57:05 +00:00
Jonathan Coates
4675583e1c STOP DOING MIXINS (on Forge)
BYTECODE WAS NOT SUPPOSED TO BE REWRITTEN

YEARS OF DEBUGGING REMAPPING FAILURES yet NO ACTUAL SOLUTION FOUND.

Wanted to use Mixins for anyway for a laugh? We had a tool for that: it
was called "FABRIC LOOM".

"Yes, please produce completely broken jars for no discernable reason"
Statements dreamed up by the utterly Deranged.

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

This removes our two mixins used on Forge:

 - Breaking progress for cabled/wired modems.
 - Running client commands from chat click events. We now suggest the
   command on Forge instead.

Occasionally we get issues where the mixin annotation processor doesn't
write its tsrg file in time for the reobfJar/reobfJarJar task. I thought
we'd fixed that cb8e06af2a, but sometimes
we still produce missing jars - I have a feeling this might be to do
with incremental compilation.

We can maybe re-evaluate this on 1.20.4, where we don't need to worry
about remapping any more.
2024-03-21 21:45:17 +00:00
Jonathan Coates
afe16cc593 Clean up turtle inventory reading 2024-03-21 21:21:31 +00:00
Jonathan Coates
0abd107348 Load services with the service's classloader
We were seeing some strange issues in the Fabric test code where we
tried to load the implementation from a different classloader. This
ensures that the classloaders are consistent.
2024-03-21 20:50:31 +00:00
Jonathan Coates
cef4b4906b Bump Cobalt for tostring yield fix 2024-03-21 19:54:29 +00:00
Jonathan Coates
04900dc82f Skip main-thread tasks if peripheral is detached
Due to the asynchronous nature of main-thread tasks, it's possible for
them to be executed on peripherals which have been detached. This has
been known for a long time (#893 was opened back in 2021), but finding a
good solution here is tricky.

Most of the time the method will silently succeed, but if we try to
interact with an IComputerAccess (such as in inventory methods, as seen
in #1750), we throw a NotAttachedException exception and spam the logs!

This is an initial step towards fixing this - when calling a peripheral
method via peripheral.call/modem.callRemote, we now wrap any enqueued
main-thread tasks and silently skip them if the peripheral has been
detached since.

This means that peripheral methods may start to return nil when they
didn't before. I think this is *fine* (though not ideal for sure!) - we
return nil if the peripheral has been detached, so it's largely
equivalent to that.
2024-03-21 19:54:22 +00:00
Jonathan Coates
9b63cc81b1 Custom equality for Fabric's storage types
Double chests peripherals were getting reattached every time there was a
block update, as the inventories were not comparing equal (despite being
so!). We now check for a couple of common cases, which should be enough
for vanilla/vanilla-like inventories.

I actively Do Not Like This Code, but do not see a good alternative.
2024-03-21 19:54:22 +00:00
Jonathan Coates
9eead7a0ec Use shell.resolve in speaker.lua
Fixes #1753
2024-03-20 10:45:23 +00:00
Jonathan Coates
ad97b2922b Invalidate peripherals on updateShape
This fixes chests not being reattached when their size changes.
2024-03-20 10:07:29 +00:00
Jonathan Coates
52986f8d73 Drop modems as an item in updateShape
We were still handling this logic in neighborChanged, like this was
1.12. The horror!
2024-03-17 22:09:21 +00:00
Jonathan Coates
ab00580389 Simplify the previous patch a little
We can use BlockEntityType.getKey, rather than having to extend our
registry wrappers.
2024-03-17 16:21:56 +00:00
Jonathan Coates
128ac2f109 Better handling when a BE type isn't registered
This should never happen, but apparently it does!? We now log an error
(rather than crashing), and include the original BE (and associated
block), as the BE type isn't very useful.

See #1750. Technically this fixes it, but want to do some more poking
there first.
2024-03-17 16:13:33 +00:00
Jonathan Coates
5d8c46c7e6 Replace integer instance IDs with UUIDs
Here's a fun bug you can try at home:
 - Create a new world
 - Spawn in a pocket computer, turn it on, and place it in a chest.
 - Reload the world - the pocket computer in the chest should now be
   off.
 - Spawn in a new pocket computer, and turn it on. The computer in chest
   will also appear to be on!

This bug has been present since pocket computers were added (27th March,
2024).

When a pocket computer is added to a player's inventory, it is assigned
a unique *per-session* "instance id" , which is used to find the
associated computer. Note the "per-session" there - these ids will be
reused if you reload the world (or restart the server).

In the above bug, we see the following:

 - The first pocket computer is assigned an instance id of 0.
 - After reloading, the second pocket computer is assigned an instance
   id of 0.
 - If the first pocket computer was in our inventory, it'd be ticked and
   assigned a new instance id. However, because it's in an inventory, it
   keeps its old one.
 - Both computers look up their client-side computer state and get the
   same value, meaning the first pocket computer mirrors the second!

To fix this, we now ensure instance ids are entirely unique (not just
per-session). Rather than sequentially assigning an int, we now use a
random UUID (we probably could get away with a random long, but this
feels more idiomatic).

This has a couple of user-visible changes:

 - /computercraft no longer lists instance ids outside of dumping an
   individual computer.
 - The @c[instance=...] selector uses UUIDs. We still use int instance
   ids for the legacy selector, but that'll be removed in a later MC
   version.
 - Pocket computers now store a UUID rather than an int.

Related to this change (I made this change first, but then they got
kinda mixed up together), we now only create PocketComputerData when
receiving server data. This makes the code a little uglier in some
places (the data may now be null), but means we don't populate the
client-side pocket computer map with computers the server doesn't know
about.
2024-03-17 14:56:12 +00:00
Jonathan Coates
1a5dc92bd4 Some more cleanup to wired modems
- Remove "initial connections" flag, and just refresh connections +
   peripherals on the first tick.

 - Remove "peripheral attached" from NBT, and just read/write it from
   the block state. This might cause issues with #1010, but that's
   sufficiently old I hope it won't!
2024-03-17 00:18:27 +00:00
Jonathan Coates
98b2d3f310 Simplify WiredModemPeripheral interface a little 2024-03-16 23:24:55 +00:00
Jonathan Coates
e92c2d02f8 Fix turtle.suck reporting incorrect error
Our GatedPredicate hack was clever, but also fundamentally didn't work.
The predicate is called before extraction, so if extraction fails (for
instance, canTakeItemThroughFace returns false), then we still think an
item has been removed.

To fix that, we inline StorageUtil.move, specialising it for what we
need.
2024-03-16 21:27:21 +00:00
Jonathan Coates
f8ef40d378 Add a method for checking peripheral equality
This feels a little overkill, but nice to standardise how this code
looks.

There's a bit of me which wonders if we should remove
IPeripheral.equals, and just use Object.equals, but I do also kinda like
the explicitness of the current interface? IDK.
2024-03-16 14:01:22 +00:00
Jonathan Coates
61f9b1d0c6 Send entire DFPWM encoder state to the client
This ensures the client decoder is in sync with the server. Well, mostly
- we don't handle the anti-jerk, but that should correct itself within a
few samples.

Fixes #1748
2024-03-15 18:25:57 +00:00
Jonathan Coates
ffb62dfa02 Bump checkstyle, fix warnings from TeaVM upgrade 2024-03-13 21:52:09 +00:00
Jonathan Coates
6fb291112d Update TeaVM for ESM support
We still need our fork (file attributes, some Math/Int/Long methods),
but this simplifies things a wee bit.
2024-03-13 13:01:25 +00:00
Jonathan Coates
7ee821e9c9 Allow coroutine managers to integrate with error reporting
The original runtime error reporting PR[^1] added a "cc.exception"
module, which allowed coroutine managers (such as parallel) to throw
rich errors, detailing the original context where the error was thrown.

Unfortunately, the change to parallel broke some programs (>_>, don't do
string pattern matching on your errors!), and so had to be reverted,
along with the cc.exception module.

As a minimal replacement for this, we add support for user-thrown
exceptions within our internal code. If an error object "looks" like an
exception ("exception" __name, and a message and thread field), then we
use that as our error information instead.

This is currently undocumented (at least in user-facing documentation),
mostly because I couldn't figure out where to put it - the interface
should remain stable.

[^1]: https://github.com/cc-tweaked/CC-Tweaked/pull/1320
2024-03-12 20:55:30 +00:00
Jonathan Coates
b7df91349a Rewrite computer selectors
This adds support for computer selectors, in the style of entity
selectors. The long-term goal here is to replace our existing ad-hoc
selectors. However, to aid migration, we currently support both - the
previous one will most likely be removed in MC 1.21.

Computer selectors take the form @c[<key>=<value>,...]. Currently we
support filtering by id, instance id, label, family (as before) and
distance from the player (new!). The code also supports computers within
a bounding box, but there's no parsing support for that yet.

This commit also (finally) documents the /computercraft command. Well,
sort of - it's definitely not my best word, but I couldn't find better
words.
2024-03-12 20:12:13 +00:00
Jonathan Coates
cb8e06af2a Ensure mixin reobf configs run after compilation
Fixes #1744
2024-03-11 22:28:02 +00:00
Jonathan Coates
6478fca7a2 Update to latest illuaminate
This allows us to remove our image copying code
2024-03-11 21:36:46 +00:00
Jonathan Coates
3493159a05 Bump CC:T to 1.109.7 2024-03-10 19:10:09 +00:00
Jonathan Coates
eead67e314 Fix a couple of warnings 2024-03-10 12:04:40 +00:00
Jonathan Coates
3b8813cf8f Slightly more detailed negative allocation logging
Hopefully will help debug #1739. Maybe.
2024-03-10 11:26:36 +00:00
Jonathan Coates
a9191a4d4e Don't cache the client monitor
When rendering non-origin monitors, we would fetch the origin monitor,
read its client state, and then cache that on the current monitor to
avoid repeated lookups.

However, if the origin monitor is unloaded/removed on the client, and
then loaded agin, this cache will be not be invalidated, causing us to
render both the old and new monitor!

I think the correct thing to do here is cache the origin monitor. This
allows us to check when the origin monitor has been removed, and
invalidate the cache if needed.

However, I'm wary of any other edge cases here, so for now we do
something much simpler, and remove the cache entirely. This does mean
that monitors now need to perform extra block entity lookups, but the
performance cost doesn't appear to be too bad.

Fixes #1741
2024-03-10 10:57:56 +00:00
Jonathan Coates
451a2593ce Move WiredNode default methods to the impl 2024-03-10 10:00:52 +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
6e374579a4 Standardise on term colour parsing
- colors.toBlit now performs bounds checks on the passed value,
   preventing weird behaviour like color.toBlit(2 ^ 16) returning "10".

 - The window API now uses colors.toBlit (or rather a copy of it) for
   parsing colours, allowing doing silly things like
   term.setTextColour(colours.blue + 5).

 - Add some top-level documentation to the term API to explain some of
   the basics.

Closes #1736
2024-03-06 10:18:40 +00:00
Jonathan Coates
4daa2a2b6a Reschedule block entities when chunks are loaded
Minecraft sometimes keeps chunks in-memory, but not actively loaded. If
we schedule a block entity to be ticked and that chunk is is then
transitioned to this partially-loaded state, then the block entity is
never actually ticked.

This is most visible with monitors. When a monitor's contents changes,
if the monitor is not already marked as changed, we set it as changed
and schedule a tick (see ServerMonitor). However, if the tick is
dropped, we don't clear the changed flag, meaning subsequent changes
don't requeue the monitor to be ticked, and so the monitor is never
updated.

We fix this by maintaining a list of block entities whose tick was
dropped. If these block entities (or rather their owning chunk) is ever
re-loaded, then we reschedule them to be ticked.

An alternative approach here would be to add the scheduled tick directly
to the LevelChunk. However, getting hold of the LevelChunk for unloaded
blocks is quiet nasty, so I think best avoided.

Fixes #1146. Fixes #1560 - I believe the second one is a duplicate, and
I noticed too late :D.
2024-02-26 19:25:38 +00:00
Jonathan Coates
84b6edab82 More efficient removal of wired nodes from networks
When we remove a wired node from a network, we need to find connected
components in the rest of the graph. Typically, this requires a
traversal of the whole graph, taking O(|V| + |E|) time.

If we remove a lot of nodes at once (such as when unloading chunks),
this ends up being quadratic in the number of nodes. In some test
networks, this can take anywhere from a few seconds, to hanging the game
indefinitely.

This attempts to reduce the cases where this can happen, with a couple
of optimisations:

 - Instead of constructing a new hash set of reachable nodes (requiring
   multiple allocations and hash lookups), we store reachability as a
   temporary field on the WiredNode.

 - We abort our traversal of the graph if we can prove the graph remains
   connected after removing the node.

There's definitely future work to be done here in optimising large wired
networks, but this is a good first step.
2024-02-24 15:02:34 +00:00
Jonathan Coates
31aaf46d09 Deprecate WiredNetwork
We don't actually need this to be in the public API.
2024-02-24 14:55:22 +00:00
Jonathan Coates
2d11b51c62 Clean up the wired network tests
- Replace usages of WiredNetwork.connect/disconnect/remove with the
   WiredNode equivalents.

 - Convert "testLarge" into a proper JMH benchmark.

 - Don't put a peripheral on every node in the benchmarks. This isn't
   entirely representative, and means the peripheral juggling code ends
   up dominating the benchmark time.
2024-02-24 14:52:44 +00:00
Jonathan Coates
a0f759527d Bump CC:T to 1.109.6 2024-02-18 18:33:42 +00:00
Jonathan Coates
385e4210fa Update Cobalt to 0.9.1 for weak table fixes 2024-02-18 18:23:43 +00:00
Jonathan Coates
d2896473f2 Update our parse errors to match latest illuaminate
We've been out-of-date for a while now, as we needed to update
lua_menhir to work with lrgrep 3.

 - Better handling of standalone names/expressions - we now correctly
   handle lists of names.

 - Handle missing commas in tables in a few more places.
2024-02-08 19:22:14 +00:00
Jonathan Coates
f14cb2a3d1 Fix MemoryMount using incorrect file lengths
- We checked the backing array when reading rather than the file's
   length, so could read beyond the end of the file.
 - We used the entry length when resizing, which effectively meant we
   doubled the size of the backing array on each write.
2024-02-07 21:36:17 +00:00
Jonathan Coates
8db5c6bc3a Allow ILuaAPIs to be exposed as a module
- cc.require now uses the internal _LOADED table to get the list of
   built-in globals. This fixes several globals not showing up on the
   list (e.g. utf8), and allows us to inject more modules from the Java
   side.

 - ILuaAPI now has a getModuleName() function. This is used to inject
   the API into the aforementioned _LOADED table, allowing it to be
   "require"d.
2024-02-05 18:53:24 +00:00
833 changed files with 7745 additions and 6227 deletions

View File

@@ -8,10 +8,8 @@ body:
label: Minecraft Version
description: What version of Minecraft are you using?
options:
- 1.16.x
- 1.18.x
- 1.19.x
- 1.20.x
- 1.20.1
- 1.21.x
validations:
required: true
- type: input

View File

@@ -9,16 +9,16 @@ jobs:
steps:
- name: 📥 Clone repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: 📥 Set up Java
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
- name: 📥 Setup Gradle
uses: gradle/gradle-build-action@v2
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
@@ -58,13 +58,13 @@ jobs:
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
- name: 📤 Upload Jar
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: CC-Tweaked
path: ./jars
- name: 📤 Upload coverage
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
build-core:
strategy:
@@ -81,24 +81,28 @@ jobs:
runs-on: ${{ matrix.uses }}
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: 📥 Clone repository
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v3
- name: 📥 Set up Java
uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: 📥 Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
- name: Run tests
- name: ⚒️ Build
run: |
./gradlew --configure-on-demand :core:assemble
- name: 🧪 Run tests
run: |
./gradlew --configure-on-demand :core:test
- name: Parse test reports
- name: 🧪 Parse test reports
run: python3 ./tools/parse-reports.py
if: ${{ failure() }}

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env bash
set -eu
DEST="${GITHUB_REF#refs/*/}"
echo "Uploading docs to https://tweaked.cc/$DEST"
# Setup ssh key
mkdir -p "$HOME/.ssh/"
echo "$SSH_KEY" > "$HOME/.ssh/key"
chmod 600 "$HOME/.ssh/key"
# And upload
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
"$GITHUB_WORKSPACE/projects/web/build/site/" \
"$SSH_USER@$SSH_HOST:/$DEST"
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
"$GITHUB_WORKSPACE/projects/common-api/build/docs/javadoc/" \
"$SSH_USER@$SSH_HOST:/$DEST/javadoc"

View File

@@ -3,8 +3,7 @@ name: Build documentation
on:
push:
branches:
- mc-1.19.x
- mc-1.20.x
- mc-*
jobs:
make_doc:
@@ -12,30 +11,25 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: 📥 Clone repository
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v1
- name: 📥 Set up Java
uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: 📥 Setup Gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
- name: Build with Gradle
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
- name: ⚒️ Generate documentation
run: ./gradlew docWebsite --no-daemon
- name: Generate documentation
run: ./gradlew docWebsite :common-api:javadoc --no-daemon
- name: Upload documentation
run: .github/workflows/make-doc.sh 2> /dev/null
env:
SSH_KEY: ${{ secrets.SSH_KEY }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PORT: ${{ secrets.SSH_PORT }}
- name: 📤 Upload Jar
uses: actions/upload-artifact@v4
with:
name: Documentation
path: ./projects/web/build/site/

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@
/projects/*/logs
/projects/fabric/fabricloader.log
/projects/*/build
/projects/*/src/test/generated_tests/
/buildSrc/build
/out
/buildSrc/out

View File

@@ -1,26 +0,0 @@
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
#
# SPDX-License-Identifier: MPL-2.0
image:
file: config/gitpod/Dockerfile
ports:
- port: 25565
onOpen: notify
vscode:
extensions:
- eamodio.gitlens
- github.vscode-pull-request-github
- ms-azuretools.vscode-docker
- redhat.java
- richardwillis.vscode-gradle
- vscjava.vscode-java-debug
- vscode.github
tasks:
- name: Setup pre-commit hool
init: pre-commit install --allow-missing-config
- name: Install npm packages
init: npm ci

View File

@@ -27,7 +27,7 @@ repos:
exclude: "^(.*\\.(bat)|LICENSE)$"
- repo: https://github.com/fsfe/reuse-tool
rev: v2.1.0
rev: v4.0.3
hooks:
- id: reuse

View File

@@ -1,100 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Source: https://github.com/cc-tweaked/cc-tweaked
Upstream-Name: CC: Tweaked
Upstream-Contact: Jonathan Coates <git@squiddev.cc>
Files:
projects/common/src/main/resources/assets/computercraft/sounds.json
projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg
projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/*
projects/common/src/testMod/resources/data/cctest/structures/*
projects/fabric/src/generated/*
projects/forge/src/generated/*
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
Files:
doc/images/*
package.json
package-lock.json
projects/common/src/client/resources/computercraft-client.mixins.json
projects/common/src/main/resources/assets/minecraft/shaders/core/computercraft/monitor_tbo.json
projects/common/src/main/resources/computercraft.mixins.json
projects/common/src/testMod/resources/computercraft-gametest.mixins.json
projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json
projects/common/src/testMod/resources/pack.mcmeta
projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/.ignoreme
projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/.ignoreme
projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/.ignoreme
projects/core/src/main/resources/data/computercraft/lua/rom/motd.txt
projects/fabric-api/src/main/modJson/fabric.mod.json
projects/fabric/src/client/resources/computercraft-client.fabric.mixins.json
projects/fabric/src/main/resources/computercraft.fabric.mixins.json
projects/fabric/src/main/resources/fabric.mod.json
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/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
License: MPL-2.0
Files:
doc/logo.png
doc/logo-darkmode.png
projects/common/src/main/resources/assets/computercraft/models/*
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/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: MPL-2.0
Files:
.github/*
Comment:
GitHub build scripts are CC0. While we could add a header to each file,
it's unclear if it will break actions or issue templates in some way.
Copyright: Jonathan Coates <git@squiddev.cc>
License: CC0-1.0
Files:
gradle/wrapper/*
gradlew
gradlew.bat
Copyright: Gradle Inc
License: Apache-2.0
Files: projects/core/src/test/resources/test-rom/data/json-parsing/*
Copyright: 2016 Nicolas Seriot
License: MIT

View File

@@ -12,7 +12,6 @@ If you've any other questions, [just ask the community][community] or [open an i
## Table of Contents
- [Reporting issues](#reporting-issues)
- [Translations](#translations)
- [Setting up a development environment](#setting-up-a-development-environment)
- [Developing CC: Tweaked](#developing-cc-tweaked)
- [Writing documentation](#writing-documentation)
@@ -21,17 +20,13 @@ If you've any other questions, [just ask the community][community] or [open an i
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, do
use the issue templates - they provide a useful hint on what information to provide.
## Translations
Translations are managed through [Weblate], an online interface for managing language strings. This is synced
automatically with GitHub, so please don't submit PRs adding/changing translations!
## Setting up a development environment
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
- Make sure you've got the following software installed:
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
- Java Development Kit 17 (JDK). This can be downloaded from [Adoptium].
- [Git](https://git-scm.com/).
- If you want to work on documentation, [NodeJS][node].
- [NodeJS 20 or later][node].
- Download CC: Tweaked's source code:
```
@@ -101,7 +96,6 @@ about how you can build on that until you've covered everything!
[community]: README.md#community "Get in touch with the community."
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
[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"
[ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator."
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg

View File

@@ -26,8 +26,9 @@ developing the mod, [check out the instructions here](CONTRIBUTING.md#developing
## Community
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
ComputerCraft, do check out our [forum] and [GitHub discussions page][GitHub discussions]! There's also a fairly
populated, albeit quiet [IRC channel][irc], if that's more your cup of tea.
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
desktop client, or online using [KiwiIRC].
We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
@@ -39,7 +40,7 @@ on is present.
```groovy
repositories {
maven {
url "https://squiddev.cc/maven/"
url "https://maven.squiddev.cc"
content {
includeGroup("cc.tweaked")
}
@@ -86,6 +87,6 @@ the generated documentation [can be browsed online](https://tweaked.cc/javadoc/)
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
[forum]: https://forums.computercraft.cc/
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
[EsperNet]: https://www.esper.net/
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"

110
REUSE.toml Normal file
View File

@@ -0,0 +1,110 @@
# SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
#
# SPDX-License-Identifier: MPL-2.0
version = 1
SPDX-PackageName = "CC: Tweaked"
SPDX-PackageSupplier = "Jonathan Coates <git@squiddev.cc>"
SPDX-PackageDownloadLocation = "https://github.com/cc-tweaked/cc-tweaked"
[[annotations]]
# Generated/data files are CC0.
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
SPDX-License-Identifier = "CC0-1.0"
path = [
"gradle/gradle-daemon-jvm.properties",
"projects/common/src/main/resources/assets/computercraft/sounds.json",
"projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg",
"projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/**",
"projects/common/src/testMod/resources/data/cctest/structures/**",
"projects/**/src/generated/**",
"projects/web/src/htmlTransform/export/index.json",
"projects/web/src/htmlTransform/export/items/minecraft/**",
]
[[annotations]]
# Several assets where it's inconvenient to create a .license file.
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
SPDX-License-Identifier = "MPL-2.0"
path = [
"doc/images/**",
"package.json",
"package-lock.json",
"projects/common/src/client/resources/computercraft-client.mixins.json",
"projects/common/src/main/resources/assets/minecraft/shaders/core/computercraft/monitor_tbo.json",
"projects/common/src/main/resources/computercraft.mixins.json",
"projects/common/src/testMod/resources/computercraft-gametest.mixins.json",
"projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json",
"projects/common/src/testMod/resources/pack.mcmeta",
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/.ignoreme",
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/.ignoreme",
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/.ignoreme",
"projects/core/src/main/resources/data/computercraft/lua/rom/motd.txt",
"projects/fabric-api/src/main/modJson/fabric.mod.json",
"projects/fabric/src/client/resources/computercraft-client.fabric.mixins.json",
"projects/fabric/src/main/resources/computercraft.fabric.mixins.json",
"projects/fabric/src/main/resources/fabric.mod.json",
"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/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",
]
[[annotations]]
# Bulk-license original assets as CCPL.
SPDX-FileCopyrightText = "2011 Daniel Ratcliffe"
SPDX-License-Identifier = "LicenseRef-CCPL"
path = [
"doc/logo.png",
"doc/logo-darkmode.png",
"projects/common/src/main/resources/assets/computercraft/models/**",
"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/htmlTransform/export/items/computercraft/**",
]
[[annotations]]
# Community-contributed license files
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
SPDX-License-Identifier = "LicenseRef-CCPL"
path = [
"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",
]
[[annotations]]
# Community-contributed license files
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
SPDX-License-Identifier = "MPL-2.0"
path = "projects/common/src/main/resources/assets/computercraft/lang/**"
[[annotations]]
# GitHub build scripts are CC0. While we could add a header to each file,
# it's unclear if it will break actions or issue templates in some way.
SPDX-FileCopyrightText = "Jonathan Coates <git@squiddev.cc>"
SPDX-License-Identifier = "CC0-1.0"
path = ".github/**"
[[annotations]]
path = ["gradle/wrapper/**", "gradlew", "gradlew.bat"]
SPDX-FileCopyrightText = "Gradle Inc"
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = "projects/core/src/test/resources/test-rom/data/json-parsing/**"
SPDX-FileCopyrightText = "2016 Nicolas Seriot"
SPDX-License-Identifier = "MIT"

View File

@@ -5,9 +5,8 @@
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
import org.jetbrains.gradle.ext.*
import org.jetbrains.gradle.ext.Application
plugins {
publishing
@@ -86,6 +85,19 @@ idea.project.settings.runConfigurations {
moduleName = "${idea.project.name}.forge.test"
packageName = ""
}
register<Application>("Standalone") {
moduleName = "${idea.project.name}.standalone.main"
mainClass = "cc.tweaked.standalone.Main"
programParameters = "--resources=projects/core/src/main/resources --term=80x30 --allow-local-domains"
}
}
// Build with the IntelliJ, rather than through Gradle. This may require setting the "Compiler Output" option in
// "Project Structure".
idea.project.settings.delegateActions {
delegateBuildRunToGradle = false
testRunner = ActionDelegationConfig.TestRunner.PLATFORM
}
idea.project.settings.compiler.javac {

View File

@@ -36,7 +36,7 @@ repositories {
}
}
maven("https://squiddev.cc/maven") {
maven("https://maven.squiddev.cc") {
name = "SquidDev"
content {
includeGroup("cc.tweaked.vanilla-extract")

View File

@@ -38,12 +38,8 @@ java {
repositories {
mavenCentral()
val mainMaven = maven("https://squiddev.cc/maven") {
val mainMaven = maven("https://maven.squiddev.cc/mirror") {
name = "SquidDev"
content {
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
includeModule("org.spongepowered", "mixin")
}
}
exclusiveContent {
@@ -58,6 +54,7 @@ repositories {
filter {
includeGroup("cc.tweaked")
// Things we mirror
includeGroup("com.simibubi.create")
includeGroup("commoble.morered")
includeGroup("dev.architectury")
includeGroup("dev.emi")
@@ -98,9 +95,8 @@ sourceSets.all {
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
// Too many false positives right now. Maybe we need an indirection for it later on.
check("ReferenceEquality", CheckSeverity.OFF)
check("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
check("AlreadyChecked", CheckSeverity.OFF) // Seems to be broken?
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
@@ -113,6 +109,8 @@ sourceSets.all {
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
option("NullAway:CheckOptionalEmptiness")
option("NullAway:AcknowledgeRestrictiveAnnotations")
excludedPaths = ".*/jmh_generated/.*"
}
}
}
@@ -136,8 +134,8 @@ tasks.processResources {
tasks.withType(AbstractArchiveTask::class.java).configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
dirMode = Integer.valueOf("755", 8)
fileMode = Integer.valueOf("664", 8)
filePermissions {}
dirPermissions {}
}
tasks.jar {

View File

@@ -32,7 +32,7 @@ val publishCurseForge by tasks.registering(TaskPublishCurseForge::class) {
apiToken = findProperty("curseForgeApiKey") ?: ""
enabled = apiToken != ""
val mainFile = upload("282001", modPublishing.output.get().archiveFile)
val mainFile = upload("282001", modPublishing.output)
mainFile.changelog =
"Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
mainFile.changelogType = "markdown"

View File

@@ -38,7 +38,7 @@ publishing {
}
repositories {
maven("https://squiddev.cc/maven") {
maven("https://maven.squiddev.cc") {
name = "SquidDev"
credentials(PasswordCredentials::class)

View File

@@ -22,7 +22,6 @@ import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.configurationcache.extensions.capitalized
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources
import org.gradle.process.JavaForkOptions
@@ -35,7 +34,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.File
import java.io.IOException
import java.net.URI
import java.net.URL
import java.util.regex.Pattern
abstract class CCTweakedExtension(
@@ -182,7 +180,7 @@ abstract class CCTweakedExtension(
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
val reportTaskName = "jacoco${task.name.capitalized()}Report"
val reportTaskName = "jacoco${task.name.capitalise()}Report"
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
task.configure {
@@ -226,12 +224,12 @@ abstract class CCTweakedExtension(
* where possible.
*/
fun downloadFile(label: String, url: String): File {
val url = URL(url)
val path = File(url.path)
val uri = URI(url)
val path = File(uri.path)
project.repositories.ivy {
name = label
setUrl(URI(url.protocol, url.userInfo, url.host, url.port, path.parent, null, null))
setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null))
patternLayout {
artifact("[artifact].[ext]")
}

View File

@@ -155,3 +155,15 @@ fun getNextVersion(version: String): String {
if (dashIndex >= 0) out.append(version, dashIndex, version.length)
return out.toString()
}
/**
* Capitalise the first letter of the string.
*
* This is a replacement for the now deprecated [String.capitalize].
*/
fun String.capitalise(): String {
if (isEmpty()) return this
val first = this[0]
val firstTitle = first.titlecaseChar()
return if (first == firstTitle) this else firstTitle + substring(1)
}

View File

@@ -0,0 +1,120 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package cc.tweaked.gradle
import cc.tweaked.vanillaextract.core.util.MoreFiles
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.file.*
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.*
import javax.inject.Inject
/**
* Merge common files across multiple directories into one destination directory.
*
* This is intended for merging the generated resources from the Forge and Fabric projects. Files common between the two
* are written to the global [output] directory, while distinct files are written to the per-source
* [MergeTrees.Source.output] directory.
*/
abstract class MergeTrees : DefaultTask() {
/**
* A source directory to read from.
*/
interface Source {
/**
* The folder contianing all input files.
*/
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
val input: ConfigurableFileTree
fun input(configure: Action<ConfigurableFileTree>) {
configure.execute(input)
}
/**
* The folder to write files unique to this folder to.
*/
@get:OutputDirectory
val output: DirectoryProperty
}
/**
* The list of sources.
*/
@get:Nested
abstract val sources: ListProperty<Source>
/**
* Add and configure a new source.
*/
fun source(configure: Action<Source>) {
val instance = objectFactory.newInstance(Source::class.java)
configure.execute(instance)
instance.output.disallowChanges()
sources.add(instance)
}
/**
* The directory to write common files to.
*/
@get:OutputDirectory
abstract val output: DirectoryProperty
@get:Inject
protected abstract val objectFactory: ObjectFactory
@get:Inject
protected abstract val fsOperations: FileSystemOperations
@TaskAction
fun run() {
val sources = this.sources.get()
if (sources.isEmpty()) throw GradleException("Cannot have an empty list of sources")
val files = mutableMapOf<String, SharedFile>()
for (source in sources) {
source.input.visit(
object : FileVisitor {
override fun visitDir(dirDetails: FileVisitDetails) = Unit
override fun visitFile(fileDetails: FileVisitDetails) {
val path = fileDetails.file.toRelativeString(source.input.dir)
val hash = MoreFiles.computeSha1(fileDetails.file.toPath())
val existing = files[path]
if (existing == null) {
files[path] = SharedFile(hash, 1)
} else if (existing.hash == hash) {
existing.found++
}
}
},
)
}
val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
// Copy shared files to the common directory
fsOperations.sync {
from(sources[0].input)
into(output)
include(sharedFiles)
}
// And all other files to their per-source directory
for (source in sources) {
fsOperations.sync {
from(source.input)
into(source.output)
exclude(sharedFiles)
}
}
}
class SharedFile(val hash: String, var found: Int)
}

View File

@@ -46,7 +46,7 @@ abstract class NpmInstall : DefaultTask() {
@TaskAction
fun install() {
project.exec {
commandLine("npm", "ci")
commandLine(ProcessHelpers.getExecutable("npm"), "ci")
workingDir = projectRoot.get().asFile
}
}
@@ -59,6 +59,6 @@ abstract class NpmInstall : DefaultTask() {
abstract class NpxExecToDir : ExecToDir() {
init {
dependsOn(NpmInstall.TASK_NAME)
executable = "npx"
executable = ProcessHelpers.getExecutable("npx")
}
}

View File

@@ -9,6 +9,7 @@ import org.gradle.api.GradleException
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
internal object ProcessHelpers {
fun startProcess(vararg command: String): Process {
@@ -34,7 +35,7 @@ internal object ProcessHelpers {
val process = startProcess(*command)
process.outputStream.close()
val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
val out = BufferedReader(InputStreamReader(process.inputStream, StandardCharsets.UTF_8)).use { reader ->
reader.lines().filter { it.isNotEmpty() }.toList()
}
ProcessGroovyMethods.closeStreams(process)
@@ -46,6 +47,28 @@ internal object ProcessHelpers {
val path = System.getenv("PATH") ?: return false
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
}
/**
* Search for an executable on the `PATH` if required.
*
* [Process]/[ProcessBuilder] does not handle all executable file extensions on Windows (such as `.com). When on
* Windows, this function searches `PATH` and `PATHEXT` for an executable matching [name].
*/
fun getExecutable(name: String): String {
if (!System.getProperty("os.name").lowercase().contains("windows")) return name
val path = (System.getenv("PATH") ?: return name).split(File.pathSeparator)
val pathExt = (System.getenv("PATHEXT") ?: return name).split(File.pathSeparator)
for (pathEntry in path) {
for (ext in pathExt) {
val resolved = File(pathEntry, name + ext)
if (resolved.exists()) return resolved.getAbsolutePath()
}
}
return name
}
}
internal fun Process.waitForOrThrow(message: String) {

View File

@@ -13,8 +13,12 @@ SPDX-License-Identifier: MPL-2.0
<property name="tabWidth" value="4"/>
<property name="charset" value="UTF-8" />
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
</module>
<module name="SuppressionFilter">
<property name="file" value="${config_loc}/suppressions.xml" />
<property name="file" value="${config_loc}/suppressions.xml" />
</module>
<module name="BeforeExecutionExclusionFileFilter">

View File

@@ -21,5 +21,5 @@ SPDX-License-Identifier: MPL-2.0
<suppress checks="PackageName" files=".*[\\/]T[A-Za-z]+.java" />
<!-- Allow underscores in our test classes. -->
<suppress checks="MethodName" files=".*Contract.java" />
<suppress checks="MethodName" files=".*(Contract|Test).java" />
</suppressions>

View File

@@ -1,12 +0,0 @@
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
#
# SPDX-License-Identifier: MPL-2.0
FROM gitpod/workspace-base
USER gitpod
RUN sudo apt-get -q update \
&& sudo apt-get install -yq openjdk-16-jdk python3-pip npm \
&& sudo pip3 install pre-commit \
&& sudo update-java-alternatives --set java-1.16.0-openjdk-amd64

View File

@@ -19,7 +19,7 @@ In order to give the best results, a GPS constellation needs at least four compu
constellation is redundant, but it does not cause problems.
## Building a GPS constellation
<img alt="An example GPS constellation." src="/images/gps-constellation-example.png" class="big-image" />
<img alt="An example GPS constellation." src="../images/gps-constellation-example.png" class="big-image" />
We are going to build our GPS constellation as shown in the image above. You will need 4 computers and either 4 wireless
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your

View File

@@ -131,7 +131,7 @@ different.
First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPWM 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.ReadHandle.read`] if you prefer.
@@ -191,7 +191,7 @@ end
> [Confused?][!NOTE]
> Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
> cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
> cover. That said, don't be afraid to ask [the community for help][community].
It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
@@ -205,5 +205,4 @@ This is, I'm afraid, left as an exercise to the reader.
[PCM]: https://en.wikipedia.org/wiki/Pulse-code_modulation "Pulse-code Modulation - Wikipedia"
[Ring Buffer]: https://en.wikipedia.org/wiki/Circular_buffer "Circular buffer - Wikipedia"
[Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia"
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
[Community]: /#community

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

View File

@@ -4,7 +4,14 @@ SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
SPDX-License-Identifier: MPL-2.0
-->
# ![CC: Tweaked](logo.png)
<h1>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="logo-darkmode.png">
<source media="(prefers-color-scheme: light)" srcset="logo.png">
<img alt="CC: Tweaked" src="logo.png">
</picture>
</h1>
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of
new features.
@@ -38,12 +45,16 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
- [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
- [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
- [Lyqyd's Computer Basics 1](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
- [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
Once you're a little more familiar with the mod, the sidebar and links below provide more detailed documentation on the
various APIs and peripherals provided by the mod.
If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
<h2 id="community">Community</h2>
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
desktop client, or online using [KiwiIRC].
## Get Involved
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
@@ -58,4 +69,5 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
[lua]: https://www.lua.org/ "Lua's main website"
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
[EsperNet]: https://www.esper.net/
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"

View File

@@ -45,12 +45,16 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
- [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
- [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
- [Lyqyd's Computer Basics 1](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
- [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
Once you're a little more familiar with the mod, the [wiki](https://tweaked.cc/) provides more detailed documentation on the
various APIs and peripherals provided by the mod.
If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
## Community
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
desktop client, or online using [KiwiIRC].
## Get Involved
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
@@ -60,4 +64,5 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
[lua]: https://www.lua.org/ "Lua's main website"
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
[EsperNet]: https://www.esper.net/
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"

View File

@@ -25,13 +25,13 @@ as documentation for breaking changes and "gotchas" one should look out for betw
- 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
- 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.
- `math.random` now uses Lua 5.4's random number generator.
- Support for dumping functions ([`string.dump`]) and loading binary chunks has been removed.
- [`math.random`] now uses Lua 5.4's random number generator.
- File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
@@ -44,7 +44,7 @@ as documentation for breaking changes and "gotchas" one should look out for betw
`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.
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.
@@ -70,12 +70,12 @@ as documentation for breaking changes and "gotchas" one should look out for betw
- 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
- 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/w/Java_Edition_1.13/Flattening
[legal_data_pack]: https://minecraft.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
[legal_data_pack]: https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#Legal_characters
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"

140
doc/reference/command.md Normal file
View File

@@ -0,0 +1,140 @@
---
module: [kind=reference] computercraft_command
---
<!--
SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
SPDX-License-Identifier: MPL-2.0
-->
# The `/computercraft` command
CC: Tweaked provides a `/computercraft` command for server owners to manage running computers on a server.
## Permissions {#permissions}
As the `/computercraft` command is mostly intended for debugging and administrative purposes, its sub-commands typically
require you to have op (or similar).
- All players have access to the [`queue`] sub-command.
- On a multi-player server, all other commands require op.
- On a single-player world, the player can run the [`dump`], [`turn-on`]/[`shutdown`], and [`track`] sub-commands, even
when cheats are not enabled. The [`tp`] and [`view`] commands require cheats.
If a permission mod such as [LuckPerms] is installed[^permission], you can configure access to the individual
sub-commands. Each sub-command creates a `computercraft.command.NAME` permission node to control which players can
execute it.
[LuckPerms]: https://github.com/LuckPerms/LuckPerms/ "A permissions plugin for Minecraft servers."
[fabric-permission-api]: https://github.com/lucko/fabric-permissions-api "A simple permissions API for Fabric"
[^permission]: This supports any mod which uses Forge's permission API or [fabric-permission-api].
## Computer selectors {#computer-selectors}
Some commands (such as [`tp`] or [`turn-on`]) target a specific computer, or a list of computers. To specify which
computers to operate on, you must use "computer selectors".
Computer selectors are similar to Minecraft's [entity target selectors], but targeting computers instead. They allow
you to select one or more computers, based on a set of predicates.
The following predicates are supported:
- `id=<id>`: Select computer(s) with a specific id.
- `instance=<id>`: Select the computer with the given instance id.
- `family=<normal|advanced|command>`: Select computers based on their type.
- `label=<label>`: Select computers with the given label.
- `distance=<distance>`: Select computers within a specific distance of the player executing the command. This uses
Minecraft's [float range] syntax.
`#<id>` may also be used as a shorthand for `@c[id=<id>]`, to select computer(s) with a specific id.
### Examples:
- `/computercraft turn-on #12`: Turn on the computer(s) with an id of 12.
- `/computercraft shutdown @c[distance=..100]`: Shut down all computers with 100 blocks of the player.
[entity target selectors]: https://minecraft.wiki/w/Target_selectors "Target Selectors on the Minecraft wiki"
[Float range]: https://minecraft.wiki/w/Argument_types#minecraft:float_range
## Commands {#commands}
### `/computercraft dump` {#dump}
`/computercraft dump` prints a table of currently loaded computers, including their id, position, and whether they're
running. It can also be run with a single computer argument to dump more detailed information about a computer.
![A screenshot of a Minecraft world. In the chat box, there is a table listing 5 computers, with columns labelled
"Computer", "On" and "Position". Below that, is a more detailed list of information about Computer 0, including its
label ("My computer") and that it has a monitor on the right hand side](../images/computercraft-dump.png "An example of
running '/computercraft dump'")
Next to the computer id, there are several buttons to either [teleport][`tp`] to the computer, or [open its terminal
][`view`].
Computers are sorted by distance to the player, so nearby computers will appear earlier.
### `/computercraft turn-on [computers...]` {#turn-on}
Turn on one or more computers or, if no run with no arguments, all loaded computers.
#### Examples
- `/computercraft turn-on #0 #2`: Turn on computers with id 0 and 2.
- `/computercraft turn-on @c[family=command]`: Turn on all command computers.
### `/computercraft shutdown [computers...]` {#shutdown}
Shutdown one or more computers or, if no run with no arguments, all loaded computers.
This is sometimes useful when dealing with lag, as a way to ensure that ComputerCraft is not causing problems.
#### Examples
- `/computercraft shutdown`: Shut down all loaded computers.
- `/computercraft shutdown @c[distance=..10]`: Shut down all computers in a block radius.
### `/computercraft tp [computer]` {#tp}
Teleport to the given computer.
This is normally used from via the [`dump`] command interface rather than being invoked directly.
### `/computercraft view [computer]` {#view}
Open a terminal for the specified computer. This allows remotely viewing computers without having to interact with the
block.
This is normally used from via the [`dump`] command interface rather than being invoked directly.
### `/computercraft track` {#track}
The `/computercraft track` command allows you to enable profiling of computers. When a computer runs code, or interacts
with the Minecraft world, we time how long that takes. This timing information may then be queried, and used to find
computers which may be causing lag.
To enable the profiler, run `/computercraft track start`. Computers will then start recording metrics. Once enough data
has been gathered, run `/computercraft track stop` to stop profiling and display the recorded data.
![](../images/computercraft-track.png)
The table by default shows the number of times each computer has run, and how long it ran for (in total, and on
average). In the above screenshot, we can see one computer was particularly badly behaved, and ran for 7 seconds. The
buttons may be used to [teleport][`tp`] to the computer, or [open its terminal ][`view`], and inspect it further.
`/computercraft track dump` can be used to display this table at any point (including while profiling is still running).
Computers also record other information, such as how much server-thread time they consume, or their HTTP bandwidth
usage. The `dump` subcommand accepts a list of other fields to display, instead of the default timings.
#### Examples
- `/computercraft track dump server_tasks_count server_tasks`: Print the number of server-thread tasks each computer
executed, and how long they took in total.
- `/computercraft track dump http_upload http_download`: Print the number of bytes uploaded and downloaded by each
computer.
### `/computercraft queue` {#queue}
The queue subcommand allows non-operator players to queue a `computer_command` event on *command* computers.
This has a similar purpose to vanilla's [`/trigger`] command. Command computers may choose to listen to this event, and
then perform some action.
[`/trigger`]: https://minecraft.wiki/w/Commands/trigger "/trigger on the Minecraft wiki"
[`dump`]: #dump "/computercraft dump"
[`queue`]: #queue "/computercraft queue"
[`shutdown`]: #shutdown "/computercraft shutdown"
[`tp`]: #tp "/computercraft tp"
[`track`]: #track "/computercraft track"
[`turn-on`]: #turn-on "/computercraft turn-on"
[`view`]: #view "/computercraft view"
[computer selectors]: #computer-selectors "Computer selectors"

View File

@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: MPL-2.0
org.gradle.jvmargs=-Xmx3G
org.gradle.jvmargs=-Xmx3G -Dfile.encoding=UTF-8
org.gradle.parallel=true
kotlin.stdlib.default.dependency=false
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
# Mod properties
isUnstable=false
modVersion=1.109.5
modVersion=1.112.0
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
mcVersion=1.20.1

View File

@@ -0,0 +1,2 @@
#This file is generated by updateDaemonJvm
toolchainVersion=17

View File

@@ -26,7 +26,7 @@ slf4j = "2.0.1"
asm = "9.6"
autoService = "1.1.1"
checkerFramework = "3.42.0"
cobalt = "0.9.0"
cobalt = "0.9.3"
commonsCli = "1.6.0"
jetbrainsAnnotations = "24.1.0"
jsr305 = "3.0.2"
@@ -46,33 +46,35 @@ oculus = "1.2.5"
rei = "12.0.626"
rubidium = "0.6.1"
sodium = "mc1.20-0.4.10"
create-forge = "0.5.1.f-33"
create-fabric = "0.5.1-f-build.1467+mc1.20.1"
# Testing
hamcrest = "2.2"
jqwik = "1.8.2"
junit = "5.10.1"
jmh = "1.37"
# Build tools
cctJavadoc = "1.8.2"
checkstyle = "10.12.6"
checkstyle = "10.14.1"
curseForgeGradle = "1.0.14"
errorProne-core = "2.23.0"
errorProne-core = "2.27.0"
errorProne-plugin = "3.1.0"
fabric-loom = "1.5.7"
forgeGradle = "6.0.20"
fabric-loom = "1.7.1"
forgeGradle = "6.0.21"
githubRelease = "2.5.2"
gradleVersions = "0.50.0"
ideaExt = "1.1.7"
illuaminate = "0.1.0-44-g9ee0055"
illuaminate = "0.1.0-73-g43ee16c"
librarian = "1.+"
lwjgl = "3.3.3"
minotaur = "2.+"
mixinGradle = "0.7.38"
nullAway = "0.9.9"
nullAway = "0.10.25"
spotless = "6.23.3"
taskTree = "2.1.1"
teavm = "0.10.0-SQUID.2"
vanillaExtract = "0.1.1"
teavm = "0.11.0-SQUID.1"
vanillaExtract = "0.1.3"
versionCatalogUpdate = "0.8.1"
[libraries]
@@ -100,11 +102,13 @@ nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref
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" }
create-fabric = { module = "com.simibubi.create:create-fabric-1.20.1", version.ref = "create-fabric" }
create-forge = { module = "com.simibubi.create:create-1.20.1", version.ref = "create-forge" }
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
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" }
@@ -127,6 +131,8 @@ junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.re
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
jmh = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" }
jmh-processor = { module = "org.openjdk.jmh:jmh-generator-annprocess", version.ref = "jmh" }
# LWJGL
lwjgl-bom = { module = "org.lwjgl:lwjgl-bom", version.ref = "lwjgl" }
@@ -152,6 +158,7 @@ minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur"
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
teavm-core = { module = "org.teavm:teavm-core", 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" }
@@ -168,7 +175,6 @@ githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "g
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
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" }
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

2
gradlew vendored
View File

@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.

20
gradlew.bat vendored
View File

@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail

View File

@@ -105,6 +105,10 @@
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua)
(linters -var:deprecated))
;; Suppress unused variable warnings in the parser.
(at /projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/parser.lua
(linters -var:unused))
(at /projects/core/src/test/resources/test-rom
; We should still be able to test deprecated members.
(linters -var:deprecated)

View File

@@ -4,9 +4,11 @@
package dan200.computercraft.api;
import dan200.computercraft.api.component.ComputerComponent;
import dan200.computercraft.api.filesystem.Mount;
import dan200.computercraft.api.filesystem.WritableMount;
import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.IComputerSystem;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaAPIFactory;
import dan200.computercraft.api.media.IMedia;
@@ -165,7 +167,20 @@ public final class ComputerCraftAPI {
* Register a custom {@link ILuaAPI}, which may be added onto all computers without requiring a peripheral.
* <p>
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
* to use peripherals to provide functionality to users.
* to use peripherals to provide functionality to users. If an API is <em>required</em>, you may want to consider
* using {@link ILuaAPI#getModuleName()} to expose this library as a module instead of as a global.
* <p>
* This may be used with {@link IComputerSystem#getComponent(ComputerComponent)} to only attach APIs to specific
* computers. For example, one can add an additional API just to turtles with the following code:
*
* <pre>{@code
* ComputerCraftAPI.registerAPIFactory(computer -> {
* // Read the turtle component.
* var turtle = computer.getComponent(ComputerComponents.TURTLE);
* // If present then add our API.
* return turtle == null ? null : new MyCustomTurtleApi(turtle);
* });
* }</pre>
*
* @param factory The factory for your API subclass.
* @see ILuaAPIFactory

View File

@@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.component;
import net.minecraft.commands.CommandSourceStack;
import org.jetbrains.annotations.ApiStatus;
/**
* A computer which has permission to perform administrative/op commands, such as the command computer.
*/
@ApiStatus.NonExtendable
public interface AdminComputer {
/**
* The permission level that this computer can operate at.
*
* @return The permission level for this computer.
* @see CommandSourceStack#hasPermission(int)
*/
default int permissionLevel() {
return 2;
}
}

View File

@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.component;
import dan200.computercraft.api.lua.IComputerSystem;
import dan200.computercraft.api.lua.ILuaAPIFactory;
/**
* A component attached to a computer.
* <p>
* Components provide a mechanism to attach additional data to a computer, that can then be queried with
* {@link IComputerSystem#getComponent(ComputerComponent)}.
* <p>
* This is largely designed for {@linkplain ILuaAPIFactory custom APIs}, allowing APIs to read additional properties
* of the computer, such as its position.
*
* @param <T> The type of this component.
* @see ComputerComponents The built-in components.
*/
@SuppressWarnings("UnusedTypeParameter")
public final class ComputerComponent<T> {
private final String id;
private ComputerComponent(String id) {
this.id = id;
}
/**
* Create a new computer component.
* <p>
* Mods typically will not need to create their own components.
*
* @param namespace The namespace of this component. This should be the mod id.
* @param id The unique id of this component.
* @param <T> The component
* @return The newly created component.
*/
public static <T> ComputerComponent<T> create(String namespace, String id) {
return new ComputerComponent<>(namespace + ":" + id);
}
@Override
public String toString() {
return "ComputerComponent(" + id + ")";
}
}

View File

@@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.component;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.api.turtle.ITurtleAccess;
/**
* The {@link ComputerComponent}s provided by ComputerCraft.
*/
public class ComputerComponents {
/**
* The {@link ITurtleAccess} associated with a turtle.
*/
public static final ComputerComponent<ITurtleAccess> TURTLE = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "turtle");
/**
* The {@link IPocketAccess} associated with a pocket computer.
*/
public static final ComputerComponent<IPocketAccess> POCKET = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "pocket");
/**
* This component is only present on "command computers", and other computers with admin capabilities.
*/
public static final ComputerComponent<AdminComputer> ADMIN_COMPUTER = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "admin_computer");
}

View File

@@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.lua;
import dan200.computercraft.api.component.ComputerComponent;
import dan200.computercraft.api.peripheral.IComputerAccess;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable;
/**
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information
* about a computer.
*/
@ApiStatus.NonExtendable
public interface IComputerSystem extends IComputerAccess {
/**
* Get the level this computer is currently in.
* <p>
* This method is not guaranteed to remain the same (even for stationary computers).
*
* @return The computer's current level.
*/
ServerLevel getLevel();
/**
* Get the position this computer is currently at.
* <p>
* This method is not guaranteed to remain the same (even for stationary computers).
*
* @return The computer's current position.
*/
BlockPos getPosition();
/**
* Get the label for this computer.
*
* @return This computer's label, or {@code null} if it is not set.
*/
@Nullable
String getLabel();
/**
* Get a component attached to this computer.
* <p>
* No component is guaranteed to be on a computer, and so this method should always be guarded with a null check.
* <p>
* This method will always return the same value for a given component, and so may be cached.
*
* @param component The component to query.
* @param <T> The type of the component.
* @return The component, if present.
*/
<T> @Nullable T getComponent(ComputerComponent<T> component);
}

View File

@@ -4,13 +4,15 @@
package dan200.computercraft.api.lua;
import dan200.computercraft.api.ComputerCraftAPI;
import javax.annotation.Nullable;
/**
* Construct an {@link ILuaAPI} for a specific computer.
* Construct an {@link ILuaAPI} for a computer.
*
* @see ILuaAPI
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
*/
@FunctionalInterface
public interface ILuaAPIFactory {

View File

@@ -5,6 +5,7 @@
package dan200.computercraft.api.network.wired;
import dan200.computercraft.api.peripheral.IPeripheral;
import org.jetbrains.annotations.ApiStatus;
import java.util.Map;
@@ -22,6 +23,7 @@ import java.util.Map;
*
* @see WiredNode#getNetwork()
*/
@ApiStatus.NonExtendable
public interface WiredNetwork {
/**
* Create a connection between two nodes.
@@ -35,7 +37,9 @@ public interface WiredNetwork {
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
* @see WiredNode#connectTo(WiredNode)
* @see WiredNetwork#connect(WiredNode, WiredNode)
* @deprecated Use {@link WiredNode#connectTo(WiredNode)}
*/
@Deprecated
boolean connect(WiredNode left, WiredNode right);
/**
@@ -50,7 +54,9 @@ public interface WiredNetwork {
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
* @see WiredNode#disconnectFrom(WiredNode)
* @see WiredNetwork#connect(WiredNode, WiredNode)
* @deprecated Use {@link WiredNode#disconnectFrom(WiredNode)}
*/
@Deprecated
boolean disconnect(WiredNode left, WiredNode right);
/**
@@ -64,7 +70,9 @@ public interface WiredNetwork {
* only element.
* @throws IllegalArgumentException If the node is not in the network.
* @see WiredNode#remove()
* @deprecated Use {@link WiredNode#remove()}
*/
@Deprecated
boolean remove(WiredNode node);
/**
@@ -77,6 +85,8 @@ public interface WiredNetwork {
* @param peripherals The new peripherals for this node.
* @throws IllegalArgumentException If the node is not in the network.
* @see WiredNode#updatePeripherals(Map)
* @deprecated Use {@link WiredNode#updatePeripherals(Map)}
*/
@Deprecated
void updatePeripherals(WiredNode node, Map<String, IPeripheral> peripherals);
}

View File

@@ -6,6 +6,7 @@ package dan200.computercraft.api.network.wired;
import dan200.computercraft.api.network.PacketNetwork;
import dan200.computercraft.api.peripheral.IPeripheral;
import org.jetbrains.annotations.ApiStatus;
import java.util.Map;
@@ -22,6 +23,7 @@ import java.util.Map;
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever
* be used on the main server thread.
*/
@ApiStatus.NonExtendable
public interface WiredNode extends PacketNetwork {
/**
* The associated element for this network node.
@@ -37,7 +39,9 @@ public interface WiredNode extends PacketNetwork {
* This should only be used on the server thread.
*
* @return This node's network.
* @deprecated Use the connect/disconnect/remove methods on {@link WiredNode}.
*/
@Deprecated
WiredNetwork getNetwork();
/**
@@ -47,12 +51,9 @@ public interface WiredNode extends PacketNetwork {
*
* @param node The other node to connect to.
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
* @see WiredNetwork#connect(WiredNode, WiredNode)
* @see WiredNode#disconnectFrom(WiredNode)
*/
default boolean connectTo(WiredNode node) {
return getNetwork().connect(this, node);
}
boolean connectTo(WiredNode node);
/**
* Destroy a connection between this node and another.
@@ -61,13 +62,9 @@ public interface WiredNode extends PacketNetwork {
*
* @param node The other node to disconnect from.
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
* @throws IllegalArgumentException If {@code node} is not on the same network.
* @see WiredNetwork#disconnect(WiredNode, WiredNode)
* @see WiredNode#connectTo(WiredNode)
*/
default boolean disconnectFrom(WiredNode node) {
return getNetwork().disconnect(this, node);
}
boolean disconnectFrom(WiredNode node);
/**
* Sever all connections this node has, removing it from this network.
@@ -78,11 +75,8 @@ public interface WiredNode extends PacketNetwork {
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
* only element.
* @throws IllegalArgumentException If the node is not in the network.
* @see WiredNetwork#remove(WiredNode)
*/
default boolean remove() {
return getNetwork().remove(this);
}
boolean remove();
/**
* Mark this node's peripherals as having changed.
@@ -91,9 +85,6 @@ public interface WiredNode extends PacketNetwork {
* that your network element owns.
*
* @param peripherals The new peripherals for this node.
* @see WiredNetwork#updatePeripherals(WiredNode, Map)
*/
default void updatePeripherals(Map<String, IPeripheral> peripherals) {
getNetwork().updatePeripherals(this, peripherals);
}
void updatePeripherals(Map<String, IPeripheral> peripherals);
}

View File

@@ -6,10 +6,14 @@ package dan200.computercraft.api.pocket;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeData;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable;
import java.util.Map;
@@ -17,7 +21,22 @@ import java.util.Map;
/**
* Wrapper class for pocket computers.
*/
@ApiStatus.NonExtendable
public interface IPocketAccess {
/**
* Get the level in which the pocket computer exists.
*
* @return The pocket computer's level.
*/
ServerLevel getLevel();
/**
* Get the position of the pocket computer.
*
* @return The pocket computer's position.
*/
Vec3 getPosition();
/**
* Gets the entity holding this item.
* <p>
@@ -64,6 +83,26 @@ public interface IPocketAccess {
*/
void setLight(int colour);
/**
* Get the currently equipped upgrade.
*
* @return The currently equipped upgrade.
* @see #getUpgradeNBTData()
* @see #setUpgrade(UpgradeData)
*/
@Nullable
UpgradeData<IPocketUpgrade> getUpgrade();
/**
* Set the upgrade for this pocket computer, also updating the item stack.
* <p>
* Note this method is not thread safe - it must be called from the server thread.
*
* @param upgrade The new upgrade to set it to, may be {@code null}.
* @see #getUpgrade()
*/
void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade);
/**
* Get the upgrade-specific NBT.
* <p>
@@ -73,6 +112,7 @@ public interface IPocketAccess {
* @see #updateUpgradeNBTData()
* @see UpgradeBase#getUpgradeItem(CompoundTag)
* @see UpgradeBase#getUpgradeData(ItemStack)
* @see #getUpgrade()
*/
CompoundTag getUpgradeNBTData();

View File

@@ -29,7 +29,7 @@ public final class Services {
* @throws IllegalStateException When the service cannot be loaded.
*/
public static <T> T load(Class<T> klass) {
var services = ServiceLoader.load(klass).stream().toList();
var services = ServiceLoader.load(klass, klass.getClassLoader()).stream().toList();
return switch (services.size()) {
case 1 -> services.get(0).get();
case 0 -> throw new IllegalStateException("Cannot find service for " + klass.getName());

View File

@@ -11,6 +11,12 @@ plugins {
id("cc-tweaked.publishing")
}
sourceSets {
main {
resources.srcDir("src/generated/resources")
}
}
minecraft {
accessWideners(
"src/main/resources/computercraft.accesswidener",
@@ -37,6 +43,7 @@ dependencies {
clientImplementation(clientClasses(project(":common-api")))
compileOnly(libs.bundles.externalMods.common)
compileOnly(variantOf(libs.create.forge) { classifier("slim") }) { isTransitive = false }
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
annotationProcessorEverywhere(libs.autoService)
@@ -46,6 +53,9 @@ dependencies {
testImplementation(libs.bundles.test)
testRuntimeOnly(libs.bundles.testRuntime)
testImplementation(libs.jmh)
testAnnotationProcessor(libs.jmh.processor)
testModCompileOnly(libs.mixin)
testModImplementation(testFixtures(project(":core")))
testModImplementation(testFixtures(project(":common")))
@@ -102,3 +112,21 @@ val lintLua by tasks.registering(IlluaminateExec::class) {
doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
}
val runData by tasks.registering(MergeTrees::class) {
output = layout.projectDirectory.dir("src/generated/resources")
for (loader in listOf("forge", "fabric")) {
mustRunAfter(":$loader:runData")
source {
input {
from(project(":$loader").layout.buildDirectory.dir("generatedResources"))
exclude(".cache")
}
output = project(":$loader").layout.projectDirectory.dir("src/generated/resources")
}
}
}
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }

View File

@@ -22,9 +22,9 @@ import dan200.computercraft.core.util.Colour;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerContext;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
import dan200.computercraft.shared.media.items.DiskItem;
import dan200.computercraft.shared.media.items.TreasureDiskItem;
import net.minecraft.Util;
@@ -77,10 +77,11 @@ public final class ClientRegistry {
/**
* Register any client-side objects which must be done on the main thread.
*
* @param itemProperties Callback to register item properties.
*/
public static void registerMainThread() {
public static void registerMainThread(RegisterItemProperty itemProperties) {
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
MenuScreens.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
MenuScreens.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
@@ -88,13 +89,14 @@ public final class ClientRegistry {
MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
MenuScreens.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
registerItemProperty("state",
new UnclampedPropertyFunction((stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal()),
registerItemProperty(itemProperties, "state",
new UnclampedPropertyFunction((stack, world, player, random) -> {
var computer = ClientPocketComputers.get(stack);
return (computer == null ? ComputerState.OFF : computer.getState()).ordinal();
}),
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
);
registerItemProperty("coloured",
registerItemProperty(itemProperties, "coloured",
(stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
);
@@ -115,9 +117,17 @@ public final class ClientRegistry {
}
@SafeVarargs
private static void registerItemProperty(String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
private static void registerItemProperty(RegisterItemProperty itemProperties, String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
for (var item : items) ItemProperties.register(item.get(), id, getter);
for (var item : items) itemProperties.register(item.get(), id, getter);
}
/**
* Register an item property via {@link ItemProperties#register}. Forge and Fabric expose different methods, so we
* supply this via mod-loader-specific code.
*/
public interface RegisterItemProperty {
void register(Item item, ResourceLocation name, ClampedItemPropertyFunction property);
}
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
@@ -155,17 +165,14 @@ public final class ClientRegistry {
}
private static int getPocketColour(ItemStack stack, int layer) {
switch (layer) {
case 0:
default:
return 0xFFFFFF;
case 1: // Frame colour
return IColouredItem.getColourBasic(stack);
case 2: { // Light colour
var light = ClientPocketComputers.get(stack).getLightState();
return light == -1 ? Colour.BLACK.getHex() : light;
return switch (layer) {
default -> 0xFFFFFF;
case 1 -> IColouredItem.getColourBasic(stack); // Frame colour
case 2 -> { // Light colour
var computer = ClientPocketComputers.get(stack);
yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
}
}
};
}
private static int getTurtleColour(ItemStack stack, int layer) {

View File

@@ -9,6 +9,7 @@ import dan200.computercraft.client.gui.widgets.DynamicImageButton;
import dan200.computercraft.client.gui.widgets.TerminalWidget;
import dan200.computercraft.client.network.ClientNetworking;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.InputHandler;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
@@ -18,6 +19,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.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
@@ -96,8 +98,8 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
getTerminal().update();
if (uploadNagDeadline != Long.MAX_VALUE && Util.getNanos() >= uploadNagDeadline) {
new ItemToast(minecraft, displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
.showOrReplace(minecraft.getToasts());
new ItemToast(minecraft(), displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
.showOrReplace(minecraft().getToasts());
uploadNagDeadline = Long.MAX_VALUE;
}
}
@@ -207,7 +209,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
return;
}
if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
if (!toUpload.isEmpty()) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
}
public void uploadResult(UploadResult result, @Nullable Component message) {
@@ -223,9 +225,13 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
}
private void alert(Component title, Component message) {
OptionScreen.show(minecraft, title, message,
List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
() -> minecraft.setScreen(this)
OptionScreen.show(minecraft(), title, message,
List.of(OptionScreen.newButton(OK, b -> minecraft().setScreen(this))),
() -> minecraft().setScreen(this)
);
}
private Minecraft minecraft() {
return Nullability.assertNonNull(minecraft);
}
}

View File

@@ -49,31 +49,31 @@ public final class ClientInputHandler implements InputHandler {
@Override
public void keyDown(int key, boolean repeat) {
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key));
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.Action.REPEAT : KeyEventServerMessage.Action.DOWN, key));
}
@Override
public void keyUp(int key) {
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key));
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.Action.UP, key));
}
@Override
public void mouseClick(int button, int x, int y) {
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y));
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.CLICK, button, x, y));
}
@Override
public void mouseUp(int button, int x, int y) {
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y));
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.UP, button, x, y));
}
@Override
public void mouseDrag(int button, int x, int y) {
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y));
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.DRAG, button, x, y));
}
@Override
public void mouseScroll(int direction, int x, int y) {
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y));
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.SCROLL, direction, x, y));
}
}

View File

@@ -6,8 +6,10 @@ package dan200.computercraft.client.gui;
import dan200.computercraft.client.gui.widgets.TerminalWidget;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.MenuAccess;
@@ -16,6 +18,7 @@ import net.minecraft.world.entity.player.Inventory;
import org.lwjgl.glfw.GLFW;
import javax.annotation.Nullable;
import java.util.Objects;
import static dan200.computercraft.core.util.Nullability.assertNonNull;
@@ -44,8 +47,8 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
protected void init() {
// 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();
minecraft.screen = this;
minecraft().mouseHandler.grabMouse();
minecraft().screen = this;
KeyMapping.releaseAll();
super.init();
@@ -64,13 +67,13 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
@Override
public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) {
minecraft.player.getInventory().swapPaint(pDelta);
Objects.requireNonNull(minecraft().player).getInventory().swapPaint(pDelta);
return super.mouseScrolled(pMouseX, pMouseY, pDelta);
}
@Override
public void onClose() {
minecraft.player.closeContainer();
Objects.requireNonNull(minecraft().player).closeContainer();
super.onClose();
}
@@ -93,12 +96,16 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
super.render(graphics, mouseX, mouseY, partialTicks);
var font = minecraft.font;
var font = minecraft().font;
var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8));
var y = 10;
for (var line : lines) {
graphics.drawString(font, line, (width / 2) - (minecraft.font.width(line) / 2), y, 0xFFFFFF, true);
graphics.drawString(font, line, (width / 2) - (font.width(line) / 2), y, 0xFFFFFF, true);
y += 9;
}
}
private Minecraft minecraft() {
return Nullability.assertNonNull(minecraft);
}
}

View File

@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.integration.jei;
package dan200.computercraft.client.integration.jei;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.turtle.TurtleSide;

View File

@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.integration.jei;
package dan200.computercraft.client.integration.jei;
import dan200.computercraft.shared.integration.UpgradeRecipeGenerator;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;

View File

@@ -17,6 +17,7 @@ import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.client.ClientNetworkContext;
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
@@ -27,7 +28,6 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.UUID;
/**
@@ -49,7 +49,7 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
}
@Override
public void handleMonitorData(BlockPos pos, TerminalState terminal) {
public void handleMonitorData(BlockPos pos, @Nullable TerminalState terminal) {
var player = Minecraft.getInstance().player;
if (player == null) return;
@@ -67,19 +67,17 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
}
@Override
public void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) {
var computer = ClientPocketComputers.get(instanceId, terminal.colour);
computer.setState(state, lightState);
if (terminal.hasTerminal()) computer.setTerminal(terminal);
public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, @Nullable TerminalState terminal) {
ClientPocketComputers.setState(instanceId, state, lightState, terminal);
}
@Override
public void handlePocketComputerDeleted(int instanceId) {
public void handlePocketComputerDeleted(UUID instanceId) {
ClientPocketComputers.remove(instanceId);
}
@Override
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer buffer) {
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, EncodedAudio buffer) {
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer);
}

View File

@@ -4,21 +4,25 @@
package dan200.computercraft.client.pocket;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* Maps {@link ServerComputer#getInstanceID()} to locals {@link PocketComputerData}.
* Maps {@link ServerComputer#getInstanceUUID()} to locals {@link PocketComputerData}.
* <p>
* This is populated by {@link PocketComputerDataMessage} and accessed when rendering pocket computers
*/
public final class ClientPocketComputers {
private static final Int2ObjectMap<PocketComputerData> instances = new Int2ObjectOpenHashMap<>();
private static final Map<UUID, PocketComputerData> instances = new HashMap<>();
private ClientPocketComputers() {
}
@@ -27,25 +31,29 @@ public final class ClientPocketComputers {
instances.clear();
}
public static void remove(int id) {
public static void remove(UUID id) {
instances.remove(id);
}
/**
* Get or create a pocket computer.
* Set the state of a pocket computer.
*
* @param instanceId The instance ID of the pocket computer.
* @param advanced Whether this computer has an advanced terminal.
* @return The pocket computer data.
* @param instanceId The instance ID of the pocket computer.
* @param state The computer state of the pocket computer.
* @param lightColour The current colour of the modem light.
* @param terminalData The current terminal contents.
*/
public static PocketComputerData get(int instanceId, boolean advanced) {
public static void setState(UUID instanceId, ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
var computer = instances.get(instanceId);
if (computer == null) instances.put(instanceId, computer = new PocketComputerData(advanced));
return computer;
if (computer == null) {
instances.put(instanceId, new PocketComputerData(state, lightColour, terminalData));
} else {
computer.setState(state, lightColour, terminalData);
}
}
public static PocketComputerData get(ItemStack stack) {
var family = stack.getItem() instanceof PocketComputerItem computer ? computer.getFamily() : ComputerFamily.NORMAL;
return get(PocketComputerItem.getInstanceID(stack), family != ComputerFamily.NORMAL);
public static @Nullable PocketComputerData get(ItemStack stack) {
var id = PocketComputerItem.getInstanceID(stack);
return id == null ? null : instances.get(id);
}
}

View File

@@ -4,13 +4,13 @@
package dan200.computercraft.client.pocket;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
import javax.annotation.Nullable;
/**
* Clientside data about a pocket computer.
* <p>
@@ -21,20 +21,22 @@ import dan200.computercraft.shared.pocket.core.PocketServerComputer;
* @see ClientPocketComputers The registry which holds pocket computers.
* @see PocketServerComputer The server-side pocket computer.
*/
public class PocketComputerData {
private final NetworkedTerminal terminal;
private ComputerState state = ComputerState.OFF;
private int lightColour = -1;
public final class PocketComputerData {
private @Nullable NetworkedTerminal terminal;
private ComputerState state;
private int lightColour;
public PocketComputerData(boolean colour) {
terminal = new NetworkedTerminal(Config.pocketTermWidth, Config.pocketTermHeight, colour);
PocketComputerData(ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
this.state = state;
this.lightColour = lightColour;
if (terminalData != null) terminal = terminalData.create();
}
public int getLightState() {
return state != ComputerState.OFF ? lightColour : -1;
}
public Terminal getTerminal() {
public @Nullable NetworkedTerminal getTerminal() {
return terminal;
}
@@ -42,12 +44,16 @@ public class PocketComputerData {
return state;
}
public void setState(ComputerState state, int lightColour) {
void setState(ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
this.state = state;
this.lightColour = lightColour;
}
public void setTerminal(TerminalState state) {
state.apply(terminal);
if (terminalData != null) {
if (terminal == null) {
terminal = terminalData.create();
} else {
terminalData.apply(terminal);
}
}
}
}

View File

@@ -17,6 +17,8 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import java.util.Objects;
/**
* A base class for items which have map-like rendering when held in the hand.
*
@@ -35,7 +37,7 @@ public abstract class ItemMapLikeRenderer {
protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light);
public void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
Player player = Minecraft.getInstance().player;
Player player = Objects.requireNonNull(Minecraft.getInstance().player);
transform.pushPose();
if (hand == InteractionHand.MAIN_HAND && player.getOffhandItem().isEmpty()) {

View File

@@ -11,6 +11,7 @@ import dan200.computercraft.client.pocket.ClientPocketComputers;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.util.Colour;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.item.ItemStack;
@@ -32,10 +33,16 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
@Override
protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) {
var computer = ClientPocketComputers.get(stack);
var terminal = computer.getTerminal();
var terminal = computer == null ? null : computer.getTerminal();
var termWidth = terminal.getWidth();
var termHeight = terminal.getHeight();
int termWidth, termHeight;
if (terminal == null) {
termWidth = Config.pocketTermWidth;
termHeight = Config.pocketTermHeight;
} else {
termWidth = terminal.getWidth();
termHeight = terminal.getHeight();
}
var width = termWidth * FONT_WIDTH + MARGIN * 2;
var height = termHeight * FONT_HEIGHT + MARGIN * 2;
@@ -60,14 +67,15 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
// Render the light
var lightColour = ClientPocketComputers.get(stack).getLightState();
if (lightColour == -1) lightColour = Colour.BLACK.getHex();
var lightColour = computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
renderLight(transform, bufferSource, lightColour, width, height);
FixedWidthFontRenderer.drawTerminal(
FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL)),
MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN
);
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL));
if (terminal == null) {
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, width, height);
} else {
FixedWidthFontRenderer.drawTerminal(quadEmitter, MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
}
transform.popPose();
}

View File

@@ -55,7 +55,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
// Render the label
var label = turtle.getLabel();
var hit = renderer.cameraHitResult;
if (label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
if (label != null && hit != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
var mc = Minecraft.getInstance();
var font = this.font;

View File

@@ -58,9 +58,9 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
@Override
public void render(MonitorBlockEntity monitor, float partialTicks, PoseStack transform, MultiBufferSource bufferSource, int lightmapCoord, int overlayLight) {
// Render from the origin monitor
var originTerminal = monitor.getClientMonitor();
var originTerminal = monitor.getOriginClientMonitor();
if (originTerminal == null) return;
var origin = originTerminal.getOrigin();
var renderState = originTerminal.getRenderState(MonitorRenderState::new);
var monitorPos = monitor.getBlockPos();

View File

@@ -5,6 +5,7 @@
package dan200.computercraft.client.sound;
import com.mojang.blaze3d.audio.Channel;
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.client.sounds.AudioStream;
@@ -36,7 +37,7 @@ class DfpwmStream implements AudioStream {
/**
* The {@link Channel} which this sound is playing on.
*
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
*/
@Nullable
Channel channel;
@@ -44,21 +45,23 @@ class DfpwmStream implements AudioStream {
/**
* The underlying {@link SoundEngine} executor.
*
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
* @see SoundEngine#executor
*/
@Nullable
Executor executor;
private int charge = 0; // q
private int strength = 0; // s
private int lowPassCharge;
private boolean previousBit = false;
DfpwmStream() {
}
void push(ByteBuffer input) {
void push(EncodedAudio audio) {
var charge = audio.charge();
var strength = audio.strength();
var previousBit = audio.previousBit();
var input = audio.audio();
var readable = input.remaining();
var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder());

View File

@@ -6,12 +6,12 @@ package dan200.computercraft.client.sound;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nullable;
import java.nio.ByteBuffer;
/**
* An instance of a speaker, which is either playing a {@link DfpwmStream} stream or a normal sound.
@@ -25,7 +25,7 @@ public class SpeakerInstance {
SpeakerInstance() {
}
private void pushAudio(ByteBuffer buffer) {
private void pushAudio(EncodedAudio buffer) {
var sound = this.sound;
var stream = currentStream;
@@ -43,7 +43,7 @@ public class SpeakerInstance {
}
}
public void playAudio(SpeakerPosition position, float volume, ByteBuffer buffer) {
public void playAudio(SpeakerPosition position, float volume, EncodedAudio buffer) {
pushAudio(buffer);
var soundManager = Minecraft.getInstance().getSoundManager();

View File

@@ -1,8 +1,14 @@
{
"argument.computercraft.argument_expected": "Argument expected",
"argument.computercraft.computer.distance": "Distance to entity",
"argument.computercraft.computer.family": "Computer family",
"argument.computercraft.computer.id": "Computer ID",
"argument.computercraft.computer.instance": "Unique instance ID",
"argument.computercraft.computer.label": "Computer label",
"argument.computercraft.computer.many_matching": "Multiple computers matching '%s' (instances %s)",
"argument.computercraft.computer.no_matching": "No computers matching '%s'",
"argument.computercraft.tracking_field.no_field": "Unknown field '%s'",
"argument.computercraft.unknown_computer_family": "Unknown computer family '%s'",
"block.computercraft.cable": "Networking Cable",
"block.computercraft.computer_advanced": "Advanced Computer",
"block.computercraft.computer_command": "Command Computer",

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