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

Compare commits

..

55 Commits

Author SHA1 Message Date
Jonathan Coates
8be6b1b772 Bump CC:T to 1.109.3 2024-01-03 20:00:52 +00:00
Jonathan Coates
104d5e70de Update to Cobalt 0.8.2
- Fix error when using "goto" as the first statement in an if block.
 - Fix incorrect resizing of table's hash part when adding and removing
   keys.
2024-01-03 18:44:37 +00:00
Jonathan Coates
e3bda2f763 Add command computers to the operator blocks tab
Fixes #1666
2024-01-03 18:42:31 +00:00
Jonathan Coates
234f69e8e5 Add a MessageType for network messages
Everything old is new again!

CC's network message implementation has gone through several iterations:

 - Originally network messages were implemented with a single class,
   which held an packet id/type and and opaque blobs of data (as
   string/int/byte/NBT arrays), and a big switch statement to decode and
   process this data.

 - In 42d3901ee3, we split the messages
   into different classes all inheriting from NetworkMessage - this bit
   we've stuck with ever since.

   Each packet had a `getId(): int` method, which returned the
   discriminator for this packet.

 - However, getId() was only used when registering the packet, not when
   sending, and so in ce0685c31f we
   removed it, just passing in a constant integer at registration
   instead.

 - In 53abe5e56e, we made some relatively
   minor changes to make the code more multi-loader/split-source
   friendly. However, this meant when we finally came to add Fabric
   support (8152f19b6e), we had to
   re-implement a lot of Forge's network code.

In 1.20.4, Forge moves to a system much closer to Fabric's (and indeed,
Minecraft's own CustomPacketPayload), and so it makes sense to adapt to
that now. As such, we:

 - Add a new MessageType interface. This is implemented by the
   loader-specific modules, and holds whatever information is needed to
   register the packet (e.g. discriminator, reader function).

 - Each NetworkMessage now has a type(): MessageType<?> function. This
   is used by the Fabric networking code (and for NeoForge's on 1.20.4)
   instead of a class lookup.

 - NetworkMessages now creates/stores these MessageType<T>s (much like
   we'd do for registries), and provides getters for the
   clientbound/serverbound messages. Mod initialisers then call these
   getters to register packets.

 - For Forge, this is relatively unchanged. For Fabric, we now
   `FabricPacket`s.
2024-01-03 10:23:41 +00:00
Jonathan Coates
ed3a17f9b9 Fix trailing-comma errors on method calls
We were only matching `f(a, ` patterns, and not `x:f(a, `. We now just
match against any usages of call_args - hadn't quite realised we could
do that!
2023-12-28 17:07:39 +00:00
Jonathan Coates
0349c2b1f9 Allow local domains in the standalone emulator
- Clean up option parsing a bit, so it uses the Option, rather than its
   corresponding character code.
 - Add a new -L/--allow-local-domains flag to remove the $private rule
   from the HTTP rules.
2023-12-20 17:43:08 +00:00
Jonathan Coates
03f9e6bd6d Prevent sending too many websocket messages at once 2023-12-20 17:30:57 +00:00
Jonathan Coates
9d8c933a14 Remove several usages of ComputerFamily
While ComputerFamily is still useful, there's definitely some places
where it adds an extra layer of indirection. This commit attempts to
clean up some places where we no longer need it.

 - Remove ComputerFamily from AbstractComputerBlock. The only place this
   was needed is in TurtleBlock, and that can be replaced with normal
   Minecraft explosion resistence!

 - Pass in the fuel limit to the turtle block entity, rather than
   deriving it from current family.

 - The turtle BERs now derive their model from the turtle's item, rather
   than the turtle's family.

 - When creating upgrade/overlay recipes, use the item's name, rather
   than {pocket,turtle}_family. This means we can drop getFamily() from
   IComputerItem (it is still needed on to handle the UI).

 - We replace IComputerItem.withFamily with a method to change to a
   different item of the same type. ComputerUpgradeRecipe no longer
   takes a family, and instead just uses the result's item.

 - Computer blocks now use the normal Block.asItem() to find their
   corresponding item, rather than looking it up via family.

The above means we can remove all the family-based XyzItem.create(...)
methods, which have always felt a little ugly.

We still need ComputerFamily for a couple of things:
 - Permission checks for command computers.
 - Checks for mouse/colour support in ServerComputer.
 - UI textures.
2023-12-20 14:17:38 +00:00
Jonathan Coates
78bb3da58c Improve our version tooling
- Add a check to ensure declared dependencies in the :core project, and
   those inherited from Minecraft are the same.
 - Compute the next Cobalt version, rather than specifying it manually.
 - Add the gradle versions plugin (and version catalog update), and
   update some versions.
2023-12-19 18:12:21 +00:00
Jonathan Coates
39a5e40c92 Bump CC:T to 1.109.2
Take two!
2023-12-16 22:46:30 +00:00
Jonathan Coates
763ba51919 Update Cobalt to 0.8.1 2023-12-16 22:39:48 +00:00
Jonathan Coates
cf6ec8c28f Add a slightly cleaner system for excluding deps
Previously we prevented our published full jar depending on any of the
other projects by excluding the whole cc.tweaked jar. However, as Cobalt
also now lives in that group, this meant we were missing the Cobalt
dependency.

Rather than specifying a wildcard, we now exclude the dependencies when
adding them to the project.
2023-12-16 22:35:15 +00:00
Jonathan Coates
95d3b646b2 Bump CC:T to 1.109.1 2023-12-16 19:09:39 +00:00
Jonathan Coates
488f66eead Fix mouse_drag not firing for right/middle buttons
This is a bit of an odd combination of a few bugs:
 - When the terminal component is blurred, we fire a mouse_up event for
   the last-held button. However, we had an off-by-1 error here, so this
   only triggered for the right/middle buttons.

 - This was obsucuring the second bug, which is when we clicked within
   the terminal, this caused the terminal to be blurred (thus releasing
   the mouse) and then focused again.

   We fix this by only setting the focus if there's actually a change.

Fixes #1655
2023-12-10 12:01:34 +00:00
Jonathan Coates
1f7d245876 Specify charset when printing error messages 2023-12-08 09:52:17 +00:00
Jonathan Coates
af12b3a0ea Fix goto/:: tokens erroring in error reporting 2023-12-07 19:47:39 +00:00
Jonathan Coates
eb3e8ba677 Fix deadlock when adding/removing observers
When adding/removing observers, we locked on the observer, then
acquired the global lock. When a metric is observed, then we acquire the
global lock and then the observer lock.

If these happen at the same time, we can easily end up with a deadlock.
We simply avoid holding the observer lock for the entire add/remove
process (instead only locking when actually needed).

Closes #1639
2023-12-01 12:33:03 +00:00
Jonathan Coates
2043939531 Add compostors to the list of usable blocks
Fixes #1638
2023-11-22 18:24:59 +00:00
Jonathan Coates
84a799d27a Add abstract classes for our generic peripherals
This commit adds abstract classes to describe the interface for our
mod-loader-specific generic peripherals (inventories, fluid storage,
item storage).

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

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

Note, this does /not/ implement fluid or energy storage for Fabric. We
probably could do fluid without issue, but not something worth doing
right now.
2023-11-22 18:20:15 +00:00
Jonathan Coates
fe826f5c9c Allow generic sources to have instance methods
Rather than assuming static methods are generic, and instance methods
are direct, the Generator now has separate entrypoints for handling
instance and generic methods.

As a result of this change, we've also relaxed some of the validation
code. As a result, we now allow calling private/protected methods
which are annotated with @LuaFunction.
2023-11-22 10:06:11 +00:00
Jonathan Coates
f8b7422294 Fix several issues with the web emulator
- Bump TeaVM version to fix issues with locales and our "export"
   generation.
 - Fix TComputerThread not requeuing correctly.
2023-11-15 13:12:31 +00:00
Jonathan Coates
b343c01216 Bump CC:T to 1.109.0 2023-11-15 09:39:52 +00:00
Jonathan Coates
76968f2f28 Track allocations while executing computers
This adds a new "java_allocation" metric, which tracks the number of
bytes allocated while executing the computer (as measured by Java). This
is not an 100% reliable number, but hopefully gives some insight into
what computers are doing.
2023-11-09 18:36:35 +00:00
Jonathan Coates
1d365f5a0b Add option to allow repetition in JSON serialiser
Closes #1588
2023-11-08 21:29:43 +00:00
Jonathan Coates
7b240cbf7e Merge pull request #1615 from cc-tweaked/feature/much-breakage-very-wow
Remove text mode, update Cobalt
2023-11-08 20:05:49 +00:00
Jonathan Coates
d272a327c7 Update CraftOS version to 1.9 2023-11-08 19:40:14 +00:00
Jonathan Coates
0c0556a5bc Always use raw bytes in file handles
Historically CC has supported two modes when working with file handles
(and HTTP requests):

 - Text mode, which reads/write using UTF-8.
 - Binary mode, which reads/writes the raw bytes.

However, this can be confusing at times. CC/Lua doesn't actually support
unicode, so any characters beyond the 0.255 range were replaced with
'?'. This meant that most of the time you were better off just using
binary mode.

This commit unifies text and binary mode - we now /always/ read the raw
bytes of the file, rather than converting to/from UTF-8. Binary mode now
only specifies whether handle.read() returns a number (and .write(123)
writes a byte rather than coercing to a string).

 - Refactor the entire handle hierarchy. We now have an AbstractMount
   base class, which has the concrete implementation of all methods. The
   public-facing classes then re-export these methods by annotating
   them with @LuaFunction.

   These implementations are based on the
   Binary{Readable,Writable}Handle classes. The Encoded{..}Handle
   versions are now entirely removed.

 - As we no longer need to use BufferedReader/BufferedWriter, we can
   remove quite a lot of logic in Filesystem to handle wrapping
   closeable objects.

 - Add a new WritableMount.openFile method, which generalises
   openForWrite/openForAppend to accept OpenOptions. This allows us to
   support update mode (r+, w+) in fs.open.

 - fs.open now uses the new handle types, and supports update (r+, w+)
   mode.

 - http.request now uses the new readable handle type. We no longer
   encode the request body to UTF-8, nor decode the response from UTF-8.

 - Websockets now return text frame's contents directly, rather than
   converting it from UTF-8. Sending text frames now attempts to treat
   the passed string as UTF-8, rather than treating it as latin1.
2023-11-08 19:40:14 +00:00
Jonathan Coates
87345c6b2e Add pasting support to the standalone emulator
- Move paste normalisation code to StringUtil, so it can be shared by
   emulators.
 - Add paste support to the emulator.
2023-11-08 19:40:14 +00:00
Jonathan Coates
784e623776 Update Cobalt to 0.8.0
- Update Cobalt to 0.8.0, switching our Lua version to 5.2(ish).

 - Remove our `load` wrapper, as we no longer need to inject _ENV into
   the enviroment table.

 - Update the parser to handle labels and goto. This doesn't check that
   gotos are well formed, but at least means the parser doesn't fall
   over on them.

 - Update our docs to reflect the changes to Cobalt.
2023-11-08 18:42:17 +00:00
Jonathan Coates
bcb3e9bd53 Bump CC:T to 1.108.4 2023-10-29 12:02:11 +00:00
Jonathan Coates
c30bffbd0f Add additional tests for filesystems/mounts
This tries to cover some holes in our existing coverage.

 - Port some of our Java readable handle tests to Lua (and also clean up
   the Java versions to stop using ObjectWrapper - that dates to
   pre-@LuaFunction!)

 - Test a couple of discrepancies between binary and text handles. This
   is mostly to do with the original number-based .read() and .write()
   interface for binary handles.

 - Fix a couple of edge cases in file-size accounting.
2023-10-29 12:01:26 +00:00
Jonathan Coates
91c41856c5 Add an "Incompatibilities between versions" page
This is largely based on our existing wiki page. I've pruned out a
couple of entries which I think are largely irrelevant (config file
splitting, Java API changes).

10/10 job by me of changing nothing since 1.13. Shame to break that
streak really.
2023-10-28 20:00:56 +01:00
Jonathan Coates
18c9723308 Add a standalone CC:T UI
Does it count as an emulator when it's official? I hope not, as this'd
make it my fourth or fifth emulator at this point.

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

Developing/debugging CraftOS is a massive pain to do inside Minecraft,
as any change to resources requires a compile+hot swap cycle (and
sometimes a `/reload` in-game). As such, it's often more convenient to
spin up an emulator, pointing it to load the ROM from CC:T's sources.

However, this isn't practical when also making changes to the Java
classes. In this case, we either need to go in-game, or build a custom
version of CCEmuX.

This commit offers an alternative option: we now have our own emulator,
which allows us to hot swap both Lua and Java to our heart's content.

Most of the code here is based on our monitor TBO renderer. We probably
could share some more of this, but there's not really a good place for
it - feels a bit weird just to chuck it in :core.

This is *not* a general-purpose emulator. It's limited in a lot of
ways (won't launch on Mac[^1], no support for multiple computers) - just
stick to what's there already.

[^1]: We require OpenGL 4.5 due to our use of DSA.
2023-10-28 17:58:11 +01:00
Jonathan Coates
aee382ed70 Replace Fabric JUnit hacks with fabric-loader-junit
Also configure some of our common JUnit run configurations via Gradle -
I end up setting these up in every worktree anyway - so let's just do it
once.
2023-10-26 22:06:40 +01:00
Jonathan Coates
6656da5877 Remove disable_lua51_features config option
In practice, we're never going to change this to true by default. The
old Tekkit Legends pack enabled this[^1], and that caused a lot of
problems, though admittedly back in 2016 so things might be better now.

If people do want this functionality, it should be fairly easy to
replicate with a datapack, adding a file to rom/autorun.

[^1]: See https://www.computercraft.info/forums2/index.php?/topic/27663-

      Hate that I remember this, why is this still in my brain?
2023-10-25 08:59:55 +01:00
Jonathan Coates
09e521727f Make mount error messages a bit more consistent
- Move most error message constants to a new MountHelpers class.
 - Be a little more consistent in when we throw "No such file" vs "Not a
   file/directory" messages.
2023-10-22 13:13:07 +01:00
Jonathan Coates
cab66a2d6e Replace Collections methods with {List,Map,Set}.of
The two implementations aren't entirely compatible - the implementation
returned by .of will throw an NPE on .contains(null), whereas the
Collections implementations just return false. However, we try to avoid
passing null to collections methods, so this should be safe.

There's no strong reason to do this, but it helps make the code a little
more consistent
2023-10-21 10:37:43 +01:00
Jonathan Coates
8eabd4f303 Fix signs being empty when placed
As of 1.20, sign messages are immutable - we need to do
text = text.setMesssage(...) instead. Also do a tiny bit of cleanup to
this function while we're here.

Probably not the best use of my lunch break :D:.

Fixes #1611.
2023-10-20 13:32:38 +01:00
Jonathan Coates
e3ced84885 Merge branch 'feature/split-computer-thread' into mc-1.20.x 2023-10-19 22:54:56 +01:00
Jonathan Coates
0929ab577d Split ComputerThread/ComputerExecutor up a little
This is an attempt to enforce better separation between ComputerThread
and ComputerExecutor. Both of these classes are pretty complex in their
own right, and the way the two bleed into each other makes it all the
more confusing!

This effectively splits the ComputerExecutor into two separate classes:
 - ComputerScheduler.Executor (with the actual implementation inside
   ComputerThread): This holds all the ComputerThread-related logic
   which used to be in ComputerExecutor, including:

    - before/after work hooks
    - is-on-thread tracking
    - virtual runtime computation

 - ComputerScheduler.Worker: This encapsulates all the computer-related
   behaviour. The actual implementation remains in ComputerExecutor.

The boundaries are still a little fuzzy here, and it's all definitely
more coupled then I'd like, but still an improvement!

There are several additional changes at the same time:

 - TimeoutState has also been split up, to better define the boundary
   between consumers (such as ComputerExecutor and ILuaMachine) and
   controllers (ComputerThread).

   The getters still live in TimeoutState, but the core logic lives in
   ManagedTimeoutState.

 - We no longer track cumulative time in the TimeoutState. Instead, we
   allow varying the timeout of a computer. When a computer is paused,
   we store the remaining time, and restore it when resuming again.

   This also allows us give a longer timeout for computer
   startup/shutdown, hopefully avoiding some of those class-not-found
   issues we've seen.

 - We try to make the state machine of how ComputerExecutors live on the
   queue a little more explicit. This is very messy/confusing -
   something I want to property test in the future.

I'm sure there's more to be done here, especially in ComputerExecutor,
but hopefully this makes future changes a little less intimidating.
2023-10-19 22:50:11 +01:00
Jonathan Coates
2228733abc Move ComputerThread to its own package
This is entirely broken - we rely a lot on package locals right now -
but makes the next commit a little cleaner.
2023-10-19 18:31:02 +01:00
Jonathan Coates
e67c94d1bd Fix a couple of future deprecations in Gradle 2023-10-19 18:28:15 +01:00
Jonathan Coates
ae5a661a47 Add a discarding MetricsObserver
This is useful for test code where we don't care about the metrics
gathered.
2023-10-19 18:27:58 +01:00
Jonathan Coates
0ff58cdc3e Unify the generic peirpheral system a litte
Allows registering arbitrary block lookup functions instead of a
platform-specific capability. This is roughly what Fabric did before,
but generalised to also take an invalidation callback.

This callback is a little nasty - it needs to be a NonNullableConsumer
on Forge, but that class isn't available on Fabric. For now, we make the
lookup function (and thus the generic peripheral provider) generic on
some <T extends Runnable> type, then specialise that on the Forge side.
Hopefully we can clean this up when NeoForge reworks capabilities.
2023-10-17 21:59:16 +01:00
Jonathan Coates
1747c74770 Relicense some translations
One issue with Weblate is tracking down the provenance of who has
touched what file. I'm fairly sure this is accurate though.
2023-10-16 22:12:41 +01:00
Jonathan Coates
71669cf49c Replace ASM generation with MethodHandles
This is the second time I've rewritten our class generation in a little
over a month. Oh dear!

Back in d562a051c7 we started using method
handles inside our generated ASM, effectively replacing a direct call
with .invokeExact on a constant method handle.

This goes one step further and removes our ASM entirely, building up a
MethodHandle that checks arguments and then wraps the return value.
Rather than generating a class, we just return a new LuaFunction
instance that invokeExacts the method handle.

This is definitely slower than what we had before, but in the order of
8ns vs 12ns (in the worst case, sometimes they're much more comparable),
so I'm not too worried in practice.

However, generation of the actual method is now a bit faster. I've not
done any proper benchmarking, but it's about 20-30% faster.

This also gives us a bit more flexibility in the future, for instance
uisng bound MethodHandles in generation (e.g. for instance methods on
GenericSources). Not something I'm planning on doing right now, but is
an option.
2023-10-11 20:05:37 +01:00
Jonathan Coates
bd327e37eb Fix common jar not actually being published
Or rather, being published to the wrong place. The java-convention
plugin sets the group, but that was applied after the publishing one - I
was hoping it'd read that property lazy, but clearly not!
2023-10-11 19:15:36 +01:00
Jonathan Coates
bdce9a8170 Replace several Guava classes with Java stdlib
Wow, some of this is /old/. All the Maps.newHashMap stuff dates back to
Java 6, so must originally be CCTweaks code?!

We're unlikely to drop our Guava dependency (we use too much other
stuff), but we should make the most of the stdlib where possible.
2023-10-11 09:59:55 +01:00
Jonathan Coates
7e5598d084 Use ClassValue instead of LoadingCache
This should be significantly faster than LoadingCache (2.5x in my
benchmarks, but not sure they're representative). This isn't super
important - a lookup only takes 6us - but still worth using!
2023-10-11 09:30:29 +01:00
Jonathan Coates
440fca6535 Relicense a couple of more files
We've got in touch with Brady, so can now do the last of the docs \o/!

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

Translations for German

Co-authored-by: Sammy <SammyKoch@pm.me>
Co-authored-by: SquidDev <git@squiddev.cc>
2023-10-09 22:33:03 +00:00
Jonathan Coates
93ad40efbb Ensure the terminal exists when creating a monitor peripheral
Previously we had the invariant that if we had a server monitor, we also
had a terminal. When a monitor shrank into a place, we deleted the
monitor, and then recreated it when a peripheral was requested.

As of ab785a0906 this has changed
slightly, and we now just delete the terminal (keeping the ServerMonitor
around). However, we didn't adjust the peripheral code accordingly,
meaning we didn't recreate the /terminal/ when a peripheral was
requested.

The fix for this is very simple - most of the rest of this commit is
some additional code for ensuring monitor invariants hold, so we can
write tests with a little more confidence.

I'm not 100% sold on this approach. It's tricky having a double layer of
nullable state (ServerMonitor, and then the terminal). However, I think
this is reasonable - the ServerMonitor is a reference to the multiblock,
and the Terminal is part of the multiblock's state.

Even after all the refactors, monitor code is still nastier than I'd
like :/.

Fixes #1608
2023-10-09 22:09:01 +01:00
Jonathan Coates
27dc8b5b2c Pass follow_redirects flag to the CORS proxy
Currently redirects would be returned from the proxy, and then
immediately followed by XMLHTTPRequest. The proxy now follows requests
(when requested), so that should no longer happen.

We should probably switch over to fetch(...) here, to allow setting
follow_redirects to false, but that's a job for another day.

Haha, so many web emulator related commits of late. This'll die down
soon.
2023-10-08 20:12:47 +01:00
Jonathan Coates
3ebdf7ef5e Bump CC:T to 1.108.3 2023-10-08 15:25:45 +01:00
Jonathan Coates
905d4cb091 Fix crash when joining a dedicated server
We can't use FriendlyByte.readCollection to read to a
pre-allocated/array-backed NonNullList, as that doesn't implement
List.add. Instead, we just need to do a normal loop.

We add a couple of tests to round-trip our recipe specs. Unfortunately
we can't test the recipes themselves as our own registries aren't set
up, so this'll have to do for now.
2023-10-08 15:22:32 +01:00
321 changed files with 7158 additions and 3985 deletions

View File

@@ -53,6 +53,7 @@ Files:
projects/common/src/main/resources/assets/computercraft/textures/*
projects/common/src/main/resources/pack.mcmeta
projects/common/src/main/resources/pack.png
projects/core/src/main/resources/assets/computercraft/textures/gui/term_font.png
projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
projects/core/src/main/resources/data/computercraft/lua/rom/help/*
projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
@@ -62,11 +63,23 @@ Copyright: 2011 Daniel Ratcliffe
License: LicenseRef-CCPL
Files:
projects/common/src/main/resources/assets/computercraft/lang/*
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:

View File

@@ -42,7 +42,6 @@ repositories {
url "https://squiddev.cc/maven/"
content {
includeGroup("cc.tweaked")
includeModule("org.squiddev", "Cobalt")
}
}
}

View File

@@ -2,13 +2,19 @@
//
// SPDX-License-Identifier: MPL-2.0
import cc.tweaked.gradle.JUnitExt
import net.fabricmc.loom.api.LoomGradleExtensionAPI
import net.fabricmc.loom.util.gradle.SourceSetHelper
import org.jetbrains.gradle.ext.compiler
import org.jetbrains.gradle.ext.runConfigurations
import org.jetbrains.gradle.ext.settings
plugins {
publishing
alias(libs.plugins.taskTree)
alias(libs.plugins.githubRelease)
alias(libs.plugins.gradleVersions)
alias(libs.plugins.versionCatalogUpdate)
id("org.jetbrains.gradle.plugin.idea-ext")
id("cc-tweaked")
}
@@ -38,6 +44,50 @@ githubRelease {
tasks.publish { dependsOn(tasks.githubRelease) }
idea.project.settings.runConfigurations {
register<JUnitExt>("Core Tests") {
vmParameters = "-ea"
moduleName = "${idea.project.name}.core.test"
packageName = ""
}
register<JUnitExt>("CraftOS Tests") {
vmParameters = "-ea"
moduleName = "${idea.project.name}.core.test"
className = "dan200.computercraft.core.ComputerTestDelegate"
}
register<JUnitExt>("CraftOS Tests (Fast)") {
vmParameters = "-ea -Dcc.skip_keywords=slow"
moduleName = "${idea.project.name}.core.test"
className = "dan200.computercraft.core.ComputerTestDelegate"
}
register<JUnitExt>("Common Tests") {
vmParameters = "-ea"
moduleName = "${idea.project.name}.common.test"
packageName = ""
}
register<JUnitExt>("Fabric Tests") {
val fabricProject = evaluationDependsOn(":fabric")
val classPathGroup = fabricProject.extensions.getByType<LoomGradleExtensionAPI>().mods
.joinToString(File.pathSeparator + File.pathSeparator) { modSettings ->
SourceSetHelper.getClasspath(modSettings, project).joinToString(File.pathSeparator) { it.absolutePath }
}
vmParameters = "-ea -Dfabric.classPathGroups=$classPathGroup"
moduleName = "${idea.project.name}.fabric.test"
packageName = ""
}
register<JUnitExt>("Forge Tests") {
vmParameters = "-ea"
moduleName = "${idea.project.name}.forge.test"
packageName = ""
}
}
idea.project.settings.compiler.javac {
// We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings
// and errors. Loop through our source sets and find the appropriate flags.
@@ -54,3 +104,9 @@ idea.project.settings.compiler.javac {
}
.toMap()
}
versionCatalogUpdate {
sortByKey.set(false)
pin { versions.addAll("fastutil", "guava", "netty", "slf4j") }
keep { keepUnusedLibraries.set(true) }
}

View File

@@ -5,6 +5,8 @@
plugins {
`java-gradle-plugin`
`kotlin-dsl`
alias(libs.plugins.gradleVersions)
alias(libs.plugins.versionCatalogUpdate)
}
// Duplicated in settings.gradle.kts
@@ -50,10 +52,11 @@ dependencies {
implementation(libs.curseForgeGradle)
implementation(libs.fabric.loom)
implementation(libs.forgeGradle)
implementation(libs.ideaExt)
implementation(libs.librarian)
implementation(libs.minotaur)
implementation(libs.vineflower)
implementation(libs.vanillaGradle)
implementation(libs.vineflower)
}
gradlePlugin {
@@ -74,3 +77,9 @@ gradlePlugin {
}
}
}
versionCatalogUpdate {
sortByKey.set(false)
keep { keepUnusedLibraries.set(true) }
catalogFile.set(file("../gradle/libs.versions.toml"))
}

View File

@@ -57,7 +57,6 @@ repositories {
filter {
includeGroup("cc.tweaked")
includeModule("org.squiddev", "Cobalt")
// Things we mirror
includeGroup("commoble.morered")
includeGroup("dev.architectury")
@@ -77,6 +76,12 @@ dependencies {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
checkstyle(libs.findLibrary("checkstyle").get())
constraints {
checkstyle("org.codehaus.plexus:plexus-container-default:2.1.1") {
because("2.1.0 depends on deprecated Google collections module")
}
}
errorprone(libs.findLibrary("errorProne-core").get())
errorprone(libs.findLibrary("nullAway").get())
}
@@ -202,6 +207,7 @@ spotless {
val ktlintConfig = mapOf(
"ktlint_standard_no-wildcard-imports" to "disabled",
"ktlint_standard_class-naming" to "disabled",
"ktlint_standard_function-naming" to "disabled",
"ij_kotlin_allow_trailing_comma" to "true",
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
)

View File

@@ -10,9 +10,11 @@ import org.gradle.api.GradleException
import org.gradle.api.NamedDomainObjectProvider
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Dependency
import org.gradle.api.attributes.TestSuiteType
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Provider
import org.gradle.api.provider.SetProperty
import org.gradle.api.reporting.ReportingExtension
@@ -73,11 +75,17 @@ abstract class CCTweakedExtension(
*/
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
/**
* Dependencies excluded from published artifacts.
*/
private val excludedDeps: ListProperty<Dependency> = project.objects.listProperty(Dependency::class.java)
/** All source sets referenced by this project. */
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
init {
sourceDirectories.finalizeValueOnRead()
excludedDeps.finalizeValueOnRead()
project.afterEvaluate { sourceDirectories.disallowChanges() }
}
@@ -173,7 +181,7 @@ abstract class CCTweakedExtension(
}
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}")
val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
val reportTaskName = "jacoco${task.name.capitalized()}Report"
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
@@ -185,7 +193,7 @@ abstract class CCTweakedExtension(
jacoco.applyTo(this)
extensions.configure(JacocoTaskExtension::class.java) {
includes = listOf("dan200.computercraft.*")
classDumpDir = classDump
classDumpDir = classDump.get().asFile
// Older versions of modlauncher don't include a protection domain (and thus no code
// source). Jacoco skips such classes by default, so we need to explicitly include them.
@@ -246,6 +254,20 @@ abstract class CCTweakedExtension(
).resolve().single()
}
/**
* Exclude a dependency from being publisehd in Maven.
*/
fun exclude(dep: Dependency) {
excludedDeps.add(dep)
}
/**
* Configure a [MavenDependencySpec].
*/
fun configureExcludes(spec: MavenDependencySpec) {
for (dep in excludedDeps.get()) spec.exclude(dep)
}
companion object {
private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
private val IGNORED_USERS = setOf(

View File

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

View File

@@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package cc.tweaked.gradle
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.gradle.api.artifacts.component.ModuleComponentSelector
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.artifacts.result.DependencyResult
import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.gradle.language.base.plugins.LifecycleBasePlugin
abstract class DependencyCheck : DefaultTask() {
@get:Input
abstract val configuration: ListProperty<Configuration>
init {
description = "Check :core's dependencies are consistent with Minecraft's."
group = LifecycleBasePlugin.VERIFICATION_GROUP
}
@TaskAction
fun run() {
var ok = true
for (configuration in configuration.get()) {
configuration.incoming.resolutionResult.allDependencies {
if (!check(this@allDependencies)) ok = false
}
}
if (!ok) {
throw GradleException("Mismatched versions in Minecraft dependencies. gradle/libs.versions.toml may need updating.")
}
}
private fun check(dependency: DependencyResult): Boolean {
if (dependency !is ResolvedDependencyResult) {
logger.warn("Found unexpected dependency result {}", dependency)
return false
}
// Skip dependencies on non-modules.
val requested = dependency.requested
if (requested !is ModuleComponentSelector) return true
// If this dependency is specified within some project (so is non-transitive), or is pulled in via Minecraft,
// then check for consistency.
// It would be nice to be smarter about transitive dependencies, but avoiding false positives is hard.
val from = dependency.from.id
if (
from is ProjectComponentIdentifier ||
from is ModuleComponentIdentifier && (from.group == "net.minecraft" || from.group == "io.netty")
) {
// If the version is different between the requested and selected version, report an error.
val selected = dependency.selected.moduleVersion!!.version
if (requested.version != selected) {
logger.error("Requested dependency {} (via {}) but got version {}", requested, from, selected)
return false
}
return true
}
return true
}
}

View File

@@ -6,7 +6,6 @@ package cc.tweaked.gradle
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.file.FileSystemLocation
import org.gradle.api.file.FileSystemLocationProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.JavaExec
@@ -129,3 +128,30 @@ fun <T> Property<T>.setProvider(provider: Provider<out T>) = set(provider)
/** Short-cut method to get the absolute path of a [FileSystemLocation] provider. */
fun Provider<out FileSystemLocation>.getAbsolutePath(): String = get().asFile.absolutePath
/**
* Get the version immediately after the provided version.
*
* For example, given "1.2.3", this will return "1.2.4".
*/
fun getNextVersion(version: String): String {
// Split a version like x.y.z-SNAPSHOT into x.y.z and -SNAPSHOT
val dashIndex = version.indexOf('-')
val mainVersion = if (dashIndex < 0) version else version.substring(0, dashIndex)
// Find the last component in x.y.z and increment it.
val lastIndex = mainVersion.lastIndexOf('.')
if (lastIndex < 0) throw IllegalArgumentException("Cannot parse version format \"$version\"")
val lastVersion = try {
version.substring(lastIndex + 1).toInt()
} catch (e: NumberFormatException) {
throw IllegalArgumentException("Cannot parse version format \"$version\"", e)
}
// Then append all components together.
val out = StringBuilder()
out.append(version, 0, lastIndex + 1)
out.append(lastVersion + 1)
if (dashIndex >= 0) out.append(version, dashIndex, version.length)
return out.toString()
}

View File

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

View File

@@ -6,6 +6,8 @@ package cc.tweaked.gradle
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.MinimalExternalModuleDependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.plugins.BasePluginExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.specs.Spec
@@ -26,8 +28,13 @@ class MavenDependencySpec {
fun exclude(dep: Dependency) {
exclude {
// We have to cheat a little for project dependencies, as the project name doesn't match the artifact group.
val name = when (dep) {
is ProjectDependency -> dep.dependencyProject.extensions.getByType(BasePluginExtension::class.java).archivesName.get()
else -> dep.name
}
(dep.group.isNullOrEmpty() || dep.group == it.groupId) &&
(dep.name.isNullOrEmpty() || dep.name == it.artifactId) &&
(name.isNullOrEmpty() || name == it.artifactId) &&
(dep.version.isNullOrEmpty() || dep.version == it.version)
}
}

View File

@@ -109,6 +109,15 @@ class MinecraftConfigurations private constructor(private val project: Project)
project.extensions.configure(CCTweakedExtension::class.java) {
sourceDirectories.add(SourceSetReference.internal(client))
}
// Register a task to check there are no conflicts with the core project.
val checkDependencyConsistency =
project.tasks.register("checkDependencyConsistency", DependencyCheck::class.java) {
// We need to check both the main and client classpath *configurations*, as the actual configuration
configuration.add(configurations.named(main.runtimeClasspathConfigurationName))
configuration.add(configurations.named(client.runtimeClasspathConfigurationName))
}
project.tasks.named("check") { dependsOn(checkDependencyConsistency) }
}
private fun setupOutgoing(sourceSet: SourceSet, suffix: String = "") {

View File

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

View File

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

View File

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

View File

@@ -12,7 +12,7 @@ SPDX-License-Identifier: MPL-2.0
The [`file_transfer`] event is queued when a user drags-and-drops a file on an open computer.
This event contains a single argument of type [`TransferredFiles`], which can be used to [get the files to be
transferred][`TransferredFiles.getFiles`]. Each file returned is a [binary file handle][`fs.BinaryReadHandle`] with an
transferred][`TransferredFiles.getFiles`]. Each file returned is a [binary file handle][`fs.ReadHandle`] with an
additional [getName][`TransferredFile.getName`] method.
## Return values

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
# Mod properties
isUnstable=false
modVersion=1.108.2
modVersion=1.109.3
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
mcVersion=1.20.1

View File

@@ -10,27 +10,29 @@
fabric-api = "0.86.1+1.20.1"
fabric-loader = "0.14.21"
forge = "47.1.0"
forgeSpi = "6.0.0"
forgeSpi = "7.0.1"
mixin = "0.8.5"
parchment = "2023.08.20"
parchmentMc = "1.20.1"
# Normal dependencies
asm = "9.5"
autoService = "1.1.1"
checkerFramework = "3.32.0"
cobalt = "0.7.3"
cobalt-next = "0.7.4" # Not a real version, used to constrain the version we accept.
# Core dependencies (these versions are tied to the version Minecraft uses)
fastutil = "8.5.9"
guava = "31.1-jre"
jetbrainsAnnotations = "24.0.1"
netty = "4.1.82.Final"
slf4j = "2.0.1"
# Core dependencies (independent of Minecraft)
asm = "9.6"
autoService = "1.1.1"
checkerFramework = "3.42.0"
cobalt = "0.8.2"
commonsCli = "1.6.0"
jetbrainsAnnotations = "24.1.0"
jsr305 = "3.0.2"
jzlib = "1.1.3"
kotlin = "1.8.10"
kotlin-coroutines = "1.6.4"
netty = "4.1.82.Final"
kotlin = "1.9.21"
kotlin-coroutines = "1.7.3"
nightConfig = "3.6.7"
slf4j = "2.0.1"
# Minecraft mods
emi = "1.0.8+1.20.1"
@@ -45,31 +47,33 @@ rubidium = "0.6.1"
sodium = "mc1.20-0.4.10"
# Testing
byteBuddy = "1.14.7"
hamcrest = "2.2"
jqwik = "1.7.4"
junit = "5.10.0"
jqwik = "1.8.2"
junit = "5.10.1"
# Build tools
cctJavadoc = "1.8.0"
checkstyle = "10.12.3"
cctJavadoc = "1.8.2"
checkstyle = "10.12.6"
curseForgeGradle = "1.0.14"
errorProne-core = "2.21.1"
errorProne-core = "2.23.0"
errorProne-plugin = "3.1.0"
fabric-loom = "1.3.7"
fabric-loom = "1.3.9"
forgeGradle = "6.0.8"
githubRelease = "2.4.1"
githubRelease = "2.5.2"
gradleVersions = "0.50.0"
ideaExt = "1.1.7"
illuaminate = "0.1.0-44-g9ee0055"
librarian = "1.+"
lwjgl = "3.3.3"
minotaur = "2.+"
mixinGradle = "0.7.+"
mixinGradle = "0.7.38"
nullAway = "0.9.9"
spotless = "6.21.0"
spotless = "6.23.3"
taskTree = "2.1.1"
teavm = "0.10.0-SQUID.2"
vanillaGradle = "0.2.1-SNAPSHOT"
versionCatalogUpdate = "0.8.1"
vineflower = "1.11.0"
teavm = "0.9.0-SQUID.1"
[libraries]
# Normal dependencies
@@ -77,7 +81,8 @@ asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" }
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
guava = { module = "com.google.guava:guava", version.ref = "guava" }
@@ -97,6 +102,7 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
# Minecraft mods
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
@@ -114,8 +120,6 @@ rubidium = { module = "maven.modrinth:rubidium", version.ref = "rubidium" }
sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
# Testing
byteBuddyAgent = { module = "net.bytebuddy:byte-buddy-agent", version.ref = "byteBuddy" }
byteBuddy = { module = "net.bytebuddy:byte-buddy", version.ref = "byteBuddy" }
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
@@ -124,6 +128,12 @@ junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", vers
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
# LWJGL
lwjgl-bom = { module = "org.lwjgl:lwjgl-bom", version.ref = "lwjgl" }
lwjgl-core = { module = "org.lwjgl:lwjgl" }
lwjgl-opengl = { module = "org.lwjgl:lwjgl-opengl" }
lwjgl-glfw = { module = "org.lwjgl:lwjgl-glfw" }
# Build tools
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
@@ -135,6 +145,7 @@ errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", versi
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
@@ -154,11 +165,12 @@ vineflower = { module = "io.github.juuxel:loom-vineflower", version.ref = "vinef
[plugins]
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
ideaExt = { id = "org.jetbrains.gradle.plugin.idea-ext", version.ref = "ideaExt" }
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" }
[bundles]
annotations = ["jsr305", "checkerFramework", "jetbrainsAnnotations"]
@@ -177,5 +189,5 @@ test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
# Build tools
teavm-api = [ "teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api" ]
teavm-tooling = [ "teavm-tooling", "teavm-metaprogramming-impl", "teavm-jso-impl" ]
teavm-api = ["teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api"]
teavm-tooling = ["teavm-tooling", "teavm-metaprogramming-impl", "teavm-jso-impl"]

View File

@@ -2,11 +2,11 @@
; SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
;
; SPDX-License-Identifier: LicenseRef-CCPL
; SPDX-License-Identifier: MPL-2.0
(sources
/doc/
/projects/forge/build/docs/luaJavadoc/
/projects/common/build/docs/luaJavadoc/
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
/projects/core/src/main/resources/data/computercraft/lua/rom/
/projects/core/src/test/resources/test-rom
@@ -36,7 +36,7 @@
(library-path
/doc/stub/
/projects/forge/build/docs/luaJavadoc/
/projects/common/build/docs/luaJavadoc/
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/command/
@@ -77,7 +77,6 @@
(globals
:max
_CC_DEFAULT_SETTINGS
_CC_DISABLE_LUA51_FEATURES
_HOST
;; Ideally we'd pick these up from bios.lua, but illuaminate currently
;; isn't smart enough.
@@ -89,7 +88,7 @@
(/doc/stub/
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
/projects/forge/build/docs/luaJavadoc/)
/projects/common/build/docs/luaJavadoc/)
(linters -var:unused-global)
(lint (allow-toplevel-global true)))

View File

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

View File

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

View File

@@ -2,14 +2,13 @@
//
// SPDX-License-Identifier: MPL-2.0
import cc.tweaked.gradle.annotationProcessorEverywhere
import cc.tweaked.gradle.clientClasses
import cc.tweaked.gradle.commonClasses
import cc.tweaked.gradle.*
plugins {
id("cc-tweaked.publishing")
id("cc-tweaked.vanilla")
id("cc-tweaked.gametest")
id("cc-tweaked.illuaminate")
id("cc-tweaked.publishing")
}
minecraft {
@@ -19,6 +18,10 @@ minecraft {
)
}
configurations {
register("cctJavadoc")
}
dependencies {
// Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
implementation(project(":core"))
@@ -39,4 +42,55 @@ dependencies {
testModImplementation(testFixtures(project(":core")))
testModImplementation(testFixtures(project(":common")))
testModImplementation(libs.bundles.kotlin)
testFixturesImplementation(testFixtures(project(":core")))
"cctJavadoc"(libs.cctJavadoc)
}
illuaminate {
version.set(libs.versions.illuaminate)
}
val luaJavadoc by tasks.registering(Javadoc::class) {
description = "Generates documentation for Java-side Lua functions."
group = JavaBasePlugin.DOCUMENTATION_GROUP
val sourceSets = listOf(sourceSets.main.get(), project(":core").sourceSets.main.get())
for (sourceSet in sourceSets) {
source(sourceSet.java)
classpath += sourceSet.compileClasspath
}
destinationDir = layout.buildDirectory.dir("docs/luaJavadoc").get().asFile
val options = options as StandardJavadocDocletOptions
options.docletpath = configurations["cctJavadoc"].files.toList()
options.doclet = "cc.tweaked.javadoc.LuaDoclet"
options.addStringOption("project-root", rootProject.file(".").absolutePath)
options.noTimestamp(false)
javadocTool.set(
javaToolchains.javadocToolFor {
languageVersion.set(cc.tweaked.gradle.CCTweakedPlugin.JAVA_VERSION)
},
)
}
val lintLua by tasks.registering(IlluaminateExec::class) {
group = JavaBasePlugin.VERIFICATION_GROUP
description = "Lint Lua (and Lua docs) with illuaminate"
// Config files
inputs.file(rootProject.file("illuaminate.sexp")).withPropertyName("illuaminate.sexp")
// Sources
inputs.files(rootProject.fileTree("doc")).withPropertyName("docs")
inputs.files(project(":core").fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
inputs.files(luaJavadoc)
args = listOf("lint")
workingDir = rootProject.projectDir
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::") }
}

View File

@@ -19,6 +19,7 @@ import dan200.computercraft.shared.network.server.UploadFileMessage;
import net.minecraft.ChatFormatting;
import net.minecraft.Util;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
@@ -33,7 +34,6 @@ import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -145,6 +145,11 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|| super.mouseDragged(x, y, button, deltaX, deltaY);
}
@Override
public void setFocused(@Nullable GuiEventListener listener) {
// Don't clear and re-focus if we're already focused.
if (listener != getFocused()) super.setFocused(listener);
}
@Override
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
@@ -219,7 +224,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
private void alert(Component title, Component message) {
OptionScreen.show(minecraft, title, message,
Collections.singletonList(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
() -> minecraft.setScreen(this)
);
}

View File

@@ -8,8 +8,8 @@ import com.mojang.blaze3d.vertex.Tesselator;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.util.StringUtil;
import dan200.computercraft.shared.computer.core.InputHandler;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
@@ -112,26 +112,8 @@ public class TerminalWidget extends AbstractWidget {
}
private void paste() {
var clipboard = Minecraft.getInstance().keyboardHandler.getClipboard();
// Clip to the first occurrence of \r or \n
var newLineIndex1 = clipboard.indexOf('\r');
var newLineIndex2 = clipboard.indexOf('\n');
if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
clipboard = clipboard.substring(0, Math.min(newLineIndex1, newLineIndex2));
} else if (newLineIndex1 >= 0) {
clipboard = clipboard.substring(0, newLineIndex1);
} else if (newLineIndex2 >= 0) {
clipboard = clipboard.substring(0, newLineIndex2);
}
// Filter the string
clipboard = SharedConstants.filterText(clipboard);
if (!clipboard.isEmpty()) {
// Clip to 512 characters and queue the event
if (clipboard.length() > 512) clipboard = clipboard.substring(0, 512);
computer.queueEvent("paste", new Object[]{ clipboard });
}
var clipboard = StringUtil.normaliseClipboardString(Minecraft.getInstance().keyboardHandler.getClipboard());
if (!clipboard.isEmpty()) computer.queueEvent("paste", new Object[]{ clipboard });
}
@Override
@@ -264,7 +246,7 @@ public class TerminalWidget extends AbstractWidget {
keysDown.clear();
// When blurring, we should make the last mouse button go up
if (lastMouseButton > 0) {
if (lastMouseButton >= 0) {
computer.mouseUp(lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1);
lastMouseButton = -1;
}

View File

@@ -11,7 +11,6 @@ import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.client.platform.ClientPlatformHelper;
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
import dan200.computercraft.shared.util.Holiday;
import net.minecraft.client.Minecraft;
@@ -21,7 +20,6 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
@@ -29,8 +27,6 @@ import net.minecraft.world.phys.HitResult;
import javax.annotation.Nullable;
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> {
private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_normal", "inventory");
private static final ModelResourceLocation ADVANCED_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced", "inventory");
private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
private static final ResourceLocation ELF_OVERLAY_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_elf_overlay");
@@ -42,13 +38,6 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
font = context.getFont();
}
public static ResourceLocation getTurtleModel(ComputerFamily family, boolean coloured) {
return switch (family) {
default -> coloured ? COLOUR_TURTLE_MODEL : NORMAL_TURTLE_MODEL;
case ADVANCED -> coloured ? COLOUR_TURTLE_MODEL : ADVANCED_TURTLE_MODEL;
};
}
public static @Nullable ResourceLocation getTurtleOverlayModel(@Nullable ResourceLocation overlay, boolean christmas) {
if (overlay != null) return overlay;
if (christmas) return ELF_OVERLAY_MODEL;
@@ -78,7 +67,6 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
var matrix = transform.last().pose();
var opacity = (int) (mc.options.getBackgroundOpacity(0.25f) * 255) << 24;
var width = -font.width(label) / 2.0f;
// TODO: Check this looks okay
font.drawInBatch(label, width, (float) 0, 0x20ffffff, false, matrix, buffers, Font.DisplayMode.SEE_THROUGH, opacity, lightmapCoord);
font.drawInBatch(label, width, (float) 0, 0xffffffff, false, matrix, buffers, Font.DisplayMode.NORMAL, 0, lightmapCoord);
@@ -96,10 +84,18 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
// Render the turtle
var colour = turtle.getColour();
var family = turtle.getFamily();
var overlay = turtle.getOverlay();
renderModel(transform, buffers, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour });
if (colour == -1) {
// Render the turtle using its item model.
var modelManager = Minecraft.getInstance().getItemRenderer().getItemModelShaper();
var model = modelManager.getItemModel(turtle.getBlockState().getBlock().asItem());
if (model == null) model = modelManager.getModelManager().getMissingModel();
renderModel(transform, buffers, lightmapCoord, overlayLight, model, null);
} else {
// Otherwise render it using the colour item.
renderModel(transform, buffers, lightmapCoord, overlayLight, COLOUR_TURTLE_MODEL, new int[]{ colour });
}
// Render the overlay
var overlayModel = getTurtleOverlayModel(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS);

View File

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

View File

@@ -174,6 +174,7 @@ public final class LanguageProvider implements DataProvider {
// Metrics
add(Metrics.COMPUTER_TASKS, "Tasks");
add(Metrics.SERVER_TASKS, "Server tasks");
add(Metrics.JAVA_ALLOCATION, "Java Allocations");
add(Metrics.PERIPHERAL_OPS, "Peripheral calls");
add(Metrics.FS_OPS, "Filesystem operations");
add(Metrics.HTTP_REQUESTS, "HTTP requests");
@@ -213,7 +214,6 @@ public final class LanguageProvider implements DataProvider {
addConfigEntry(ConfigSpec.floppySpaceLimit, "Floppy Disk space limit (bytes)");
addConfigEntry(ConfigSpec.uploadMaxSize, "File upload size limit (bytes)");
addConfigEntry(ConfigSpec.maximumFilesOpen, "Maximum files open per computer");
addConfigEntry(ConfigSpec.disableLua51Features, "Disable Lua 5.1 features");
addConfigEntry(ConfigSpec.defaultComputerSettings, "Default Computer settings");
addConfigEntry(ConfigSpec.logComputerErrors, "Log computer errors");
addConfigEntry(ConfigSpec.commandRequireCreative, "Command computers require creative");

View File

@@ -13,7 +13,6 @@ import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.core.util.Colour;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.platform.RecipeIngredients;
import dan200.computercraft.shared.platform.RegistryWrappers;
@@ -38,7 +37,6 @@ import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Blocks;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.function.Consumer;
@@ -106,14 +104,13 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
private void turtleUpgrades(Consumer<FinishedRecipe> add) {
for (var turtleItem : turtleItems()) {
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
var name = RegistryWrappers.ITEMS.getKey(turtleItem);
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
var result = turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, result.getItem())
.group(String.format("%s:turtle_%s", ComputerCraftAPI.MOD_ID, nameId))
.group(name.toString())
.pattern("#T")
.define('T', base.getItem())
.define('#', upgrade.getCraftingItem().getItem())
@@ -121,9 +118,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
.save(
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()),
new ResourceLocation(ComputerCraftAPI.MOD_ID, String.format("turtle_%s/%s/%s",
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
))
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
);
}
}
@@ -141,15 +136,13 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
private void pocketUpgrades(Consumer<FinishedRecipe> add) {
for (var pocket : pocketComputerItems()) {
var base = pocket.create(-1, null, -1, null);
if (base.isEmpty()) continue;
var nameId = pocket.getFamily().name().toLowerCase(Locale.ROOT);
var name = RegistryWrappers.ITEMS.getKey(pocket).withPath(x -> x.replace("pocket_computer_", "pocket_"));
for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) {
var result = pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, result.getItem())
.group(String.format("%s:pocket_%s", ComputerCraftAPI.MOD_ID, nameId))
.group(name.toString())
.pattern("#")
.pattern("P")
.define('P', base.getItem())
@@ -158,9 +151,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
inventoryChange(base.getItem(), upgrade.getCraftingItem().getItem()))
.save(
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPED.get(), add).withResultTag(result.getTag()),
new ResourceLocation(ComputerCraftAPI.MOD_ID, String.format("pocket_%s/%s/%s",
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
))
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
);
}
}
@@ -190,12 +181,10 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
private void turtleOverlay(Consumer<FinishedRecipe> add, String overlay, Consumer<ShapelessRecipeBuilder> build) {
for (var turtleItem : turtleItems()) {
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
var group = "%s:turtle_%s_overlay".formatted(ComputerCraftAPI.MOD_ID, nameId);
var name = RegistryWrappers.ITEMS.getKey(turtleItem);
var builder = ShapelessRecipeBuilder.shapeless(RecipeCategory.REDSTONE, base.getItem())
.group(group)
.group(name.withSuffix("_overlay").toString())
.unlockedBy("has_turtle", inventoryChange(base.getItem()));
build.accept(builder);
builder
@@ -204,7 +193,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
RecipeWrapper
.wrap(ModRegistry.RecipeSerializers.TURTLE_OVERLAY.get(), add)
.withExtraData(x -> x.addProperty("overlay", new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay).toString())),
new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_%s_overlays/%s".formatted(nameId, overlay))
name.withSuffix("_overlays/" + overlay)
);
}
}
@@ -253,7 +242,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
.save(
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)),
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade")
);
@@ -277,7 +266,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
.define('I', ingredients.woodenChest())
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add).withExtraData(family(ComputerFamily.NORMAL)));
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
@@ -288,7 +277,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.define('C', ModRegistry.Items.COMPUTER_ADVANCED.get())
.define('I', ingredients.woodenChest())
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)));
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
@@ -300,7 +289,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.define('B', ingredients.goldBlock())
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.TURTLE_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
.save(
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)),
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade")
);
@@ -367,7 +356,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.define('C', ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
.save(
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)),
RecipeWrapper.wrap(ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get(), add),
new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade")
);
@@ -519,10 +508,6 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
return tag;
}
private static Consumer<JsonObject> family(ComputerFamily family) {
return json -> json.addProperty("family", family.getSerializedName());
}
private static void addSpecial(Consumer<FinishedRecipe> add, SimpleCraftingRecipeSerializer<?> special) {
SpecialRecipeBuilder.special(special).save(add, RegistryWrappers.RECIPE_SERIALIZERS.getKey(special).toString());
}

View File

@@ -58,7 +58,10 @@ class TagProvider {
tags.tag(ComputerCraftTags.Blocks.TURTLE_SWORD_BREAKABLE).addTag(BlockTags.WOOL).add(Blocks.COBWEB);
tags.tag(ComputerCraftTags.Blocks.TURTLE_CAN_USE).addTag(BlockTags.CAULDRONS).addTag(BlockTags.BEEHIVES);
tags.tag(ComputerCraftTags.Blocks.TURTLE_CAN_USE)
.addTag(BlockTags.BEEHIVES)
.addTag(BlockTags.CAULDRONS)
.add(Blocks.COMPOSTER);
// Make all blocks aside from command computer mineable.
tags.tag(BlockTags.MINEABLE_WITH_PICKAXE).add(
@@ -76,6 +79,8 @@ class TagProvider {
ModRegistry.Blocks.WIRED_MODEM_FULL.get(),
ModRegistry.Blocks.CABLE.get()
);
tags.tag(BlockTags.WITHER_IMMUNE).add(ModRegistry.Blocks.COMPUTER_COMMAND.get());
}
public static void itemTags(ItemTagConsumer tags) {

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,12 +14,15 @@ import dan200.computercraft.shared.computer.metrics.ComputerMBean;
import dan200.computercraft.shared.peripheral.monitor.MonitorWatcher;
import dan200.computercraft.shared.util.DropConsumer;
import dan200.computercraft.shared.util.TickScheduler;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.packs.resources.PreparableReloadListener;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
@@ -111,4 +114,17 @@ public final class CommonHooks {
public static boolean onLivingDrop(Entity entity, ItemStack stack) {
return DropConsumer.onLivingDrop(entity, stack);
}
/**
* Add items to an existing creative tab.
*
* @param key The {@link ResourceKey} for this creative tab.
* @param context Additional parameters used for building the contents.
* @param out The creative tab output to append items to.
*/
public static void onBuildCreativeTab(ResourceKey<CreativeModeTab> key, CreativeModeTab.ItemDisplayParameters context, CreativeModeTab.Output out) {
if (key == CreativeModeTabs.OP_BLOCKS && context.hasPermissions()) {
out.accept(ModRegistry.Items.COMPUTER_COMMAND.get());
}
}
}

View File

@@ -34,6 +34,7 @@ import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
import dan200.computercraft.shared.computer.items.CommandComputerItem;
import dan200.computercraft.shared.computer.items.ComputerItem;
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.data.BlockNamedEntityLootCondition;
import dan200.computercraft.shared.data.ConstantLootConditionSerializer;
import dan200.computercraft.shared.data.HasComputerIdLootCondition;
@@ -139,19 +140,17 @@ public final class ModRegistry {
}
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_NORMAL = REGISTRY.register("computer_normal",
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.COMPUTER_NORMAL));
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), BlockEntities.COMPUTER_NORMAL));
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced",
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.COMPUTER_ADVANCED));
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), BlockEntities.COMPUTER_ADVANCED));
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command", () -> new CommandComputerBlock<>(
computerProperties().strength(-1, 6000000.0F),
ComputerFamily.COMMAND, BlockEntities.COMPUTER_COMMAND
));
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command",
() -> new CommandComputerBlock<>(computerProperties().strength(-1, 6000000.0F), BlockEntities.COMPUTER_COMMAND));
public static final RegistryEntry<TurtleBlock> TURTLE_NORMAL = REGISTRY.register("turtle_normal",
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), ComputerFamily.NORMAL, BlockEntities.TURTLE_NORMAL));
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.STONE), BlockEntities.TURTLE_NORMAL));
public static final RegistryEntry<TurtleBlock> TURTLE_ADVANCED = REGISTRY.register("turtle_advanced",
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.GOLD), ComputerFamily.ADVANCED, BlockEntities.TURTLE_ADVANCED));
() -> new TurtleBlock(turtleProperties().mapColor(MapColor.GOLD).explosionResistance(TurtleBlock.IMMUNE_EXPLOSION_RESISTANCE), BlockEntities.TURTLE_ADVANCED));
public static final RegistryEntry<SpeakerBlock> SPEAKER = REGISTRY.register("speaker", () -> new SpeakerBlock(properties().mapColor(MapColor.STONE)));
public static final RegistryEntry<DiskDriveBlock> DISK_DRIVE = REGISTRY.register("disk_drive", () -> new DiskDriveBlock(properties().mapColor(MapColor.STONE)));
@@ -192,9 +191,9 @@ public final class ModRegistry {
ofBlock(Blocks.COMPUTER_COMMAND, (p, s) -> new CommandComputerBlockEntity(BlockEntities.COMPUTER_COMMAND.get(), p, s));
public static final RegistryEntry<BlockEntityType<TurtleBlockEntity>> TURTLE_NORMAL =
ofBlock(Blocks.TURTLE_NORMAL, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_NORMAL.get(), p, s, ComputerFamily.NORMAL));
ofBlock(Blocks.TURTLE_NORMAL, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_NORMAL.get(), p, s, () -> Config.turtleFuelLimit, ComputerFamily.NORMAL));
public static final RegistryEntry<BlockEntityType<TurtleBlockEntity>> TURTLE_ADVANCED =
ofBlock(Blocks.TURTLE_ADVANCED, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_ADVANCED.get(), p, s, ComputerFamily.ADVANCED));
ofBlock(Blocks.TURTLE_ADVANCED, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_ADVANCED.get(), p, s, () -> Config.advancedTurtleFuelLimit, ComputerFamily.ADVANCED));
public static final RegistryEntry<BlockEntityType<SpeakerBlockEntity>> SPEAKER =
ofBlock(Blocks.SPEAKER, (p, s) -> new SpeakerBlockEntity(BlockEntities.SPEAKER.get(), p, s));
@@ -311,7 +310,10 @@ public final class ModRegistry {
() -> new MenuType<>(PrinterMenu::new, FeatureFlags.VANILLA_SET));
public static final RegistryEntry<MenuType<HeldItemMenu>> PRINTOUT = REGISTRY.register("printout",
() -> ContainerData.toType(HeldItemContainerData::new, HeldItemMenu::createPrintout));
() -> ContainerData.toType(
HeldItemContainerData::new,
(id, inventory, data) -> new HeldItemMenu(Menus.PRINTOUT.get(), id, inventory.player, data.getHand())
));
public static final RegistryEntry<MenuType<ViewComputerMenu>> VIEW_COMPUTER = REGISTRY.register("view_computer",
() -> ContainerData.toType(ComputerContainerData::new, ViewComputerMenu::new));
@@ -371,11 +373,11 @@ public final class ModRegistry {
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ClearColourRecipe>> DYEABLE_ITEM_CLEAR = simple("clear_colour", ClearColourRecipe::new);
public static final RegistryEntry<RecipeSerializer<TurtleRecipe>> TURTLE = REGISTRY.register("turtle", () -> TurtleRecipe.validatingSerialiser(TurtleRecipe::of));
public static final RegistryEntry<SimpleCraftingRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new);
public static final RegistryEntry<RecipeSerializer<TurtleOverlayRecipe>> TURTLE_OVERLAY = REGISTRY.register("turtle_overlay", TurtleOverlayRecipe.Serializer::new);
public static final RegistryEntry<RecipeSerializer<TurtleOverlayRecipe>> TURTLE_OVERLAY = REGISTRY.register("turtle_overlay", TurtleOverlayRecipe.Serialiser::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PrintoutRecipe>> PRINTOUT = simple("printout", PrintoutRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<DiskRecipe>> DISK = simple("disk", DiskRecipe::new);
public static final RegistryEntry<RecipeSerializer<ComputerUpgradeRecipe>> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", ComputerUpgradeRecipe.Serializer::new);
public static final RegistryEntry<RecipeSerializer<ComputerUpgradeRecipe>> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", () -> CustomShapedRecipe.validatingSerialiser(ComputerUpgradeRecipe::of));
}
public static class Permissions {
@@ -464,8 +466,8 @@ public final class ModRegistry {
* Register any objects which must be done on the main thread.
*/
public static void registerMainThread() {
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
CauldronInteraction.WATER.put(Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
CauldronInteraction.WATER.put(Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
}
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {

View File

@@ -89,7 +89,7 @@ public final class CommandComputerCraft {
RequiredArgumentBuilder.<CommandSourceStack, ComputersArgumentType.ComputersSupplier>argument("computer", manyComputers())
.suggests((context, builder) -> Suggestions.empty())
)
.argManyValue("args", StringArgumentType.string(), Collections.emptyList())
.argManyValue("args", StringArgumentType.string(), List.of())
.executes((c, a) -> queue(getComputersArgument(c, "computer"), a)))
.then(command("view")
@@ -300,7 +300,7 @@ public final class CommandComputerCraft {
return 1;
}
private static final List<AggregatedMetric> DEFAULT_FIELDS = Arrays.asList(
private static final List<AggregatedMetric> DEFAULT_FIELDS = List.of(
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.COUNT),
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.NONE),
new AggregatedMetric(Metrics.COMPUTER_TASKS, Aggregate.AVG)

View File

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

View File

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

View File

@@ -4,8 +4,6 @@
package dan200.computercraft.shared.common;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.network.container.HeldItemContainerData;
import net.minecraft.network.chat.Component;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.MenuProvider;
@@ -28,10 +26,6 @@ public class HeldItemMenu extends AbstractContainerMenu {
stack = player.getItemInHand(hand).copy();
}
public static HeldItemMenu createPrintout(int id, Inventory inventory, HeldItemContainerData data) {
return new HeldItemMenu(ModRegistry.Menus.PRINTOUT.get(), id, inventory.player, data.getHand());
}
public ItemStack getStack() {
return stack;
}

View File

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

View File

@@ -7,7 +7,6 @@ package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.annotations.ForgeOverride;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.items.IComputerItem;
import dan200.computercraft.shared.platform.RegistryEntry;
import dan200.computercraft.shared.util.BlockEntityHelpers;
@@ -42,13 +41,11 @@ import javax.annotation.Nullable;
public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntity> extends HorizontalDirectionalBlock implements IBundledRedstoneBlock, EntityBlock {
private static final ResourceLocation DROP = new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer");
private final ComputerFamily family;
protected final RegistryEntry<BlockEntityType<T>> type;
private final BlockEntityTicker<T> serverTicker = (level, pos, state, computer) -> computer.serverTick();
protected AbstractComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
protected AbstractComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
super(settings);
this.family = family;
this.type = type;
}
@@ -82,10 +79,6 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
protected abstract ItemStack getItem(AbstractComputerBlockEntity tile);
public ComputerFamily getFamily() {
return family;
}
@Override
@Deprecated
public int getSignal(BlockState state, BlockGetter world, BlockPos pos, Direction incomingSide) {

View File

@@ -4,7 +4,6 @@
package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.platform.RegistryEntry;
import net.minecraft.world.level.block.GameMasterBlock;
import net.minecraft.world.level.block.entity.BlockEntityType;
@@ -17,7 +16,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
* @see dan200.computercraft.shared.computer.items.CommandComputerItem
*/
public class CommandComputerBlock<T extends CommandComputerBlockEntity> extends ComputerBlock<T> implements GameMasterBlock {
public CommandComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
super(settings, family, type);
public CommandComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
super(settings, type);
}
}

View File

@@ -4,9 +4,8 @@
package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.items.ComputerItemFactory;
import dan200.computercraft.shared.computer.items.ComputerItem;
import dan200.computercraft.shared.platform.RegistryEntry;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
@@ -25,8 +24,8 @@ public class ComputerBlock<T extends ComputerBlockEntity> extends AbstractComput
public static final EnumProperty<ComputerState> STATE = EnumProperty.create("state", ComputerState.class);
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public ComputerBlock(Properties settings, ComputerFamily family, RegistryEntry<BlockEntityType<T>> type) {
super(settings, family, type);
public ComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
super(settings, type);
registerDefaultState(defaultBlockState()
.setValue(FACING, Direction.NORTH)
.setValue(STATE, ComputerState.OFF)
@@ -46,6 +45,9 @@ public class ComputerBlock<T extends ComputerBlockEntity> extends AbstractComput
@Override
protected ItemStack getItem(AbstractComputerBlockEntity tile) {
return tile instanceof ComputerBlockEntity ? ComputerItemFactory.create((ComputerBlockEntity) tile) : ItemStack.EMPTY;
if (!(tile instanceof ComputerBlockEntity computer)) return ItemStack.EMPTY;
if (!(asItem() instanceof ComputerItem item)) return ItemStack.EMPTY;
return item.create(computer.getComputerID(), computer.getLabel());
}
}

View File

@@ -32,11 +32,9 @@ public class ComputerBlockEntity extends AbstractComputerBlockEntity {
@Override
protected ServerComputer createComputer(int id) {
var family = getFamily();
return new ServerComputer(
(ServerLevel) getLevel(), getBlockPos(), id, label,
family, Config.computerTermWidth,
Config.computerTermHeight
getFamily(), Config.computerTermWidth, Config.computerTermHeight
);
}

View File

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

View File

@@ -21,6 +21,8 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static dan200.computercraft.api.filesystem.MountConstants.NO_SUCH_FILE;
/**
* A mount backed by Minecraft's {@link ResourceManager}.
*

View File

@@ -8,7 +8,6 @@ import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.filesystem.Mount;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.config.Config;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
@@ -22,11 +21,8 @@ import javax.annotation.Nullable;
import java.util.List;
public abstract class AbstractComputerItem extends BlockItem implements IComputerItem, IMedia {
private final ComputerFamily family;
public AbstractComputerItem(AbstractComputerBlock<?> block, Properties settings) {
super(block, settings);
family = block.getFamily();
}
@Override
@@ -45,13 +41,6 @@ public abstract class AbstractComputerItem extends BlockItem implements ICompute
return IComputerItem.super.getLabel(stack);
}
@Override
public final ComputerFamily getFamily() {
return family;
}
// IMedia implementation
@Override
public boolean setLabel(ItemStack stack, @Nullable String label) {
if (label != null) {

View File

@@ -5,8 +5,8 @@
package dan200.computercraft.shared.computer.items;
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nullable;
@@ -24,9 +24,9 @@ public class ComputerItem extends AbstractComputerItem {
}
@Override
public ItemStack withFamily(ItemStack stack, ComputerFamily family) {
var result = ComputerItemFactory.create(getComputerID(stack), null, family);
if (stack.hasCustomHoverName()) result.setHoverName(stack.getHoverName());
return result;
public ItemStack changeItem(ItemStack stack, Item newItem) {
return newItem instanceof ComputerItem computer
? computer.create(getComputerID(stack), getLabel(stack))
: ItemStack.EMPTY;
}
}

View File

@@ -1,29 +0,0 @@
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
//
// SPDX-License-Identifier: LicenseRef-CCPL
package dan200.computercraft.shared.computer.items;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nullable;
public final class ComputerItemFactory {
private ComputerItemFactory() {
}
public static ItemStack create(ComputerBlockEntity tile) {
return create(tile.getComputerID(), tile.getLabel(), tile.getFamily());
}
public static ItemStack create(int id, @Nullable String label, ComputerFamily family) {
return switch (family) {
case NORMAL -> ModRegistry.Items.COMPUTER_NORMAL.get().create(id, label);
case ADVANCED -> ModRegistry.Items.COMPUTER_ADVANCED.get().create(id, label);
case COMMAND -> ModRegistry.Items.COMPUTER_COMMAND.get().create(id, label);
};
}
}

View File

@@ -4,7 +4,7 @@
package dan200.computercraft.shared.computer.items;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nullable;
@@ -21,7 +21,15 @@ public interface IComputerItem {
return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null;
}
ComputerFamily getFamily();
ItemStack withFamily(ItemStack stack, ComputerFamily family);
/**
* Create a new stack, changing the underlying item.
* <p>
* This should copy the computer's data to a different item of the same type (for instance, converting a normal
* computer to an advanced one).
*
* @param stack The current computer stack.
* @param newItem The new item.
* @return The new stack, possibly {@linkplain ItemStack#EMPTY empty} if {@code newItem} is of the same type.
*/
ItemStack changeItem(ItemStack stack, Item newItem);
}

View File

@@ -46,12 +46,14 @@ public final class GlobalMetrics {
* Add a new global metrics observer. This will receive metrics data for all computers.
*
* @param tracker The observer to add.
* @return Whether the observer was added. {@code false} if the observer was already registered.
*/
public void addObserver(ComputerMetricsObserver tracker) {
public boolean addObserver(ComputerMetricsObserver tracker) {
synchronized (lock) {
if (trackers.contains(tracker)) return;
if (trackers.contains(tracker)) return false;
trackers.add(tracker);
enabled = true;
return true;
}
}
@@ -59,11 +61,13 @@ public final class GlobalMetrics {
* Remove a previously-registered global metrics observer.
*
* @param tracker The observer to add.
* @return Whether the observer was removed. {@code false} if the observer was not registered.
*/
public void removeObserver(ComputerMetricsObserver tracker) {
public boolean removeObserver(ComputerMetricsObserver tracker) {
synchronized (lock) {
trackers.remove(tracker);
var changed = trackers.remove(tracker);
enabled = !trackers.isEmpty();
return changed;
}
}

View File

@@ -10,6 +10,7 @@ import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.metrics.ComputerMetricsObserver;
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
import javax.annotation.concurrent.GuardedBy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -21,29 +22,31 @@ import java.util.Map;
*/
public class BasicComputerMetricsObserver implements ComputerMetricsObserver {
private final GlobalMetrics owner;
private boolean tracking = false;
@GuardedBy("this")
private final List<ComputerMetrics> timings = new ArrayList<>();
@GuardedBy("this")
private final Map<ServerComputer, ComputerMetrics> timingLookup = new MapMaker().weakKeys().makeMap();
public BasicComputerMetricsObserver(GlobalMetrics owner) {
this.owner = owner;
}
public synchronized void start() {
if (!tracking) owner.addObserver(this);
tracking = true;
public void start() {
if (!owner.addObserver(this)) return;
timings.clear();
timingLookup.clear();
synchronized (this) {
timings.clear();
timingLookup.clear();
}
}
public synchronized boolean stop() {
if (!tracking) return false;
owner.removeObserver(this);
tracking = false;
timingLookup.clear();
public boolean stop() {
if (!owner.removeObserver(this)) return false;
synchronized (this) {
timingLookup.clear();
}
return true;
}
@@ -57,6 +60,7 @@ public class BasicComputerMetricsObserver implements ComputerMetricsObserver {
return new ArrayList<>(timings);
}
@GuardedBy("this")
private ComputerMetrics getMetrics(ServerComputer computer) {
var existing = timingLookup.get(computer);
if (existing != null) return existing;

View File

@@ -4,57 +4,44 @@
package dan200.computercraft.shared.computer.recipe;
import com.google.gson.JsonObject;
import com.mojang.serialization.DataResult;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.items.IComputerItem;
import dan200.computercraft.shared.recipe.ShapedRecipeSpec;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeSerializer;
/**
* A recipe which "upgrades" a {@linkplain IComputerItem computer}, converting it from one {@linkplain ComputerFamily
* family} to another.
* A recipe which "upgrades" a {@linkplain IComputerItem computer}, converting to it a new item (for instance a normal
* turtle to an advanced one).
*
* @see IComputerItem#changeItem(ItemStack, Item)
*/
public final class ComputerUpgradeRecipe extends ComputerConvertRecipe {
private final ComputerFamily family;
private final Item result;
private ComputerUpgradeRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe, ComputerFamily family) {
private ComputerUpgradeRecipe(ResourceLocation identifier, ShapedRecipeSpec recipe) {
super(identifier, recipe);
this.family = family;
this.result = recipe.result().getItem();
}
public static DataResult<ComputerUpgradeRecipe> of(ResourceLocation id, ShapedRecipeSpec recipe) {
if (!(recipe.result().getItem() instanceof IComputerItem)) {
return DataResult.error(() -> recipe.result().getItem() + " is not a computer item");
}
return DataResult.success(new ComputerUpgradeRecipe(id, recipe));
}
@Override
protected ItemStack convert(IComputerItem item, ItemStack stack) {
return item.withFamily(stack, family);
return item.changeItem(stack, result);
}
@Override
public RecipeSerializer<ComputerUpgradeRecipe> getSerializer() {
return ModRegistry.RecipeSerializers.COMPUTER_UPGRADE.get();
}
public static class Serializer implements RecipeSerializer<ComputerUpgradeRecipe> {
@Override
public ComputerUpgradeRecipe fromJson(ResourceLocation identifier, JsonObject json) {
var recipe = ShapedRecipeSpec.fromJson(json);
var family = ComputerFamily.getFamily(json, "family");
return new ComputerUpgradeRecipe(identifier, recipe, family);
}
@Override
public ComputerUpgradeRecipe fromNetwork(ResourceLocation identifier, FriendlyByteBuf buf) {
var recipe = ShapedRecipeSpec.fromNetwork(buf);
var family = buf.readEnum(ComputerFamily.class);
return new ComputerUpgradeRecipe(identifier, recipe, family);
}
@Override
public void toNetwork(FriendlyByteBuf buf, ComputerUpgradeRecipe recipe) {
recipe.toSpec().toNetwork(buf);
buf.writeEnum(recipe.family);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.network;
/**
* A type of message to send over the network.
* <p>
* Much like recipe or argument serialisers, each type of {@link NetworkMessage} should have a unique type associated
* with it. This holds platform-specific information about how the packet should be sent over the network.
*
* @param <T> The type of message to send
* @see NetworkMessages
* @see NetworkMessage#type()
*/
public interface MessageType<T extends NetworkMessage<?>> {
}

View File

@@ -17,6 +17,13 @@ import net.minecraft.network.FriendlyByteBuf;
* @see ServerNetworkContext
*/
public interface NetworkMessage<T> {
/**
* Get the type of this message.
*
* @return The type of this message.
*/
MessageType<?> type();
/**
* Write this packet to a buffer.
* <p>
@@ -24,7 +31,7 @@ public interface NetworkMessage<T> {
*
* @param buf The buffer to write data to.
*/
void toBytes(FriendlyByteBuf buf);
void write(FriendlyByteBuf buf);
/**
* Handle this {@link NetworkMessage}.

View File

@@ -4,48 +4,84 @@
package dan200.computercraft.shared.network;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.network.client.*;
import dan200.computercraft.shared.network.server.*;
import dan200.computercraft.shared.platform.PlatformHelper;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import java.util.function.Function;
import java.util.*;
/**
* Registry for all packets provided by CC: Tweaked.
* List of all {@link MessageType}s provided by CC: Tweaked.
*
* @see PlatformHelper The platform helper is used to send packets.
*/
public final class NetworkMessages {
private static final IntSet seenIds = new IntOpenHashSet();
private static final Set<String> seenChannel = new HashSet<>();
private static final List<MessageType<? extends NetworkMessage<ServerNetworkContext>>> serverMessages = new ArrayList<>();
private static final List<MessageType<? extends NetworkMessage<ClientNetworkContext>>> clientMessages = new ArrayList<>();
public static final MessageType<ComputerActionServerMessage> COMPUTER_ACTION = registerServerbound(0, "computer_action", ComputerActionServerMessage.class, ComputerActionServerMessage::new);
public static final MessageType<QueueEventServerMessage> QUEUE_EVENT = registerServerbound(1, "queue_event", QueueEventServerMessage.class, QueueEventServerMessage::new);
public static final MessageType<KeyEventServerMessage> KEY_EVENT = registerServerbound(2, "key_event", KeyEventServerMessage.class, KeyEventServerMessage::new);
public static final MessageType<MouseEventServerMessage> MOUSE_EVENT = registerServerbound(3, "mouse_event", MouseEventServerMessage.class, MouseEventServerMessage::new);
public static final MessageType<UploadFileMessage> UPLOAD_FILE = registerServerbound(4, "upload_file", UploadFileMessage.class, UploadFileMessage::new);
public static final MessageType<ChatTableClientMessage> CHAT_TABLE = registerClientbound(10, "chat_table", ChatTableClientMessage.class, ChatTableClientMessage::new);
public static final MessageType<PocketComputerDataMessage> POCKET_COMPUTER_DATA = registerClientbound(11, "pocket_computer_data", PocketComputerDataMessage.class, PocketComputerDataMessage::new);
public static final MessageType<PocketComputerDeletedClientMessage> POCKET_COMPUTER_DELETED = registerClientbound(12, "pocket_computer_deleted", PocketComputerDeletedClientMessage.class, PocketComputerDeletedClientMessage::new);
public static final MessageType<ComputerTerminalClientMessage> COMPUTER_TERMINAL = registerClientbound(13, "computer_terminal", ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new);
public static final MessageType<PlayRecordClientMessage> PLAY_RECORD = registerClientbound(14, "play_record", PlayRecordClientMessage.class, PlayRecordClientMessage::new);
public static final MessageType<MonitorClientMessage> MONITOR_CLIENT = registerClientbound(15, "monitor_client", MonitorClientMessage.class, MonitorClientMessage::new);
public static final MessageType<SpeakerAudioClientMessage> SPEAKER_AUDIO = registerClientbound(16, "speaker_audio", SpeakerAudioClientMessage.class, SpeakerAudioClientMessage::new);
public static final MessageType<SpeakerMoveClientMessage> SPEAKER_MOVE = registerClientbound(17, "speaker_move", SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new);
public static final MessageType<SpeakerPlayClientMessage> SPEAKER_PLAY = registerClientbound(18, "speaker_play", SpeakerPlayClientMessage.class, SpeakerPlayClientMessage::new);
public static final MessageType<SpeakerStopClientMessage> SPEAKER_STOP = registerClientbound(19, "speaker_stop", SpeakerStopClientMessage.class, SpeakerStopClientMessage::new);
public static final MessageType<UploadResultMessage> UPLOAD_RESULT = registerClientbound(20, "upload_result", UploadResultMessage.class, UploadResultMessage::new);
public static final MessageType<UpgradesLoadedMessage> UPGRADES_LOADED = registerClientbound(21, "upgrades_loaded", UpgradesLoadedMessage.class, UpgradesLoadedMessage::new);
private NetworkMessages() {
}
public interface PacketRegistry {
<T extends NetworkMessage<ClientNetworkContext>> void registerClientbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder);
<T extends NetworkMessage<ServerNetworkContext>> void registerServerbound(int id, Class<T> type, Function<FriendlyByteBuf, T> decoder);
private static <C, T extends NetworkMessage<C>> MessageType<T> register(
List<MessageType<? extends NetworkMessage<C>>> messages,
int id, String channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader
) {
if (!seenIds.add(id)) throw new IllegalArgumentException("Duplicate id " + id);
if (!seenChannel.add(channel)) throw new IllegalArgumentException("Duplicate channel " + channel);
var type = PlatformHelper.get().createMessageType(id, new ResourceLocation(ComputerCraftAPI.MOD_ID, channel), klass, reader);
messages.add(type);
return type;
}
public static void register(PacketRegistry registry) {
// Server messages
registry.registerServerbound(0, ComputerActionServerMessage.class, ComputerActionServerMessage::new);
registry.registerServerbound(1, QueueEventServerMessage.class, QueueEventServerMessage::new);
registry.registerServerbound(2, KeyEventServerMessage.class, KeyEventServerMessage::new);
registry.registerServerbound(3, MouseEventServerMessage.class, MouseEventServerMessage::new);
registry.registerServerbound(4, UploadFileMessage.class, UploadFileMessage::new);
private static <T extends NetworkMessage<ServerNetworkContext>> MessageType<T> registerServerbound(int id, String channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
return register(serverMessages, id, channel, klass, reader);
}
// Client messages
registry.registerClientbound(10, ChatTableClientMessage.class, ChatTableClientMessage::new);
registry.registerClientbound(11, PocketComputerDataMessage.class, PocketComputerDataMessage::new);
registry.registerClientbound(12, PocketComputerDeletedClientMessage.class, PocketComputerDeletedClientMessage::new);
registry.registerClientbound(13, ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new);
registry.registerClientbound(14, PlayRecordClientMessage.class, PlayRecordClientMessage::new);
registry.registerClientbound(15, MonitorClientMessage.class, MonitorClientMessage::new);
registry.registerClientbound(16, SpeakerAudioClientMessage.class, SpeakerAudioClientMessage::new);
registry.registerClientbound(17, SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new);
registry.registerClientbound(18, SpeakerPlayClientMessage.class, SpeakerPlayClientMessage::new);
registry.registerClientbound(19, SpeakerStopClientMessage.class, SpeakerStopClientMessage::new);
registry.registerClientbound(20, UploadResultMessage.class, UploadResultMessage::new);
registry.registerClientbound(21, UpgradesLoadedMessage.class, UpgradesLoadedMessage::new);
private static <T extends NetworkMessage<ClientNetworkContext>> MessageType<T> registerClientbound(int id, String channel, Class<T> klass, FriendlyByteBuf.Reader<T> reader) {
return register(clientMessages, id, channel, klass, reader);
}
/**
* Get all serverbound message types.
*
* @return An unmodifiable sequence of all serverbound message types.
*/
public static Collection<MessageType<? extends NetworkMessage<ServerNetworkContext>>> getServerbound() {
return Collections.unmodifiableCollection(serverMessages);
}
/**
* Get all clientbound message types.
*
* @return An unmodifiable sequence of all clientbound message types.
*/
public static Collection<MessageType<? extends NetworkMessage<ClientNetworkContext>>> getClientbound() {
return Collections.unmodifiableCollection(clientMessages);
}
}

View File

@@ -5,7 +5,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.command.text.TableBuilder;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
@@ -43,7 +45,7 @@ public class ChatTableClientMessage implements NetworkMessage<ClientNetworkConte
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeUtf(table.getId(), MAX_LEN);
buf.writeVarInt(table.getColumns());
buf.writeBoolean(table.getHeaders() != null);
@@ -63,4 +65,9 @@ public class ChatTableClientMessage implements NetworkMessage<ClientNetworkConte
public void handle(ClientNetworkContext context) {
context.handleChatTable(table);
}
@Override
public MessageType<ChatTableClientMessage> type() {
return NetworkMessages.CHAT_TABLE;
}
}

View File

@@ -5,7 +5,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -25,7 +27,7 @@ public class ComputerTerminalClientMessage implements NetworkMessage<ClientNetwo
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(containerId);
terminal.write(buf);
}
@@ -34,4 +36,9 @@ public class ComputerTerminalClientMessage implements NetworkMessage<ClientNetwo
public void handle(ClientNetworkContext context) {
context.handleComputerTerminal(containerId, terminal);
}
@Override
public MessageType<ComputerTerminalClientMessage> type() {
return NetworkMessages.COMPUTER_TERMINAL;
}
}

View File

@@ -5,7 +5,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
@@ -25,7 +27,7 @@ public class MonitorClientMessage implements NetworkMessage<ClientNetworkContext
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeBlockPos(pos);
state.write(buf);
}
@@ -34,4 +36,9 @@ public class MonitorClientMessage implements NetworkMessage<ClientNetworkContext
public void handle(ClientNetworkContext context) {
context.handleMonitorData(pos, state);
}
@Override
public MessageType<MonitorClientMessage> type() {
return NetworkMessages.MONITOR_CLIENT;
}
}

View File

@@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
@@ -43,7 +45,7 @@ public class PlayRecordClientMessage implements NetworkMessage<ClientNetworkCont
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeBlockPos(pos);
buf.writeNullable(soundEvent, (b, e) -> e.writeToNetwork(b));
buf.writeNullable(name, FriendlyByteBuf::writeUtf);
@@ -53,4 +55,9 @@ public class PlayRecordClientMessage implements NetworkMessage<ClientNetworkCont
public void handle(ClientNetworkContext context) {
context.handlePlayRecord(pos, soundEvent, name);
}
@Override
public MessageType<PlayRecordClientMessage> type() {
return NetworkMessages.PLAY_RECORD;
}
}

View File

@@ -7,7 +7,9 @@ package dan200.computercraft.shared.network.client;
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.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
import net.minecraft.network.FriendlyByteBuf;
@@ -35,7 +37,7 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(instanceId);
buf.writeEnum(state);
buf.writeVarInt(lightState);
@@ -46,4 +48,9 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
public void handle(ClientNetworkContext context) {
context.handlePocketComputerData(instanceId, state, lightState, terminal);
}
@Override
public MessageType<PocketComputerDataMessage> type() {
return NetworkMessages.POCKET_COMPUTER_DATA;
}
}

View File

@@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
@@ -20,7 +22,7 @@ public class PocketComputerDeletedClientMessage implements NetworkMessage<Client
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(instanceId);
}
@@ -28,4 +30,9 @@ public class PocketComputerDeletedClientMessage implements NetworkMessage<Client
public void handle(ClientNetworkContext context) {
context.handlePocketComputerDeleted(instanceId);
}
@Override
public MessageType<PocketComputerDeletedClientMessage> type() {
return NetworkMessages.POCKET_COMPUTER_DELETED;
}
}

View File

@@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.network.FriendlyByteBuf;
@@ -47,7 +49,7 @@ public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkCo
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeUUID(source);
pos.write(buf);
buf.writeFloat(volume);
@@ -58,4 +60,9 @@ public class SpeakerAudioClientMessage implements NetworkMessage<ClientNetworkCo
public void handle(ClientNetworkContext context) {
context.handleSpeakerAudio(source, pos, volume);
}
@Override
public MessageType<SpeakerAudioClientMessage> type() {
return NetworkMessages.SPEAKER_AUDIO;
}
}

View File

@@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.network.FriendlyByteBuf;
@@ -33,7 +35,7 @@ public class SpeakerMoveClientMessage implements NetworkMessage<ClientNetworkCon
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeUUID(source);
pos.write(buf);
}
@@ -42,4 +44,9 @@ public class SpeakerMoveClientMessage implements NetworkMessage<ClientNetworkCon
public void handle(ClientNetworkContext context) {
context.handleSpeakerMove(source, pos);
}
@Override
public MessageType<SpeakerMoveClientMessage> type() {
return NetworkMessages.SPEAKER_MOVE;
}
}

View File

@@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import net.minecraft.network.FriendlyByteBuf;
@@ -43,7 +45,7 @@ public class SpeakerPlayClientMessage implements NetworkMessage<ClientNetworkCon
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeUUID(source);
pos.write(buf);
buf.writeResourceLocation(sound);
@@ -55,4 +57,9 @@ public class SpeakerPlayClientMessage implements NetworkMessage<ClientNetworkCon
public void handle(ClientNetworkContext context) {
context.handleSpeakerPlay(source, pos, sound, volume, pitch);
}
@Override
public MessageType<SpeakerPlayClientMessage> type() {
return NetworkMessages.SPEAKER_PLAY;
}
}

View File

@@ -4,7 +4,9 @@
package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
import net.minecraft.network.FriendlyByteBuf;
@@ -29,7 +31,7 @@ public class SpeakerStopClientMessage implements NetworkMessage<ClientNetworkCon
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeUUID(source);
}
@@ -37,4 +39,9 @@ public class SpeakerStopClientMessage implements NetworkMessage<ClientNetworkCon
public void handle(ClientNetworkContext context) {
context.handleSpeakerStop(source);
}
@Override
public MessageType<SpeakerStopClientMessage> type() {
return NetworkMessages.SPEAKER_STOP;
}
}

View File

@@ -13,7 +13,9 @@ import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.impl.PocketUpgrades;
import dan200.computercraft.impl.TurtleUpgrades;
import dan200.computercraft.impl.UpgradeManager;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.platform.PlatformHelper;
import net.minecraft.core.Registry;
import net.minecraft.network.FriendlyByteBuf;
@@ -27,7 +29,7 @@ import java.util.Objects;
/**
* Syncs turtle and pocket upgrades to the client.
*/
public class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContext> {
public final class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContext> {
private final Map<String, UpgradeManager.UpgradeWrapper<TurtleUpgradeSerialiser<?>, ITurtleUpgrade>> turtleUpgrades;
private final Map<String, UpgradeManager.UpgradeWrapper<PocketUpgradeSerialiser<?>, IPocketUpgrade>> pocketUpgrades;
@@ -65,7 +67,7 @@ public class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContex
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
toBytes(buf, TurtleUpgradeSerialiser.registryId(), turtleUpgrades);
toBytes(buf, PocketUpgradeSerialiser.registryId(), pocketUpgrades);
}
@@ -95,4 +97,9 @@ public class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContex
TurtleUpgrades.instance().loadFromNetwork(turtleUpgrades);
PocketUpgrades.instance().loadFromNetwork(pocketUpgrades);
}
@Override
public MessageType<UpgradesLoadedMessage> type() {
return NetworkMessages.UPGRADES_LOADED;
}
}

View File

@@ -6,7 +6,9 @@ package dan200.computercraft.shared.network.client;
import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.computer.upload.UploadResult;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -43,7 +45,7 @@ public class UploadResultMessage implements NetworkMessage<ClientNetworkContext>
}
@Override
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(containerId);
buf.writeEnum(result);
if (result == UploadResult.ERROR) buf.writeComponent(Nullability.assertNonNull(errorMessage));
@@ -53,4 +55,9 @@ public class UploadResultMessage implements NetworkMessage<ClientNetworkContext>
public void handle(ClientNetworkContext context) {
context.handleUploadResult(containerId, result, errorMessage);
}
@Override
public MessageType<UploadResultMessage> type() {
return NetworkMessages.UPLOAD_RESULT;
}
}

View File

@@ -5,6 +5,8 @@
package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -23,8 +25,8 @@ public class ComputerActionServerMessage extends ComputerServerMessage {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
super.toBytes(buf);
public void write(FriendlyByteBuf buf) {
super.write(buf);
buf.writeEnum(action);
}
@@ -37,6 +39,11 @@ public class ComputerActionServerMessage extends ComputerServerMessage {
}
}
@Override
public MessageType<ComputerActionServerMessage> type() {
return NetworkMessages.COMPUTER_ACTION;
}
public enum Action {
TURN_ON,
SHUTDOWN,

View File

@@ -28,7 +28,7 @@ public abstract class ComputerServerMessage implements NetworkMessage<ServerNetw
@Override
@OverridingMethodsMustInvokeSuper
public void toBytes(FriendlyByteBuf buf) {
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(containerId);
}

View File

@@ -5,6 +5,8 @@
package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -30,8 +32,8 @@ public class KeyEventServerMessage extends ComputerServerMessage {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
super.toBytes(buf);
public void write(FriendlyByteBuf buf) {
super.write(buf);
buf.writeByte(type);
buf.writeVarInt(key);
}
@@ -45,4 +47,9 @@ public class KeyEventServerMessage extends ComputerServerMessage {
input.keyDown(key, type == TYPE_REPEAT);
}
}
@Override
public MessageType<KeyEventServerMessage> type() {
return NetworkMessages.KEY_EVENT;
}
}

View File

@@ -5,6 +5,8 @@
package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessages;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -37,8 +39,8 @@ public class MouseEventServerMessage extends ComputerServerMessage {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
super.toBytes(buf);
public void write(FriendlyByteBuf buf) {
super.write(buf);
buf.writeByte(type);
buf.writeVarInt(arg);
buf.writeVarInt(x);
@@ -55,4 +57,9 @@ public class MouseEventServerMessage extends ComputerServerMessage {
case TYPE_SCROLL -> input.mouseScroll(arg, x, y);
}
}
@Override
public MessageType<MouseEventServerMessage> type() {
return NetworkMessages.MOUSE_EVENT;
}
}

View File

@@ -7,6 +7,8 @@ package dan200.computercraft.shared.network.server;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.menu.ServerInputHandler;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessages;
import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -37,8 +39,8 @@ public class QueueEventServerMessage extends ComputerServerMessage {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
super.toBytes(buf);
public void write(FriendlyByteBuf buf) {
super.write(buf);
buf.writeUtf(event);
buf.writeNbt(args == null ? null : NBTUtil.encodeObjects(args));
}
@@ -47,4 +49,9 @@ public class QueueEventServerMessage extends ComputerServerMessage {
protected void handle(ServerNetworkContext context, ComputerMenu container) {
container.getInput().queueEvent(event, args);
}
@Override
public MessageType<QueueEventServerMessage> type() {
return NetworkMessages.QUEUE_EVENT;
}
}

View File

@@ -9,6 +9,8 @@ import dan200.computercraft.shared.computer.menu.ComputerMenu;
import dan200.computercraft.shared.computer.upload.FileSlice;
import dan200.computercraft.shared.computer.upload.FileUpload;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.network.MessageType;
import dan200.computercraft.shared.network.NetworkMessages;
import io.netty.handler.codec.DecoderException;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -91,8 +93,8 @@ public class UploadFileMessage extends ComputerServerMessage {
}
@Override
public void toBytes(FriendlyByteBuf buf) {
super.toBytes(buf);
public void write(FriendlyByteBuf buf) {
super.write(buf);
buf.writeUUID(uuid);
buf.writeByte(flag);
@@ -166,4 +168,9 @@ public class UploadFileMessage extends ComputerServerMessage {
input.continueUpload(uuid, slices);
if ((flag & FLAG_LAST) != 0) input.finishUpload(player, uuid);
}
@Override
public MessageType<UploadFileMessage> type() {
return NetworkMessages.UPLOAD_FILE;
}
}

View File

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

View File

@@ -6,12 +6,9 @@ package dan200.computercraft.shared.peripheral.generic;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
import dan200.computercraft.core.methods.MethodSupplier;
import dan200.computercraft.core.methods.NamedMethod;
import dan200.computercraft.core.methods.PeripheralMethod;
import dan200.computercraft.shared.computer.core.ServerContext;
import net.minecraft.core.Direction;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.block.entity.BlockEntity;
import javax.annotation.Nullable;
@@ -28,16 +25,10 @@ import java.util.Set;
* See the platform-specific peripheral providers for the usage of this.
*/
final class GenericPeripheralBuilder {
private final MethodSupplier<PeripheralMethod> peripheralMethods;
private @Nullable String name;
private final Set<String> additionalTypes = new HashSet<>(0);
private final ArrayList<SaturatedMethod> methods = new ArrayList<>();
GenericPeripheralBuilder(MinecraftServer server) {
peripheralMethods = ServerContext.get(server).peripheralMethods();
}
@Nullable
IPeripheral toPeripheral(BlockEntity blockEntity, Direction side) {
if (methods.isEmpty()) return null;
@@ -46,18 +37,16 @@ final class GenericPeripheralBuilder {
return new GenericPeripheral(blockEntity, side, name, additionalTypes, methods);
}
boolean addMethods(Object target) {
return peripheralMethods.forEachSelfMethod(target, (name, method, info) -> {
methods.add(new SaturatedMethod(target, name, method));
void addMethod(Object target, String name, PeripheralMethod method, @Nullable NamedMethod<PeripheralMethod> info) {
methods.add(new SaturatedMethod(target, name, method));
// If we have a peripheral type, use it. Always pick the smallest one, so it's consistent (assuming mods
// don't change).
var type = info == null ? null : info.genericType();
if (type != null && type.getPrimaryType() != null) {
var primaryType = type.getPrimaryType();
if (this.name == null || this.name.compareTo(primaryType) > 0) this.name = primaryType;
}
if (type != null) additionalTypes.addAll(type.getAdditionalTypes());
});
// If we have a peripheral type, use it. Always pick the smallest one, so it's consistent (assuming mods
// don't change).
var type = info == null ? null : info.genericType();
if (type != null && type.getPrimaryType() != null) {
var primaryType = type.getPrimaryType();
if (this.name == null || this.name.compareTo(primaryType) > 0) this.name = primaryType;
}
if (type != null) additionalTypes.addAll(type.getAdditionalTypes());
}
}

View File

@@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.peripheral.generic;
import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.methods.MethodSupplier;
import dan200.computercraft.core.methods.PeripheralMethod;
import dan200.computercraft.shared.computer.core.ServerContext;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* A peripheral provider which finds methods from various {@linkplain GenericSource generic sources}.
* <p>
* Methods are found using the original block entity itself and a registered list of {@link ComponentLookup}s.
*
* @param <C> A platform-specific type, used for the invalidation callback.
*/
public final class GenericPeripheralProvider<C extends Runnable> {
private static final Logger LOG = LoggerFactory.getLogger(GenericPeripheralProvider.class);
private final List<ComponentLookup<? super C>> lookups = new ArrayList<>();
/**
* Register a component lookup function.
*
* @param lookup The component lookup function.
*/
public synchronized void registerLookup(ComponentLookup<? super C> lookup) {
Objects.requireNonNull(lookup);
if (!lookups.contains(lookup)) lookups.add(lookup);
}
public void forEachMethod(MethodSupplier<PeripheralMethod> methods, Level level, BlockPos pos, Direction side, BlockEntity blockEntity, C invalidate, MethodSupplier.TargetedConsumer<PeripheralMethod> consumer) {
methods.forEachMethod(blockEntity, consumer);
for (var lookup : lookups) {
var contents = lookup.find(level, pos, blockEntity.getBlockState(), blockEntity, side, invalidate);
if (contents != null) methods.forEachMethod(contents, consumer);
}
}
@Nullable
public IPeripheral getPeripheral(Level level, BlockPos pos, Direction side, @Nullable BlockEntity blockEntity, C invalidate) {
if (blockEntity == null) return null;
var server = level.getServer();
if (server == null) {
LOG.warn("Fetching peripherals on a non-server level {}.", level, new IllegalStateException("Fetching peripherals on a non-server level."));
return null;
}
var builder = new GenericPeripheralBuilder();
forEachMethod(ServerContext.get(server).peripheralMethods(), level, pos, side, blockEntity, invalidate, builder::addMethod);
return builder.toPeripheral(blockEntity, side);
}
}

View File

@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import dan200.computercraft.api.peripheral.PeripheralType;
/**
* Methods for interacting with blocks which store energy.
* <p>
* This works with energy storage blocks, as well as generators and machines which consume energy.
* <p>
* > [!NOTE]
* > Due to limitations with Forge's energy API, it is not possible to measure throughput (i.e. FE used/generated per
* > tick).
*
* @param <T> The type for energy storage.
* @cc.module energy_storage
* @cc.since 1.94.0
*/
public abstract class AbstractEnergyMethods<T> implements GenericPeripheral {
@Override
public final PeripheralType getType() {
return PeripheralType.ofAdditional("energy_storage");
}
@Override
public final String id() {
return ComputerCraftAPI.MOD_ID + ":energy";
}
/**
* Get the energy of this block.
*
* @param energy The current energy storage.
* @return The energy stored in this block, in FE.
*/
@LuaFunction(mainThread = true)
public abstract int getEnergy(T energy);
/**
* Get the maximum amount of energy this block can store.
*
* @param energy The current energy storage.
* @return The energy capacity of this block.
*/
@LuaFunction(mainThread = true)
public abstract int getEnergyCapacity(T energy);
}

View File

@@ -0,0 +1,92 @@
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.PeripheralType;
import java.util.Map;
import java.util.Optional;
/**
* Methods for interacting with tanks and other fluid storage blocks.
*
* @param <T> The type for fluid inventories.
* @cc.module fluid_storage
* @cc.since 1.94.0
*/
public abstract class AbstractFluidMethods<T> implements GenericPeripheral {
@Override
public final PeripheralType getType() {
return PeripheralType.ofAdditional("fluid_storage");
}
@Override
public final String id() {
return ComputerCraftAPI.MOD_ID + ":fluid";
}
/**
* Get all "tanks" in this fluid storage.
* <p>
* Each tank either contains some amount of fluid or is empty. Tanks with fluids inside will return some basic
* information about the fluid, including its name and amount.
* <p>
* The returned table is sparse, and so empty tanks will be `nil` - it is recommended to loop over using [`pairs`]
* rather than [`ipairs`].
*
* @param fluids The current fluid handler.
* @return All tanks.
* @cc.treturn { (table|nil)... } All tanks in this fluid storage.
*/
@LuaFunction(mainThread = true)
public abstract Map<Integer, Map<String, ?>> tanks(T fluids);
/**
* Move a fluid from one fluid container to another connected one.
* <p>
* This allows you to pull fluid in the current fluid container to another container <em>on the same wired
* network</em>. Both containers must attached to wired modems which are connected via a cable.
*
* @param from Container to move fluid from.
* @param computer The current computer.
* @param toName The name of the peripheral/container to push to. This is the string given to [`peripheral.wrap`],
* and displayed by the wired modem.
* @param limit The maximum amount of fluid to move.
* @param fluidName The fluid to move. If not given, an arbitrary fluid will be chosen.
* @return The amount of moved fluid.
* @throws LuaException If the peripheral to transfer to doesn't exist or isn't an fluid container.
* @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
*/
@LuaFunction(mainThread = true)
public abstract int pushFluid(
T from, IComputerAccess computer, String toName, Optional<Integer> limit, Optional<String> fluidName
) throws LuaException;
/**
* Move a fluid from a connected fluid container into this oneone.
* <p>
* This allows you to pull fluid in the current fluid container from another container <em>on the same wired
* network</em>. Both containers must attached to wired modems which are connected via a cable.
*
* @param to Container to move fluid to.
* @param computer The current computer.
* @param fromName The name of the peripheral/container to push to. This is the string given to [`peripheral.wrap`],
* and displayed by the wired modem.
* @param limit The maximum amount of fluid to move.
* @param fluidName The fluid to move. If not given, an arbitrary fluid will be chosen.
* @return The amount of moved fluid.
* @throws LuaException If the peripheral to transfer to doesn't exist or isn't an fluid container.
* @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
*/
@LuaFunction(mainThread = true)
public abstract int pullFluid(
T to, IComputerAccess computer, String fromName, Optional<Integer> limit, Optional<String> fluidName
) throws LuaException;
}

View File

@@ -0,0 +1,197 @@
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
//
// SPDX-License-Identifier: LicenseRef-CCPL
package dan200.computercraft.shared.peripheral.generic.methods;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaFunction;
import dan200.computercraft.api.peripheral.GenericPeripheral;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.PeripheralType;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Optional;
/**
* Methods for interacting with inventories.
*
* @param <T> The type for inventories.
* @cc.module inventory
* @cc.since 1.94.0
*/
public abstract class AbstractInventoryMethods<T> implements GenericPeripheral {
@Override
public final PeripheralType getType() {
return PeripheralType.ofAdditional("inventory");
}
@Override
public final String id() {
return ComputerCraftAPI.MOD_ID + ":inventory";
}
/**
* Get the size of this inventory.
*
* @param inventory The current inventory.
* @return The number of slots in this inventory.
*/
@LuaFunction(mainThread = true)
public abstract int size(T inventory);
/**
* List all items in this inventory. This returns a table, with an entry for each slot.
* <p>
* Each item in the inventory is represented by a table containing some basic information, much like
* {@link dan200.computercraft.shared.turtle.apis.TurtleAPI#getItemDetail(ILuaContext, Optional, Optional)}
* includes. More information can be fetched with {@link #getItemDetail}. The table contains the item `name`, the
* `count` and an a (potentially nil) hash of the item's `nbt.` This NBT data doesn't contain anything useful, but
* allows you to distinguish identical items.
* <p>
* The returned table is sparse, and so empty slots will be `nil` - it is recommended to loop over using [`pairs`]
* rather than [`ipairs`].
*
* @param inventory The current inventory.
* @return All items in this inventory.
* @cc.treturn { (table|nil)... } All items in this inventory.
* @cc.usage Find an adjacent chest and print all items in it.
*
* <pre>{@code
* local chest = peripheral.find("minecraft:chest")
* for slot, item in pairs(chest.list()) do
* print(("%d x %s in slot %d"):format(item.count, item.name, slot))
* end
* }</pre>
*/
@LuaFunction(mainThread = true)
public abstract Map<Integer, Map<String, ?>> list(T inventory);
/**
* Get detailed information about an item.
* <p>
* The returned information contains the same information as each item in
* {@link #list}, as well as additional details like the display name
* (`displayName`), and item and item durability (`damage`, `maxDamage`, `durability`).
* <p>
* Some items include more information (such as enchantments) - it is
* recommended to print it out using [`textutils.serialize`] or in the Lua
* REPL, to explore what is available.
* <p>
* > [Deprecated fields][!INFO]
* > Older versions of CC: Tweaked exposed an {@code itemGroups} field, listing the
* > creative tabs an item was available under. This information is no longer available on
* > more recent versions of the game, and so this field will always be empty. Do not use this
* > field in new code!
*
* @param inventory The current inventory.
* @param slot The slot to get information about.
* @return Information about the item in this slot, or {@code nil} if not present.
* @throws LuaException If the slot is out of range.
* @cc.treturn table Information about the item in this slot, or {@code nil} if not present.
* @cc.usage Print some information about the first in a chest.
*
* <pre>{@code
* local chest = peripheral.find("minecraft:chest")
* local item = chest.getItemDetail(1)
* if not item then print("No item") return end
*
* print(("%s (%s)"):format(item.displayName, item.name))
* print(("Count: %d/%d"):format(item.count, item.maxCount))
*
* if item.damage then
* print(("Damage: %d/%d"):format(item.damage, item.maxDamage))
* end
* }</pre>
*/
@Nullable
@LuaFunction(mainThread = true)
public abstract Map<String, ?> getItemDetail(T inventory, int slot) throws LuaException;
/**
* Get the maximum number of items which can be stored in this slot.
* <p>
* Typically this will be limited to 64 items. However, some inventories (such as barrels or caches) can store
* hundreds or thousands of items in one slot.
*
* @param inventory Inventory to probe.
* @param slot The slot
* @return The maximum number of items in this slot.
* @throws LuaException If the slot is out of range.
* @cc.usage Count the maximum number of items an adjacent chest can hold.
* <pre>{@code
* local chest = peripheral.find("minecraft:chest")
* local total = 0
* for i = 1, chest.size() do
* total = total + chest.getItemLimit(i)
* end
* print(total)
* }</pre>
* @cc.since 1.96.0
*/
@LuaFunction(mainThread = true)
public abstract long getItemLimit(T inventory, int slot) throws LuaException;
/**
* Push items from one inventory to another connected one.
* <p>
* This allows you to push an item in an inventory to another inventory <em>on the same wired network</em>. Both
* inventories must attached to wired modems which are connected via a cable.
*
* @param from Inventory to move items from.
* @param computer The current computer.
* @param toName The name of the peripheral/inventory to push to. This is the string given to [`peripheral.wrap`],
* and displayed by the wired modem.
* @param fromSlot The slot in the current inventory to move items to.
* @param limit The maximum number of items to move. Defaults to the current stack limit.
* @param toSlot The slot in the target inventory to move to. If not given, the item will be inserted into any slot.
* @return The number of transferred items.
* @throws LuaException If the peripheral to transfer to doesn't exist or isn't an inventory.
* @throws LuaException If either source or destination slot is out of range.
* @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
* @cc.usage Wrap two chests, and push an item from one to another.
* <pre>{@code
* local chest_a = peripheral.wrap("minecraft:chest_0")
* local chest_b = peripheral.wrap("minecraft:chest_1")
*
* chest_a.pushItems(peripheral.getName(chest_b), 1)
* }</pre>
*/
@LuaFunction(mainThread = true)
public abstract int pushItems(
T from, IComputerAccess computer, String toName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
) throws LuaException;
/**
* Pull items from a connected inventory into this one.
* <p>
* This allows you to transfer items between inventories <em>on the same wired network</em>. Both this and the source
* inventory must attached to wired modems which are connected via a cable.
*
* @param to Inventory to move items to.
* @param computer The current computer.
* @param fromName The name of the peripheral/inventory to pull from. This is the string given to [`peripheral.wrap`],
* and displayed by the wired modem.
* @param fromSlot The slot in the source inventory to move items from.
* @param limit The maximum number of items to move. Defaults to the current stack limit.
* @param toSlot The slot in current inventory to move to. If not given, the item will be inserted into any slot.
* @return The number of transferred items.
* @throws LuaException If the peripheral to transfer to doesn't exist or isn't an inventory.
* @throws LuaException If either source or destination slot is out of range.
* @cc.see peripheral.getName Allows you to get the name of a [wrapped][`peripheral.wrap`] peripheral.
* @cc.usage Wrap two chests, and push an item from one to another.
* <pre>{@code
* local chest_a = peripheral.wrap("minecraft:chest_0")
* local chest_b = peripheral.wrap("minecraft:chest_1")
*
* chest_a.pullItems(peripheral.getName(chest_b), 1)
* }</pre>
*/
@LuaFunction(mainThread = true)
public abstract int pullItems(
T to, IComputerAccess computer, String fromName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
) throws LuaException;
}

View File

@@ -4,12 +4,12 @@
package dan200.computercraft.shared.peripheral.modem.wired;
import com.google.common.collect.ImmutableMap;
import dan200.computercraft.annotations.ForgeOverride;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.util.WaterloggableHelpers;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
@@ -52,12 +52,14 @@ public class CableBlock extends Block implements SimpleWaterloggedBlock, EntityB
public static final BooleanProperty UP = BooleanProperty.create("up");
public static final BooleanProperty DOWN = BooleanProperty.create("down");
static final EnumMap<Direction, BooleanProperty> CONNECTIONS =
new EnumMap<>(new ImmutableMap.Builder<Direction, BooleanProperty>()
.put(Direction.DOWN, DOWN).put(Direction.UP, UP)
.put(Direction.NORTH, NORTH).put(Direction.SOUTH, SOUTH)
.put(Direction.WEST, WEST).put(Direction.EAST, EAST)
.build());
static final EnumMap<Direction, BooleanProperty> CONNECTIONS = Util.make(new EnumMap<>(Direction.class), m -> {
m.put(Direction.DOWN, DOWN);
m.put(Direction.UP, UP);
m.put(Direction.NORTH, NORTH);
m.put(Direction.SOUTH, SOUTH);
m.put(Direction.WEST, WEST);
m.put(Direction.EAST, EAST);
});
public CableBlock(Properties settings) {
super(settings);

View File

@@ -4,7 +4,6 @@
package dan200.computercraft.shared.peripheral.modem.wired;
import com.google.common.base.Objects;
import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.network.wired.WiredNode;
import dan200.computercraft.api.peripheral.IPeripheral;
@@ -31,7 +30,8 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
public class CableBlockEntity extends BlockEntity {
private static final String NBT_PERIPHERAL_ENABLED = "PeripheralAccess";
@@ -181,7 +181,7 @@ public class CableBlockEntity extends BlockEntity {
var oldName = peripheral.getConnectedName();
togglePeripheralAccess();
var newName = peripheral.getConnectedName();
if (!Objects.equal(newName, oldName)) {
if (!Objects.equals(newName, oldName)) {
if (oldName != null) {
player.displayClientMessage(Component.translatable("chat.computercraft.wired_modem.peripheral_disconnected",
ChatHelpers.copy(oldName)), false);
@@ -274,7 +274,7 @@ public class CableBlockEntity extends BlockEntity {
if (!canAttachPeripheral() && peripheralAccessAllowed) {
peripheralAccessAllowed = false;
peripheral.detach();
node.updatePeripherals(Collections.emptyMap());
node.updatePeripherals(Map.of());
setChanged();
updateBlockState();
}
@@ -291,7 +291,7 @@ public class CableBlockEntity extends BlockEntity {
peripheral.detach();
peripheralAccessAllowed = false;
node.updatePeripherals(Collections.emptyMap());
node.updatePeripherals(Map.of());
}
updateBlockState();

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