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

Compare commits

...

60 Commits

Author SHA1 Message Date
Jonathan Coates
9e6e0c8b88 Bump CC:T to 1.103.0 2023-02-09 21:02:09 +00:00
Jonathan Coates
5502412181 Improve display of runtime errors (#1320)
- Bump Cobalt to 0.6.0. We now track both line and column numbers of
   each bytecode instruction, allowing us to map an error to a concrete
   position.

 - `loadfile` (and similar functions) now use the full path, rather than
   the file name. Cobalt truncates this to 30 characters (rather than
   the previous 60) so this should be less noisy.

 - The shell, edit and Lua REPL now display the corresponding source
   code alongside an error.

   Note this is incredibly limited right now - it won't cope with errors
   which cross coroutine boundaries. Supporting this is on the roadmap,
   but requires some careful API design.
2023-02-09 20:53:50 +00:00
Jonathan Coates
62e3c5f9aa Queue key presses for a short period after pressing a computer shortcut
Fixes #1326
2023-02-09 20:10:20 +00:00
Jonathan Coates
7e54a40fa9 Add a couple of missing version annotations 2023-02-09 20:09:57 +00:00
Jonathan Coates
8ac42566ec Be a little more robust in turtle item rendering
See #1328. Also some holiday cleanup, because my commit discipline is
terrible.
2023-02-09 20:07:55 +00:00
Jonathan Coates
66b20d2bdb Don't dye/undye turtles when right clicked
While it's a nice immersive interaction, it's far too easy to trigger by
accident. Dying/undying can now only be done via crafting and the
cauldron.

Closes #1321
2023-02-08 20:14:07 +00:00
Jonathan Coates
81dad421d5 Allow undying items using a sponge
It's much less aggressive than dunking it in a cauldron, so won't damage
any of your precious electronics.

I had this idea back in 2017 (dan200/ComputerCraft#230). Can't believe
it took me almost 6 years to implement.
2023-02-08 20:13:58 +00:00
Jonathan Coates
3075d3cea8 Fxi teh spolling of handshakder 2023-02-07 20:59:45 +00:00
Jonathan Coates
cdab8f429e Clarify Minecraft' shift+right click behaviour
Closes #1323
2023-02-07 08:45:17 +00:00
Jonathan Coates
2e5cd29e12 Use tags to what items turtle.place() can .use()
In older versions we just used a hard-coded list of items and
superclasses. This was somewhat ugly, and so in 1.19.3 I tried to make
this code more generic.

However, this has a lot of unintended consequences - for instance
turtles can now throw ender pearls, which is definitely not intended!

By using a tag, we can emulate the old behaviour, while still allowing
modders and pack devs to add additional items if needed.
2023-02-04 18:14:18 +00:00
Jonathan Coates
22cadd6730 Various improvements to our Lua parser
- Encode the DFA as a virtual machine (identical to lrgrep) rather than
   compiling it to a series of Lua functions. While this is a little
   slower and uglier, it's much more space efficient, shaving off 16Kb.

 - Minimise the DFA properly. This only shaves off a few states, but
   every little helps.

 - Run the error handling code from a non-reduced parser stack. This was
   incredibly nasty to get right (and positions are still not correctly
   handled), but it fixes several broken error messages.
2023-02-04 12:46:51 +00:00
Jonathan Coates
366052ec48 Merge pull request #1316 from LelouBil/patch-1
Fix word error in rednet documentation
2023-02-03 19:39:13 +00:00
Jonathan Coates
3224e0bf8b Fix a few typos in the documentation
- Move modem recipes out of the usage section.
 - Add missing argument names to BinaryWriableHandle.write. Illuaminate
   really should catch this, but for now I did a grep and couldn't find
   any more instances of this.
2023-02-03 19:29:16 +00:00
Weblate
fb4b097a66 Translations for Toki Pona
Co-authored-by: wackbyte <wackbyte@protonmail.com>
2023-02-03 04:55:48 +00:00
Bilel MEDIMEGH
67f3d91850 Fix word error in rednet documentation
Replaced "The name this protocol exposes for the given protocol" into "The name this **computer** exposes for the given protocol"
2023-01-30 18:19:06 +01:00
Jonathan Coates
1e3a930543 Copy pocket upgrades items before dropping them
We were modifying the stack, meaning the crafting item would end up
empty!
2023-01-26 22:40:03 +00:00
Jonathan Coates
b21e2f4e63 Update Cobalt to fix yielding inside __len
Closes #1307
2023-01-26 09:43:10 +00:00
Jonathan Coates
a12b405acf Custom parse errors for Lua (#1298)
- Add several (internal) modules for lexing and parsing Lua code. These
   allow us to provide (hopefully) higher quality error messages than
   Lua's built-in messages.

 - `shell.run`, `edit` and `lua` now use this parser when fed invalid
   code. This allows us to provide better syntax errors, while not
   having any impact on the happy path.

   Note this does not affect any other mechanism for loading code 
   (`load`, `require`, `dofile`).

There's still a lot of work to do here in improving error message
quality, but hopefully this provides a good starting point.
2023-01-25 20:35:43 +00:00
Jonathan Coates
e076818b29 Bump Cobalt for even more verbose VM logging
See #1307
2023-01-25 18:47:27 +00:00
Jonathan Coates
1554c7b397 Always expose nbt from turtle.getItemDetail
- Document the thread safety of DetailRegistry a little better.

 - Turtles now duplicate their inventory to the "previous
   inventory" (now called inventorySnapshot) immediately, rather than
   when the block is ticked.

   This is slightly more resource intensive, but I don't think it's so
   bad we need to worry.

 - As this snapshot is now always up-to-date, we can read it from the
   computer thread. Given the item is immutable, it's safe to read NBT
   from it.

   _Technically_ this is not safe under the Java memory model, but in
   practice I don't think we'll observe the wrong value.

Closes #1306
2023-01-25 18:37:14 +00:00
Jonathan Coates
6cd32a6368 Fix a few typos
Just ran[^1] over the codebase. Turns out we'd duplicated one of the
changelog entries entirely - I suspect due to a version merge gone
wrong!

[^1]: https://github.com/crate-ci/typos/
2023-01-24 18:47:29 +00:00
Jonathan Coates
7335a892b5 Expose test methods to required libraries
This allows us to use stub everywhere, rather than having to pass it
around as an argument.
2023-01-24 18:27:30 +00:00
Weblate
83eddc6636 Translations for Toki Pona
Translations for Italian

Co-authored-by: Alessandro <ale.proto00@gmail.com>
Co-authored-by: wackbyte <wackbyte@protonmail.com>
2023-01-24 05:55:48 +00:00
Jonathan Coates
d066d175bf Attach a speaker peripheral inside its docs
This required a version bump to cct-javadoc to allow using attributes
inside <pre> blocks.
2023-01-22 12:24:36 +00:00
Jonathan Coates
9873ccfa0d Send the original rednet message to the current computer
We were incorrectly enquing the modem payload, not the underlying rednet
message.

Closes #1308.
2023-01-21 08:23:00 +00:00
Weblate
da7a50368d Added translation for Toki Pona
Co-authored-by: wackbyte <wackbyte@protonmail.com>
2023-01-18 14:59:35 +00:00
Jonathan Coates
8cfbfe7ceb Make errors messages in edit more obvious
See #1222.
2023-01-17 21:43:26 +00:00
ouroborus
67244b17af Update event docs (#1304) 2023-01-17 21:17:47 +00:00
Jonathan Coates
9e1de23f4a Log internal Cobalt VM errors
See #1307
2023-01-17 20:37:16 +00:00
Jonathan Coates
86b60855d6 Enforce version bounds for the Kotlin stdlib
We were pulling in an ancient version of the jdk8 stdlib via
kotlinx.coroutines, hence the conflict in versions.
2023-01-17 20:28:31 +00:00
Jonathan Coates
db6b6fd173 Bump Kotlin version to 1.8 2023-01-17 19:33:49 +00:00
Jonathan Coates
2014e9527e Some event typo fixes 2023-01-16 22:53:58 +00:00
Emma
f43b839056 Add support for shebangs (#1273) 2023-01-14 22:12:04 +00:00
Jonathan Coates
f561572509 Bump CC:T to 1.102.2 2023-01-14 20:07:01 +00:00
Jonathan Coates
edb21f33be Mount drives on the main thread
When the peripheral is attached, we add the computer to the map and
queue the actual disk to be mounted next tick. This avoids the
thread-safety issues with mutating the item (and creating disk ids) that
might be caused by doing it on the computer thread.

The mount is now also managed separately to the MediaStack, as that was
meant to be an immutable snapshot of the item!

Fixes #1282
2023-01-14 17:51:49 +00:00
Jonathan Coates
02b68b259e Correctly track coverage for startup.lua too 2023-01-12 22:26:39 +00:00
Jonathan Coates
28a55349a9 Move coverage to the Java side
While slightly irritating (requires Cobalt magic), it's much, much
faster.
2023-01-12 21:02:33 +00:00
Jonathan Coates
2457a31728 Fix printouts crashing in item frames 2023-01-09 18:28:44 +00:00
Jonathan Coates
cdc91a8e5d Fix stack overflow in logging code 2023-01-08 18:29:36 +00:00
Jonathan Coates
8024017f53 Bump CC:T to 1.102.1
There's still some remaining bugs (#1282), but I think worth getting the
fixes for the worst issues out first.
2023-01-08 18:21:10 +00:00
Jonathan Coates
592ff84aea Read computer threads directly from the config object (#1295)
This gives us slightly better guarantees that the config has actually
been loaded. This, along with a FCAP bump, fixes this config option
not doing anything on Fabric.
2023-01-07 22:16:06 +00:00
Jonathan Coates
4360458416 Attempt to reduce test flakiness
I'm really not sure why the modem one fails. I can't reproduce outside
of CI, so quite hard to debug :/.
2023-01-07 15:05:21 +00:00
Jonathan Coates
717e096b94 Include the licences of our dependencies in the credits
I feel like we should have been doing this from the beginning. Love to
uncompliant for 11 years :/.
2023-01-06 09:34:07 +00:00
Jonathan Coates
34a31abd9c Move our internal module into the main package path
I originally put cc.import in a separate directory from the main
modules. This means that programs must extend the package path in order
to import these modules.

However, this ends up being a mixed blessing: while it makes it much
harder for users to accidentally require user code, it also means we
can't expose a public interface which wraps a private module.

Instead, cc.import now lives on the main package path, but lives under
the cc.internal namespace and is not documented anywhere. Hopefully this
should be enough of a clue that one shouldn't use it :p.
2023-01-05 21:55:08 +00:00
Jonathan Coates
bdecb88cca Support resource conditions in upgrade JSON 2023-01-02 15:56:01 +00:00
Weblate
af15030fa4 Translations for Italian
Co-authored-by: Trenord <luca.ilari@gmail.com>
2023-01-01 20:55:45 +00:00
Jonathan Coates
3a883db49e Test that wired modems form networks
See #1278
2023-01-01 17:57:53 +00:00
Jonathan Coates
8ea5b64f64 Improve our CI artifacts
- Publish Forge and Fabric
 - Include version hash in the download
2023-01-01 15:04:25 +00:00
Jonathan Coates
7b6caf76e4 Increase the distance from which we drop items
I was having issues where dropped items would clip into blocks when
dropped, and then phase upwards through the turtle instead. This makes
things a little more consistent with dispenser behaviour.
2023-01-01 14:51:47 +00:00
Jonathan Coates
230c7ee904 Clamp speaker volume again
Looks like this was removed in b048b6666d.
2023-01-01 08:51:07 +00:00
Jonathan Coates
aa203802c6 Fix search tab showing up in itemGroups 2022-12-31 16:24:19 +00:00
Jonathan Coates
1259e29f21 Fix full-block wired modems not connecting
Closes #1278.
2022-12-29 22:30:09 +00:00
Jonathan Coates
77f62dac94 Fix mouse_up not being fired on Fabric 2022-12-29 22:16:36 +00:00
Jonathan Coates
7f34aff6bb Correctly handle double chests on Fabric
Closes #1279. The perils of ignoring the transfer API :(.
2022-12-29 21:48:59 +00:00
Jonathan Coates
3047e3cdf4 Simplify cable/modem block breaking code
Instead of taking control of the breaking logic in all cases, we now
only do so when we have both a cable and modem. This allows us to fall
back to default vanilla behaviour and so correctly drop the modem/cable
item.
2022-12-29 12:21:10 +00:00
Emma
7a83a403f0 Fix OOB when item insertion wraps around (#1277)
Co-authored-by: Jonathan Coates <git@squiddev.cc>
2022-12-29 11:26:25 +00:00
Jonathan Coates
a1d5c76d00 Test for moving into a rotated container
Similar to the test in #1277. The duplication here is a little
irritating, but don't think we can avoid that.
2022-12-29 11:11:26 +00:00
Emma
bcdfa7c5ff Fix bug where turtle drops inventory when moving (#1276) 2022-12-29 09:02:02 +00:00
Jonathan Coates
2c59b9122b Set location when creating a pocket modem
We now call getLevel() when attaching the peripheral, so need the
position to be available immediately. Fixes #1274.

I /think/ the entity should always be present, as peripherals are
only created on startup or when calling pocket.equipBack, both of which
require a player.[^1]

I suspect this was a little broken before (the level wouldn't be
available if a modem received a message before the position had
been set), but that would be much rarer.

I'm not 100% convinced about the thread-safety of this code (the writes
to level may not be immediately visible on other threads), so may need
to think about that.

[^1]: Note that when peripherals come to be /attached/ they may no
longer have a player (as there's a gap between turning a computer on and
it actually starting). However, the level/position will have been
initialised by then, so this isn't a problem.
2022-12-29 08:52:28 +00:00
Emma
d2c7b944ab Fix crash on Fabric when attempting to use a non-fuel item as fuel. (#1275)
Co-authored-by: Jonathan Coates <git@squiddev.cc>
2022-12-29 08:22:09 +00:00
198 changed files with 7283 additions and 947 deletions

View File

@@ -38,11 +38,17 @@ jobs:
# These are a little flaky on GH actions: its useful to run them, but don't break the build. # These are a little flaky on GH actions: its useful to run them, but don't break the build.
continue-on-error: true continue-on-error: true
- name: Prepare Jars
run: |
# Find the main jar and append the git hash onto it.
mkdir -p jars
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
- name: Upload Jar - name: Upload Jar
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: CC-Tweaked name: CC-Tweaked
path: projects/forge/build/libs path: ./jars
- name: Upload coverage - name: Upload coverage
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v3

View File

@@ -80,7 +80,7 @@ sourceSets.all {
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
check("NullAway", CheckSeverity.ERROR) check("NullAway", CheckSeverity.ERROR)
option("NullAway:AnnotatedPackages", listOf("dan200.computercraft").joinToString(",")) option("NullAway:AnnotatedPackages", listOf("dan200.computercraft", "net.fabricmc.fabric.api").joinToString(","))
option("NullAway:ExcludedFieldAnnotations", listOf("org.spongepowered.asm.mixin.Shadow").joinToString(",")) option("NullAway:ExcludedFieldAnnotations", listOf("org.spongepowered.asm.mixin.Shadow").joinToString(","))
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull") option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
option("NullAway:CheckOptionalEmptiness") option("NullAway:CheckOptionalEmptiness")

View File

@@ -34,7 +34,7 @@ abstract class CheckChangelog : DefaultTask() {
var ok = true var ok = true
// Check we're targetting the current version // Check we're targeting the current version
var whatsNew = whatsNew.get().asFile.readLines() var whatsNew = whatsNew.get().asFile.readLines()
if (whatsNew[0] != "New features in CC: Tweaked $version") { if (whatsNew[0] != "New features in CC: Tweaked $version") {
ok = false ok = false

View File

@@ -3,19 +3,20 @@ module: [kind=event] alarm
see: os.setAlarm To start an alarm. see: os.setAlarm To start an alarm.
--- ---
The @{timer} event is fired when an alarm started with @{os.setAlarm} completes. The @{alarm} event is fired when an alarm started with @{os.setAlarm} completes.
## Return Values ## Return Values
1. @{string}: The event name. 1. @{string}: The event name.
2. @{number}: The ID of the alarm that finished. 2. @{number}: The ID of the alarm that finished.
## Example ## Example
Starts a timer and then prints its ID: Starts a timer and then waits for it to complete.
```lua ```lua
local alarmID = os.setAlarm(os.time() + 0.05) local alarm_id = os.setAlarm(os.time() + 0.05)
local event, id local event, id
repeat repeat
event, id = os.pullEvent("alarm") event, id = os.pullEvent("alarm")
until id == alarmID until id == alarm_id
print("Alarm with ID " .. id .. " was fired") print("Alarm with ID " .. id .. " was fired")
``` ```

View File

@@ -3,7 +3,7 @@ module: [kind=event] char
see: key To listen to any key press. see: key To listen to any key press.
--- ---
The @{char} event is fired when a character is _typed_ on the keyboard. The @{char} event is fired when a character is typed on the keyboard.
The @{char} event is different to a key press. Sometimes multiple key presses may result in one character being The @{char} event is different to a key press. Sometimes multiple key presses may result in one character being
typed (for instance, on some European keyboards). Similarly, some keys (e.g. <kbd>Ctrl</kbd>) do not have any typed (for instance, on some European keyboards). Similarly, some keys (e.g. <kbd>Ctrl</kbd>) do not have any
@@ -16,9 +16,10 @@ corresponding character. The @{key} should be used if you want to listen to key
## Example ## Example
Prints each character the user presses: Prints each character the user presses:
```lua ```lua
while true do while true do
local event, character = os.pullEvent("char") local event, character = os.pullEvent("char")
print(character .. " was pressed.") print(character .. " was pressed.")
end end
``` ```

View File

@@ -6,7 +6,7 @@ The @{computer_command} event is fired when the `/computercraft queue` command i
## Return Values ## Return Values
1. @{string}: The event name. 1. @{string}: The event name.
... @{string}: The arguments passed to the command. 2. @{string}<abbr title="Variable number of arguments">&hellip;</abbr>: The arguments passed to the command.
## Example ## Example
Prints the contents of messages sent: Prints the contents of messages sent:

View File

@@ -5,9 +5,9 @@ since: 1.101.0
The @{file_transfer} event is queued when a user drags-and-drops a file on an open computer. The @{file_transfer} event is queued when a user drags-and-drops a file on an open computer.
This event contains a single argument, that in turn has a single method @{TransferredFiles.getFiles|getFiles}. This This event contains a single argument of type @{TransferredFiles}, which can be used to @{TransferredFiles.getFiles|get
returns the list of files that are being transferred. Each file is a @{fs.BinaryReadHandle|binary file handle} with an the files to be transferred}. Each file returned is a @{fs.BinaryReadHandle|binary file handle} with an additional
additional @{TransferredFile.getName|getName} method. @{TransferredFile.getName|getName} method.
## Return values ## Return values
1. @{string}: The event name 1. @{string}: The event name

View File

@@ -11,4 +11,4 @@ This event is normally handled inside @{http.checkURL}, but it can still be seen
1. @{string}: The event name. 1. @{string}: The event name.
2. @{string}: The URL requested to be checked. 2. @{string}: The URL requested to be checked.
3. @{boolean}: Whether the check succeeded. 3. @{boolean}: Whether the check succeeded.
4. @{string|nil}: If the check failed, a reason explaining why the check failed. 4. <span class="type">@{string}|@{nil}</span>: If the check failed, a reason explaining why the check failed.

View File

@@ -11,7 +11,8 @@ This event is normally handled inside @{http.get} and @{http.post}, but it can s
1. @{string}: The event name. 1. @{string}: The event name.
2. @{string}: The URL of the site requested. 2. @{string}: The URL of the site requested.
3. @{string}: An error describing the failure. 3. @{string}: An error describing the failure.
4. @{http.Response|nil}: A response handle if the connection succeeded, but the server's response indicated failure. 4. <span class="type">@{http.Response}|@{nil}</span>: A response handle if the connection succeeded, but the server's
response indicated failure.
## Example ## Example
Prints an error why the website cannot be contacted: Prints an error why the website cannot be contacted:

View File

@@ -10,7 +10,7 @@ This event is normally handled inside @{http.get} and @{http.post}, but it can s
## Return Values ## Return Values
1. @{string}: The event name. 1. @{string}: The event name.
2. @{string}: The URL of the site requested. 2. @{string}: The URL of the site requested.
3. @{http.Response}: The handle for the response text. 3. @{http.Response}: The successful HTTP response.
## Example ## Example
Prints the content of a website (this may fail if the request fails): Prints the content of a website (this may fail if the request fails):

View File

@@ -10,7 +10,7 @@ The @{modem_message} event is fired when a message is received on an open channe
3. @{number}: The channel that the message was sent on. 3. @{number}: The channel that the message was sent on.
4. @{number}: The reply channel set by the sender. 4. @{number}: The reply channel set by the sender.
5. @{any}: The message as sent by the sender. 5. @{any}: The message as sent by the sender.
6. @{number}: The distance between the sender and the receiver, in blocks. 6. <span class="type">@{number}|@{nil}</span>: The distance between the sender and the receiver in blocks, or @{nil} if the message was sent between dimensions.
## Example ## Example
Wraps a @{modem} peripheral, opens channel 0 for listening, and prints all received messages. Wraps a @{modem} peripheral, opens channel 0 for listening, and prints all received messages.
@@ -20,7 +20,9 @@ local modem = peripheral.find("modem") or error("No modem attached", 0)
modem.open(0) modem.open(0)
while true do while true do
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message") local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
print(("Message received on side %s on channel %d (reply to %d) from %f blocks away with message %s"):format(side, channel, replyChannel, distance, tostring(message))) print(("Message received on side %s on channel %d (reply to %d) from %f blocks away with message %s"):format(
side, channel, replyChannel, distance, tostring(message)
))
end end
``` ```

View File

@@ -6,10 +6,11 @@ The @{monitor_resize} event is fired when an adjacent or networked monitor's siz
## Return Values ## Return Values
1. @{string}: The event name. 1. @{string}: The event name.
2. @{string}: The side or network ID of the monitor that resized. 2. @{string}: The side or network ID of the monitor that was resized.
## Example ## Example
Prints a message when a monitor is resized: Prints a message when a monitor is resized:
```lua ```lua
while true do while true do
local event, side = os.pullEvent("monitor_resize") local event, side = os.pullEvent("monitor_resize")

View File

@@ -14,7 +14,7 @@ This event is usually handled by @{rednet.receive}, but it can also be pulled ma
1. @{string}: The event name. 1. @{string}: The event name.
2. @{number}: The ID of the sending computer. 2. @{number}: The ID of the sending computer.
3. @{any}: The message sent. 3. @{any}: The message sent.
4. @{string|nil}: The protocol of the message, if provided. 4. <span class="type">@{string}|@{nil}</span>: The protocol of the message, if provided.
## Example ## Example
Prints a message when one is sent: Prints a message when one is sent:

View File

@@ -4,6 +4,9 @@ module: [kind=event] redstone
The @{event!redstone} event is fired whenever any redstone inputs on the computer change. The @{event!redstone} event is fired whenever any redstone inputs on the computer change.
## Return values
1. @{string}: The event name.
## Example ## Example
Prints a message when a redstone input changes: Prints a message when a redstone input changes:
```lua ```lua

View File

@@ -10,7 +10,7 @@ The @{task_complete} event is fired when an asynchronous task completes. This is
2. @{number}: The ID of the task that completed. 2. @{number}: The ID of the task that completed.
3. @{boolean}: Whether the command succeeded. 3. @{boolean}: Whether the command succeeded.
4. @{string}: If the command failed, an error message explaining the failure. (This is not present if the command succeeded.) 4. @{string}: If the command failed, an error message explaining the failure. (This is not present if the command succeeded.)
...: Any parameters returned from the command. 5. <abbr title="Variable number of arguments">&hellip;</abbr>: Any parameters returned from the command.
## Example ## Example
Prints the results of an asynchronous command: Prints the results of an asynchronous command:

View File

@@ -9,8 +9,12 @@ The @{term_resize} event is fired when the main terminal is resized. For instanc
When this event fires, some parts of the terminal may have been moved or deleted. Simple terminal programs (those When this event fires, some parts of the terminal may have been moved or deleted. Simple terminal programs (those
not using @{term.setCursorPos}) can ignore this event, but more complex GUI programs should redraw the entire screen. not using @{term.setCursorPos}) can ignore this event, but more complex GUI programs should redraw the entire screen.
## Return values
1. @{string}: The event name.
## Example ## Example
Prints : Print a message each time the terminal is resized.
```lua ```lua
while true do while true do
os.pullEvent("term_resize") os.pullEvent("term_resize")

View File

@@ -8,6 +8,9 @@ This event is normally handled by @{os.pullEvent}, and will not be returned. How
@{terminate} will be sent even when a filter is provided to @{os.pullEventRaw}. When using @{os.pullEventRaw} with a filter, make sure to check that the event is not @{terminate}. @{terminate} will be sent even when a filter is provided to @{os.pullEventRaw}. When using @{os.pullEventRaw} with a filter, make sure to check that the event is not @{terminate}.
## Return values
1. @{string}: The event name.
## Example ## Example
Prints a message when Ctrl-T is held: Prints a message when Ctrl-T is held:
```lua ```lua

View File

@@ -10,12 +10,12 @@ The @{timer} event is fired when a timer started with @{os.startTimer} completes
2. @{number}: The ID of the timer that finished. 2. @{number}: The ID of the timer that finished.
## Example ## Example
Starts a timer and then prints its ID: Start and wait for a timer to finish.
```lua ```lua
local timerID = os.startTimer(2) local timer_id = os.startTimer(2)
local event, id local event, id
repeat repeat
event, id = os.pullEvent("timer") event, id = os.pullEvent("timer")
until id == timerID until id == timer_id
print("Timer with ID " .. id .. " was fired") print("Timer with ID " .. id .. " was fired")
``` ```

View File

@@ -4,6 +4,9 @@ module: [kind=event] turtle_inventory
The @{turtle_inventory} event is fired when a turtle's inventory is changed. The @{turtle_inventory} event is fired when a turtle's inventory is changed.
## Return values
1. @{string}: The event name.
## Example ## Example
Prints a message when the inventory is changed: Prints a message when the inventory is changed:
```lua ```lua

View File

@@ -25,7 +25,7 @@ single-player or multiplayer. Look for lines that look like this:
``` ```
On 1.95.0 and later, this will be a single entry with `host = "$private"`. On earlier versions, this will be a number of On 1.95.0 and later, this will be a single entry with `host = "$private"`. On earlier versions, this will be a number of
`[[http.rules]]` with various IP addresses. You will want to remove all of the `[[http.rules]]` entires that have `[[http.rules]]` with various IP addresses. You will want to remove all of the `[[http.rules]]` entries that have
`action = "deny"`. Then save the file and relaunch Minecraft (Server). `action = "deny"`. Then save the file and relaunch Minecraft (Server).
Here's what it should look like after removing: Here's what it should look like after removing:
@@ -54,7 +54,7 @@ like this:
```toml ```toml
#A list of wildcards for domains or IP ranges that cannot be accessed through the "http" API on Computers. #A list of wildcards for domains or IP ranges that cannot be accessed through the "http" API on Computers.
#If this is empty then all whitelisted domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com. #If this is empty then all whitelisted domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com.
#You can use domain names ("pastebin.com"), wilcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8"). #You can use domain names ("pastebin.com"), wildcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
blacklist = ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fd00::/8"] blacklist = ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fd00::/8"]
``` ```
@@ -65,7 +65,7 @@ Here's what it should look like after removing:
```toml ```toml
#A list of wildcards for domains or IP ranges that cannot be accessed through the "http" API on Computers. #A list of wildcards for domains or IP ranges that cannot be accessed through the "http" API on Computers.
#If this is empty then all whitelisted domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com. #If this is empty then all whitelisted domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com.
#You can use domain names ("pastebin.com"), wilcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8"). #You can use domain names ("pastebin.com"), wildcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
blacklist = [] blacklist = []
``` ```

View File

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

View File

@@ -4,7 +4,7 @@
# MC version is specified in gradle.properties, as we need that in settings.gradle. # MC version is specified in gradle.properties, as we need that in settings.gradle.
fabric-api = "0.68.1+1.19.3" fabric-api = "0.68.1+1.19.3"
fabric-loader = "0.14.11" fabric-loader = "0.14.11"
forge = "44.0.18" forge = "44.1.0"
forgeSpi = "6.0.0" forgeSpi = "6.0.0"
mixin = "0.8.5" mixin = "0.8.5"
parchment = "2022.11.27" parchment = "2022.11.27"
@@ -14,19 +14,19 @@ parchmentMc = "1.19.2"
asm = "9.3" asm = "9.3"
autoService = "1.0.1" autoService = "1.0.1"
checkerFramework = "3.12.0" checkerFramework = "3.12.0"
cobalt = "0.5.9" cobalt = "0.6.0"
fastutil = "8.5.9" fastutil = "8.5.9"
guava = "31.1-jre" guava = "31.1-jre"
jetbrainsAnnotations = "23.0.0" jetbrainsAnnotations = "23.0.0"
jsr305 = "3.0.2" jsr305 = "3.0.2"
kotlin = "1.7.10" kotlin = "1.8.0"
kotlin-coroutines = "1.6.0" kotlin-coroutines = "1.6.4"
netty = "4.1.82.Final" netty = "4.1.82.Final"
nightConfig = "3.6.5" nightConfig = "3.6.5"
slf4j = "1.7.36" slf4j = "1.7.36"
# Minecraft mods # Minecraft mods
forgeConfig = "5.0.3" forgeConfig = "5.0.4"
iris = "1.19.3-v1.4.6" iris = "1.19.3-v1.4.6"
jei = "11.3.0.262" jei = "11.3.0.262"
modmenu = "5.0.1" modmenu = "5.0.1"
@@ -42,7 +42,7 @@ jqwik = "1.7.0"
junit = "5.9.1" junit = "5.9.1"
# Build tools # Build tools
cctJavadoc = "1.5.3" cctJavadoc = "1.6.0"
checkstyle = "10.3.4" checkstyle = "10.3.4"
curseForgeGradle = "1.0.11" curseForgeGradle = "1.0.11"
errorProne-core = "2.14.0" errorProne-core = "2.14.0"
@@ -51,7 +51,7 @@ fabric-loom = "1.0-SNAPSHOT"
forgeGradle = "5.1.+" forgeGradle = "5.1.+"
githubRelease = "2.2.12" githubRelease = "2.2.12"
ideaExt = "1.1.6" ideaExt = "1.1.6"
illuaminate = "0.1.0-7-g2a5a89c" illuaminate = "0.1.0-13-g689d73d"
librarian = "1.+" librarian = "1.+"
minotaur = "2.+" minotaur = "2.+"
mixinGradle = "0.7.+" mixinGradle = "0.7.+"
@@ -73,8 +73,9 @@ forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
guava = { module = "com.google.guava:guava", version.ref = "guava" } guava = { module = "com.google.guava:guava", version.ref = "guava" }
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" } jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" } jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" } kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" } netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" } nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" }
nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" } nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" }

View File

@@ -8,7 +8,11 @@ package dan200.computercraft.api;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
/** /**
@@ -21,6 +25,15 @@ public class ComputerCraftTags {
public static final TagKey<Item> WIRED_MODEM = make("wired_modem"); public static final TagKey<Item> WIRED_MODEM = make("wired_modem");
public static final TagKey<Item> MONITOR = make("monitor"); public static final TagKey<Item> MONITOR = make("monitor");
/**
* Items which can be {@linkplain Item#use(Level, Player, InteractionHand) used} when calling
* {@code turtle.place()}.
* <p>
* This does not cover items who handle placing inside {@link Item#useOn(UseOnContext)}, as that is always
* called.
*/
public static final TagKey<Item> TURTLE_CAN_PLACE = make("turtle_can_place");
private static TagKey<Item> make(String name) { private static TagKey<Item> make(String name) {
return TagKey.create(Registries.ITEM, new ResourceLocation(ComputerCraftAPI.MOD_ID, name)); return TagKey.create(Registries.ITEM, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
} }

View File

@@ -32,6 +32,8 @@ public interface DetailRegistry<T> {
/** /**
* Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable * Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable
* for when you need to compute the details for a large number of values. * for when you need to compute the details for a large number of values.
* <p>
* This method <em>MAY</em> be thread safe: consult the instance's documentation for details.
* *
* @param object The object to get details for. * @param object The object to get details for.
* @return The basic details. * @return The basic details.
@@ -40,6 +42,8 @@ public interface DetailRegistry<T> {
/** /**
* Compute all details about an object, using {@link #getBasicDetails(Object)} and any registered providers. * Compute all details about an object, using {@link #getBasicDetails(Object)} and any registered providers.
* <p>
* This method is <em>NOT</em> thread safe. It should only be called from the computer thread.
* *
* @param object The object to get details for. * @param object The object to get details for.
* @return The computed details. * @return The computed details.

View File

@@ -15,12 +15,17 @@ import net.minecraft.world.level.block.Block;
public class VanillaDetailRegistries { public class VanillaDetailRegistries {
/** /**
* Provides details for {@link ItemStack}s. * Provides details for {@link ItemStack}s.
* <p>
* This instance's {@link DetailRegistry#getBasicDetails(Object)} is thread safe (assuming the stack is immutable)
* and may be called from the computer thread.
*/ */
public static final DetailRegistry<ItemStack> ITEM_STACK = ComputerCraftAPIService.get().getItemStackDetailRegistry(); public static final DetailRegistry<ItemStack> ITEM_STACK = ComputerCraftAPIService.get().getItemStackDetailRegistry();
/** /**
* Provides details for {@link BlockReference}, a reference to a {@link Block} in the world. * Provides details for {@link BlockReference}, a reference to a {@link Block} in the world.
* <p>
* This instance's {@link DetailRegistry#getBasicDetails(Object)} is thread safe and may be called from the computer
* thread.
*/ */
public static final DetailRegistry<BlockReference> BLOCK_IN_WORLD = ComputerCraftAPIService.get().getBlockInWorldDetailRegistry(); public static final DetailRegistry<BlockReference> BLOCK_IN_WORLD = ComputerCraftAPIService.get().getBlockInWorldDetailRegistry();
} }

View File

@@ -15,6 +15,7 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.Container; import net.minecraft.world.Container;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -24,6 +25,7 @@ import javax.annotation.Nullable;
* This should not be implemented by your classes. Do not interact with turtles except via this interface and * This should not be implemented by your classes. Do not interact with turtles except via this interface and
* {@link ITurtleUpgrade}. * {@link ITurtleUpgrade}.
*/ */
@ApiStatus.NonExtendable
public interface ITurtleAccess { public interface ITurtleAccess {
/** /**
* Returns the world in which the turtle resides. * Returns the world in which the turtle resides.

View File

@@ -98,7 +98,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
* get the final damage. * get the final damage.
* *
* @param damageMultiplier The damage multiplier. * @param damageMultiplier The damage multiplier.
* @return The tool builder, for futher use. * @return The tool builder, for further use.
*/ */
public ToolBuilder damageMultiplier(float damageMultiplier) { public ToolBuilder damageMultiplier(float damageMultiplier) {
this.damageMultiplier = damageMultiplier; this.damageMultiplier = damageMultiplier;

View File

@@ -144,7 +144,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
} }
public List<T> getGeneratedUpgrades() { public List<T> getGeneratedUpgrades() {
if (upgrades == null) throw new IllegalStateException("Upgrades have not beeen generated yet"); if (upgrades == null) throw new IllegalStateException("Upgrades have not been generated yet");
return upgrades; return upgrades;
} }

View File

@@ -105,6 +105,16 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
return super.keyPressed(key, scancode, modifiers); return super.keyPressed(key, scancode, modifiers);
} }
@Override
public boolean mouseReleased(double x, double y, int button) {
// Reimplement ContainerEventHandler.mouseReleased, as it's not called in vanilla (it is in Forge, but that
// shouldn't matter).
setDragging(false);
var child = getChildAt(x, y);
if (child.isPresent() && child.get().mouseReleased(x, y, button)) return true;
return super.mouseReleased(x, y, button);
}
@Override @Override
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) { public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {

View File

@@ -30,6 +30,7 @@ public class TerminalWidget extends AbstractWidget {
private static final Component DESCRIPTION = Component.translatable("gui.computercraft.terminal"); private static final Component DESCRIPTION = Component.translatable("gui.computercraft.terminal");
private static final float TERMINATE_TIME = 0.5f; private static final float TERMINATE_TIME = 0.5f;
private static final float KEY_SUPPRESS_DELAY = 0.2f;
private final Terminal terminal; private final Terminal terminal;
private final InputHandler computer; private final InputHandler computer;
@@ -79,15 +80,12 @@ public class TerminalWidget extends AbstractWidget {
switch (key) { switch (key) {
case GLFW.GLFW_KEY_T -> { case GLFW.GLFW_KEY_T -> {
if (terminateTimer < 0) terminateTimer = 0; if (terminateTimer < 0) terminateTimer = 0;
return true;
} }
case GLFW.GLFW_KEY_S -> { case GLFW.GLFW_KEY_S -> {
if (shutdownTimer < 0) shutdownTimer = 0; if (shutdownTimer < 0) shutdownTimer = 0;
return true;
} }
case GLFW.GLFW_KEY_R -> { case GLFW.GLFW_KEY_R -> {
if (rebootTimer < 0) rebootTimer = 0; if (rebootTimer < 0) rebootTimer = 0;
return true;
} }
case GLFW.GLFW_KEY_V -> { case GLFW.GLFW_KEY_V -> {
// Ctrl+V for paste // Ctrl+V for paste
@@ -118,7 +116,7 @@ public class TerminalWidget extends AbstractWidget {
} }
} }
if (key >= 0 && terminateTimer < 0 && rebootTimer < 0 && shutdownTimer < 0) { if (key >= 0 && terminateTimer < KEY_SUPPRESS_DELAY && rebootTimer < KEY_SUPPRESS_DELAY && shutdownTimer < KEY_SUPPRESS_DELAY) {
// Queue the "key" event and add to the down set // Queue the "key" event and add to the down set
var repeat = keysDown.get(key); var repeat = keysDown.get(key);
keysDown.set(key); keysDown.set(key);
@@ -184,7 +182,7 @@ public class TerminalWidget extends AbstractWidget {
lastMouseX = charX; lastMouseX = charX;
lastMouseY = charY; lastMouseY = charY;
return false; return true;
} }
@Override @Override
@@ -203,7 +201,7 @@ public class TerminalWidget extends AbstractWidget {
lastMouseY = charY; lastMouseY = charY;
} }
return false; return true;
} }
@Override @Override

View File

@@ -15,7 +15,6 @@ import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
import dan200.computercraft.client.turtle.TurtleUpgradeModellers; import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
import dan200.computercraft.shared.turtle.items.TurtleItem; import dan200.computercraft.shared.turtle.items.TurtleItem;
import dan200.computercraft.shared.util.Holiday; import dan200.computercraft.shared.util.Holiday;
import dan200.computercraft.shared.util.HolidayUtil;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@@ -71,13 +70,16 @@ public final class TurtleModelParts {
} }
public Combination getCombination(ItemStack stack) { public Combination getCombination(ItemStack stack) {
var turtle = (TurtleItem) stack.getItem(); var christmas = Holiday.getCurrent() == Holiday.CHRISTMAS;
if (!(stack.getItem() instanceof TurtleItem turtle)) {
return new Combination(false, null, null, null, christmas, false);
}
var colour = turtle.getColour(stack); var colour = turtle.getColour(stack);
var leftUpgrade = turtle.getUpgrade(stack, TurtleSide.LEFT); var leftUpgrade = turtle.getUpgrade(stack, TurtleSide.LEFT);
var rightUpgrade = turtle.getUpgrade(stack, TurtleSide.RIGHT); var rightUpgrade = turtle.getUpgrade(stack, TurtleSide.RIGHT);
var overlay = turtle.getOverlay(stack); var overlay = turtle.getOverlay(stack);
var christmas = HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS;
var label = turtle.getLabel(stack); var label = turtle.getLabel(stack);
var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm")); var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));

View File

@@ -17,7 +17,6 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity; import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.Holiday; import dan200.computercraft.shared.util.Holiday;
import dan200.computercraft.shared.util.HolidayUtil;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font; import net.minecraft.client.gui.Font;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
@@ -112,7 +111,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
renderModel(transform, buffer, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour }); renderModel(transform, buffer, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour });
// Render the overlay // Render the overlay
var overlayModel = getTurtleOverlayModel(overlay, HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS); var overlayModel = getTurtleOverlayModel(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS);
if (overlayModel != null) { if (overlayModel != null) {
renderModel(transform, buffer, lightmapCoord, overlayLight, overlayModel, null); renderModel(transform, buffer, lightmapCoord, overlayLight, overlayModel, null);
} }

View File

@@ -65,6 +65,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
addSpecial(add, ModRegistry.RecipeSerializers.PRINTOUT.get()); addSpecial(add, ModRegistry.RecipeSerializers.PRINTOUT.get());
addSpecial(add, ModRegistry.RecipeSerializers.DISK.get()); addSpecial(add, ModRegistry.RecipeSerializers.DISK.get());
addSpecial(add, ModRegistry.RecipeSerializers.DYEABLE_ITEM.get()); addSpecial(add, ModRegistry.RecipeSerializers.DYEABLE_ITEM.get());
addSpecial(add, ModRegistry.RecipeSerializers.DYEABLE_ITEM_CLEAR.get());
addSpecial(add, ModRegistry.RecipeSerializers.TURTLE_UPGRADE.get()); addSpecial(add, ModRegistry.RecipeSerializers.TURTLE_UPGRADE.get());
addSpecial(add, ModRegistry.RecipeSerializers.POCKET_COMPUTER_UPGRADE.get()); addSpecial(add, ModRegistry.RecipeSerializers.POCKET_COMPUTER_UPGRADE.get());
} }

View File

@@ -15,6 +15,7 @@ import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagBuilder; import net.minecraft.tags.TagBuilder;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
@@ -85,6 +86,10 @@ class TagProvider {
ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(),
ModRegistry.Items.MONITOR_ADVANCED.get() ModRegistry.Items.MONITOR_ADVANCED.get()
); );
tags.tag(ComputerCraftTags.Items.TURTLE_CAN_PLACE)
.add(Items.GLASS_BOTTLE)
.addTag(ItemTags.BOATS);
} }
/** /**

View File

@@ -114,8 +114,9 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
private void loadUpgrade(Map<String, UpgradeWrapper<R, T>> current, ResourceLocation id, JsonElement json) { private void loadUpgrade(Map<String, UpgradeWrapper<R, T>> current, ResourceLocation id, JsonElement json) {
var root = GsonHelper.convertToJsonObject(json, "top element"); var root = GsonHelper.convertToJsonObject(json, "top element");
var serialiserId = new ResourceLocation(GsonHelper.getAsString(root, "type")); if (!PlatformHelper.get().shouldLoadResource(root)) return;
var serialiserId = new ResourceLocation(GsonHelper.getAsString(root, "type"));
var serialiser = PlatformHelper.get().tryGetRegistryObject(registry, serialiserId); var serialiser = PlatformHelper.get().tryGetRegistryObject(registry, serialiserId);
if (serialiser == null) throw new JsonSyntaxException("Unknown upgrade type '" + serialiserId + "'"); if (serialiser == null) throw new JsonSyntaxException("Unknown upgrade type '" + serialiserId + "'");

View File

@@ -19,6 +19,7 @@ import dan200.computercraft.shared.command.arguments.ComputerArgumentType;
import dan200.computercraft.shared.command.arguments.ComputersArgumentType; import dan200.computercraft.shared.command.arguments.ComputersArgumentType;
import dan200.computercraft.shared.command.arguments.RepeatArgumentType; import dan200.computercraft.shared.command.arguments.RepeatArgumentType;
import dan200.computercraft.shared.command.arguments.TrackingFieldArgumentType; import dan200.computercraft.shared.command.arguments.TrackingFieldArgumentType;
import dan200.computercraft.shared.common.ClearColourRecipe;
import dan200.computercraft.shared.common.ColourableRecipe; import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider; import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
import dan200.computercraft.shared.common.HeldItemMenu; import dan200.computercraft.shared.common.HeldItemMenu;
@@ -351,6 +352,7 @@ public final class ModRegistry {
} }
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ColourableRecipe>> DYEABLE_ITEM = simple("colour", ColourableRecipe::new); public static final RegistryEntry<SimpleCraftingRecipeSerializer<ColourableRecipe>> DYEABLE_ITEM = simple("colour", ColourableRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ClearColourRecipe>> DYEABLE_ITEM_CLEAR = simple("clear_colour", ClearColourRecipe::new);
public static final RegistryEntry<TurtleRecipe.Serializer> TURTLE = REGISTRY.register("turtle", TurtleRecipe.Serializer::new); public static final RegistryEntry<TurtleRecipe.Serializer> TURTLE = REGISTRY.register("turtle", TurtleRecipe.Serializer::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new); public static final RegistryEntry<SimpleCraftingRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new); public static final RegistryEntry<SimpleCraftingRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new);

View File

@@ -0,0 +1,84 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.common;
import dan200.computercraft.shared.ModRegistry;
import net.minecraft.core.NonNullList;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.Level;
/**
* Craft a wet sponge with a {@linkplain IColouredItem dyable item} to remove its dye.
*/
public final class ClearColourRecipe extends CustomRecipe {
public ClearColourRecipe(ResourceLocation id, CraftingBookCategory category) {
super(id, category);
}
@Override
public boolean matches(CraftingContainer inv, Level world) {
var hasColourable = false;
var hasSponge = false;
for (var i = 0; i < inv.getContainerSize(); i++) {
var stack = inv.getItem(i);
if (stack.isEmpty()) continue;
if (stack.getItem() instanceof IColouredItem colourable) {
if (hasColourable) return false;
if (colourable.getColour(stack) == -1) return false;
hasColourable = true;
} else if (stack.getItem() == Items.WET_SPONGE) {
if (hasSponge) return false;
hasSponge = true;
} else {
return false;
}
}
return hasColourable && hasSponge;
}
@Override
public ItemStack assemble(CraftingContainer inv) {
var colourable = ItemStack.EMPTY;
for (var i = 0; i < inv.getContainerSize(); i++) {
var stack = inv.getItem(i);
if (stack.getItem() instanceof IColouredItem) colourable = stack;
}
if (colourable.isEmpty()) return ItemStack.EMPTY;
var stack = ((IColouredItem) colourable.getItem()).withColour(colourable, -1);
stack.setCount(1);
return stack;
}
@Override
public NonNullList<ItemStack> getRemainingItems(CraftingContainer container) {
var remaining = NonNullList.withSize(container.getContainerSize(), ItemStack.EMPTY);
for (var i = 0; i < remaining.size(); i++) {
if (container.getItem(i).getItem() == Items.WET_SPONGE) remaining.set(i, new ItemStack(Items.WET_SPONGE));
}
return remaining;
}
@Override
public boolean canCraftInDimensions(int x, int y) {
return x * y >= 2;
}
@Override
public RecipeSerializer<ClearColourRecipe> getSerializer() {
return ModRegistry.RecipeSerializers.DYEABLE_ITEM_CLEAR.get();
}
}

View File

@@ -74,7 +74,7 @@ public final class ColourableRecipe extends CustomRecipe {
} }
@Override @Override
public RecipeSerializer<?> getSerializer() { public RecipeSerializer<ColourableRecipe> getSerializer() {
return ModRegistry.RecipeSerializers.DYEABLE_ITEM.get(); return ModRegistry.RecipeSerializers.DYEABLE_ITEM.get();
} }
} }

View File

@@ -18,7 +18,7 @@ import dan200.computercraft.core.lua.ILuaMachine;
import dan200.computercraft.impl.AbstractComputerCraftAPI; import dan200.computercraft.impl.AbstractComputerCraftAPI;
import dan200.computercraft.shared.CommonHooks; import dan200.computercraft.shared.CommonHooks;
import dan200.computercraft.shared.computer.metrics.GlobalMetrics; import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import dan200.computercraft.shared.util.IDAssigner; import dan200.computercraft.shared.util.IDAssigner;
import net.minecraft.SharedConstants; import net.minecraft.SharedConstants;
@@ -69,7 +69,7 @@ public final class ServerContext {
mainThread = new MainThread(); mainThread = new MainThread();
context = new ComputerContext( context = new ComputerContext(
new Environment(server), new Environment(server),
new ComputerThread(Config.computerThreads), new ComputerThread(ConfigSpec.computerThreads.get()),
mainThread, luaMachine mainThread, luaMachine
); );
idAssigner = new IDAssigner(storageDir.resolve("ids.json")); idAssigner = new IDAssigner(storageDir.resolve("ids.json"));

View File

@@ -17,8 +17,6 @@ public final class Config {
public static int floppySpaceLimit = 125 * 1000; public static int floppySpaceLimit = 125 * 1000;
public static boolean commandRequireCreative = true; public static boolean commandRequireCreative = true;
public static int computerThreads = 1;
public static boolean enableCommandBlock = false; public static boolean enableCommandBlock = false;
public static int modemRange = 64; public static int modemRange = 64;
public static int modemHighAltitudeRange = 384; public static int modemHighAltitudeRange = 384;

View File

@@ -142,7 +142,7 @@ public final class ConfigSpec {
computers can run at once, but may induce lag. Please note that some mods may computers can run at once, but may induce lag. Please note that some mods may
not work with a thread count higher than 1. Use with caution.""") not work with a thread count higher than 1. Use with caution.""")
.worldRestart() .worldRestart()
.defineInRange("computer_threads", Config.computerThreads, 1, Integer.MAX_VALUE); .defineInRange("computer_threads", 1, 1, Integer.MAX_VALUE);
maxMainGlobalTime = builder maxMainGlobalTime = builder
.comment(""" .comment("""
@@ -342,11 +342,9 @@ public final class ConfigSpec {
CoreConfig.maximumFilesOpen = maximumFilesOpen.get(); CoreConfig.maximumFilesOpen = maximumFilesOpen.get();
CoreConfig.disableLua51Features = disableLua51Features.get(); CoreConfig.disableLua51Features = disableLua51Features.get();
CoreConfig.defaultComputerSettings = defaultComputerSettings.get(); CoreConfig.defaultComputerSettings = defaultComputerSettings.get();
Config.computerThreads = computerThreads.get();
Config.commandRequireCreative = commandRequireCreative.get(); Config.commandRequireCreative = commandRequireCreative.get();
// Execution // Execution
Config.computerThreads = computerThreads.get();
CoreConfig.maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos(maxMainGlobalTime.get()); CoreConfig.maxMainGlobalTime = TimeUnit.MILLISECONDS.toNanos(maxMainGlobalTime.get());
CoreConfig.maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos(maxMainComputerTime.get()); CoreConfig.maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos(maxMainComputerTime.get());

View File

@@ -12,6 +12,7 @@ import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs; import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.EnchantedBookItem; import net.minecraft.world.item.EnchantedBookItem;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@@ -24,14 +25,9 @@ import java.util.*;
* Data providers for items. * Data providers for items.
*/ */
public class ItemDetails { public class ItemDetails {
public static <T extends Map<? super String, Object>> T fillBasicSafe(T data, ItemStack stack) { public static void fillBasic(Map<? super String, Object> data, ItemStack stack) {
data.put("name", DetailHelpers.getId(RegistryWrappers.ITEMS, stack.getItem())); data.put("name", DetailHelpers.getId(RegistryWrappers.ITEMS, stack.getItem()));
data.put("count", stack.getCount()); data.put("count", stack.getCount());
return data;
}
public static void fillBasic(Map<? super String, Object> data, ItemStack stack) {
fillBasicSafe(data, stack);
var hash = NBTUtil.getNBTHash(stack.getTag()); var hash = NBTUtil.getNBTHash(stack.getTag());
if (hash != null) data.put("nbt", hash); if (hash != null) data.put("nbt", hash);
} }
@@ -96,19 +92,18 @@ public class ItemDetails {
* @return A filled list that contains pairs of item group IDs and their display names. * @return A filled list that contains pairs of item group IDs and their display names.
*/ */
private static List<Map<String, Object>> getItemGroups(ItemStack stack) { private static List<Map<String, Object>> getItemGroups(ItemStack stack) {
List<Map<String, Object>> groups = new ArrayList<>(1); return CreativeModeTabs.allTabs().stream()
.filter(x -> x.shouldDisplay() && x.getType() == CreativeModeTab.Type.CATEGORY && x.contains(stack))
.map(group -> {
Map<String, Object> groupData = new HashMap<>(2);
CreativeModeTabs.tabs().stream().filter(x -> x.contains(stack)).forEach(group -> { var id = PlatformHelper.get().getCreativeTabId(group);
Map<String, Object> groupData = new HashMap<>(2); if (id != null) groupData.put("id", id.toString());
var id = PlatformHelper.get().getCreativeTabId(group); groupData.put("displayName", group.getDisplayName().getString());
if (id != null) groupData.put("id", id.toString()); return groupData;
})
groupData.put("displayName", group.getDisplayName().getString()); .toList();
groups.add(groupData);
});
return groups;
} }
/** /**

View File

@@ -82,10 +82,10 @@ public class UpgradesLoadedMessage implements NetworkMessage<ClientNetworkContex
var serialiser = entry.getValue().serialiser(); var serialiser = entry.getValue().serialiser();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
var unwrapedSerialiser = (UpgradeSerialiser<T>) serialiser; var unwrappedSerialiser = (UpgradeSerialiser<T>) serialiser;
buf.writeResourceLocation(Objects.requireNonNull(registry.getKey(serialiser), "Serialiser is not registered!")); buf.writeResourceLocation(Objects.requireNonNull(registry.getKey(serialiser), "Serialiser is not registered!"));
unwrapedSerialiser.toNetwork(buf, entry.getValue().upgrade()); unwrappedSerialiser.toNetwork(buf, entry.getValue().upgrade());
buf.writeUtf(entry.getValue().modId()); buf.writeUtf(entry.getValue().modId());
} }

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.shared.peripheral.diskdrive; package dan200.computercraft.shared.peripheral.diskdrive;
import com.google.errorprone.annotations.concurrent.GuardedBy; import com.google.errorprone.annotations.concurrent.GuardedBy;
import dan200.computercraft.api.filesystem.Mount;
import dan200.computercraft.api.filesystem.WritableMount; import dan200.computercraft.api.filesystem.WritableMount;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
@@ -47,11 +48,14 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
private final NonNullList<ItemStack> inventory = NonNullList.withSize(1, ItemStack.EMPTY); private final NonNullList<ItemStack> inventory = NonNullList.withSize(1, ItemStack.EMPTY);
private MediaStack media = MediaStack.EMPTY; private MediaStack media = MediaStack.EMPTY;
private @Nullable Mount mount;
private boolean recordPlaying = false; private boolean recordPlaying = false;
// In order to avoid main-thread calls in the peripheral, we set flags to mark which operation should be performed, // In order to avoid main-thread calls in the peripheral, we set flags to mark which operation should be performed,
// then read them when ticking. // then read them when ticking.
private final AtomicReference<RecordCommand> recordQueued = new AtomicReference<>(null); private final AtomicReference<RecordCommand> recordQueued = new AtomicReference<>(null);
private final AtomicBoolean ejectQueued = new AtomicBoolean(false); private final AtomicBoolean ejectQueued = new AtomicBoolean(false);
private final AtomicBoolean mountQueued = new AtomicBoolean(false);
public DiskDriveBlockEntity(BlockEntityType<DiskDriveBlockEntity> type, BlockPos pos, BlockState state) { public DiskDriveBlockEntity(BlockEntityType<DiskDriveBlockEntity> type, BlockPos pos, BlockState state) {
super(type, pos, state); super(type, pos, state);
@@ -109,6 +113,12 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
} }
} }
} }
if (mountQueued.get()) {
synchronized (this) {
mountAll();
}
}
} }
@Override @Override
@@ -124,9 +134,9 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
private void updateItem() { private void updateItem() {
var newDisk = getDiskStack(); var newDisk = getDiskStack();
if (ItemStack.isSame(newDisk, media.stack)) return; if (ItemStack.isSameItemSameTags(newDisk, media.stack)) return;
var media = new MediaStack(newDisk.copy()); var media = MediaStack.of(newDisk);
if (newDisk.isEmpty()) { if (newDisk.isEmpty()) {
updateBlockState(DiskDriveState.EMPTY); updateBlockState(DiskDriveState.EMPTY);
@@ -146,12 +156,10 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
recordPlaying = false; recordPlaying = false;
} }
mount = null;
this.media = media; this.media = media;
// Mount new disk mountAll();
if (!this.media.stack.isEmpty()) {
for (var computer : computers.entrySet()) mountDisk(computer.getKey(), computer.getValue(), this.media);
}
} }
} }
@@ -163,11 +171,30 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
return media; return media;
} }
/**
* Set the current disk stack, mounting/unmounting if needed.
*
* @param stack The new disk stack.
*/
void setDiskStack(ItemStack stack) { void setDiskStack(ItemStack stack) {
setItem(0, stack); setItem(0, stack);
setChanged(); setChanged();
} }
/**
* Update the current disk stack, assuming the underlying item does not change. Unlike
* {@link #setDiskStack(ItemStack)} this will not change any mounts.
*
* @param stack The new disk stack.
*/
void updateDiskStack(ItemStack stack) {
setItem(0, stack);
if (!ItemStack.isSameItemSameTags(stack, media.stack)) {
media = MediaStack.of(stack);
super.setChanged();
}
}
@Nullable @Nullable
String getDiskMountPath(IComputerAccess computer) { String getDiskMountPath(IComputerAccess computer) {
synchronized (this) { synchronized (this) {
@@ -176,15 +203,21 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
} }
} }
void mount(IComputerAccess computer) { /**
* Attach a computer to this disk drive. This sets up the {@link MountInfo} map and flags us to mount next tick. We
* don't mount here, as that might require mutating the current stack.
*
* @param computer The computer to attach.
*/
void attach(IComputerAccess computer) {
synchronized (this) { synchronized (this) {
var info = new MountInfo(); var info = new MountInfo();
computers.put(computer, info); computers.put(computer, info);
mountDisk(computer, info, media); mountQueued.set(true);
} }
} }
void unmount(IComputerAccess computer) { void detach(IComputerAccess computer) {
synchronized (this) { synchronized (this) {
unmountDisk(computer, computers.remove(computer)); unmountDisk(computer, computers.remove(computer));
} }
@@ -202,10 +235,35 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
ejectQueued.set(true); ejectQueued.set(true);
} }
/**
* Add our mount to all computers.
*/
@GuardedBy("this") @GuardedBy("this")
private void mountDisk(IComputerAccess computer, MountInfo info, MediaStack disk) { private void mountAll() {
var mount = disk.getMount((ServerLevel) getLevel()); doMountAll();
if (mount != null) { mountQueued.set(false);
}
/**
* The worker for {@link #mountAll()}. This is responsible for creating the mount and placing it on all computers.
*/
@GuardedBy("this")
private void doMountAll() {
if (computers.isEmpty() || media.media == null) return;
if (mount == null) {
var stack = getDiskStack();
mount = media.media.createDataMount(stack, (ServerLevel) level);
setDiskStack(stack);
}
if (mount == null) return;
for (var entry : computers.entrySet()) {
var computer = entry.getKey();
var info = entry.getValue();
if (info.mountPath != null) continue;
if (mount instanceof WritableMount writable) { if (mount instanceof WritableMount writable) {
// Try mounting at the lowest numbered "disk" name we can // Try mounting at the lowest numbered "disk" name we can
var n = 1; var n = 1;
@@ -221,11 +279,9 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
n++; n++;
} }
} }
} else {
info.mountPath = null;
}
computer.queueEvent("disk", computer.getAttachmentName()); computer.queueEvent("disk", computer.getAttachmentName());
}
} }
private static void unmountDisk(IComputerAccess computer, MountInfo info) { private static void unmountDisk(IComputerAccess computer, MountInfo info) {
@@ -251,7 +307,7 @@ public final class DiskDriveBlockEntity extends AbstractContainerBlockEntity {
if (stack.isEmpty()) return; if (stack.isEmpty()) return;
setDiskStack(ItemStack.EMPTY); setDiskStack(ItemStack.EMPTY);
WorldUtil.dropItemStack(stack, getLevel(), getBlockPos(), getDirection()); WorldUtil.dropItemStack(getLevel(), getBlockPos(), getDirection(), stack);
getLevel().levelEvent(LevelEvent.SOUND_DISPENSER_DISPENSE, getBlockPos(), 0); getLevel().levelEvent(LevelEvent.SOUND_DISPENSER_DISPENSE, getBlockPos(), 0);
} }

View File

@@ -84,11 +84,12 @@ public class DiskDrivePeripheral implements IPeripheral {
var media = diskDrive.getMedia(); var media = diskDrive.getMedia();
if (media.media == null) return; if (media.media == null) return;
var stack = media.stack.copy(); // We're on the main thread so the stack and media should be in sync.
var stack = diskDrive.getDiskStack();
if (!media.media.setLabel(stack, label.map(StringUtil::normaliseLabel).orElse(null))) { if (!media.media.setLabel(stack, label.map(StringUtil::normaliseLabel).orElse(null))) {
throw new LuaException("Disk label cannot be changed"); throw new LuaException("Disk label cannot be changed");
} }
diskDrive.setDiskStack(stack); diskDrive.updateDiskStack(stack);
} }
/** /**
@@ -178,12 +179,12 @@ public class DiskDrivePeripheral implements IPeripheral {
@Override @Override
public void attach(IComputerAccess computer) { public void attach(IComputerAccess computer) {
diskDrive.mount(computer); diskDrive.attach(computer);
} }
@Override @Override
public void detach(IComputerAccess computer) { public void detach(IComputerAccess computer) {
diskDrive.unmount(computer); diskDrive.detach(computer);
} }
@Override @Override

View File

@@ -5,10 +5,8 @@
*/ */
package dan200.computercraft.shared.peripheral.diskdrive; package dan200.computercraft.shared.peripheral.diskdrive;
import dan200.computercraft.api.filesystem.Mount;
import dan200.computercraft.api.media.IMedia; import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.impl.MediaProviders; import dan200.computercraft.impl.MediaProviders;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@@ -17,18 +15,22 @@ import javax.annotation.Nullable;
/** /**
* An immutable snapshot of the current disk. This allows us to read the stack in a thread-safe manner. * An immutable snapshot of the current disk. This allows us to read the stack in a thread-safe manner.
*/ */
class MediaStack { final class MediaStack {
static final MediaStack EMPTY = new MediaStack(ItemStack.EMPTY); static final MediaStack EMPTY = new MediaStack(ItemStack.EMPTY, null);
final ItemStack stack; final ItemStack stack;
final @Nullable IMedia media; final @Nullable IMedia media;
@Nullable private MediaStack(ItemStack stack, @Nullable IMedia media) {
private Mount mount;
MediaStack(ItemStack stack) {
this.stack = stack; this.stack = stack;
media = MediaProviders.get(stack); this.media = media;
}
public static MediaStack of(ItemStack stack) {
if (stack.isEmpty()) return EMPTY;
var freshStack = stack.copy();
return new MediaStack(freshStack, MediaProviders.get(freshStack));
} }
@Nullable @Nullable
@@ -40,12 +42,4 @@ class MediaStack {
String getAudioTitle() { String getAudioTitle() {
return media != null ? media.getAudioTitle(stack) : null; return media != null ? media.getAudioTitle(stack) : null;
} }
@Nullable
public Mount getMount(ServerLevel level) {
if (media == null) return null;
if (mount == null) mount = media.createDataMount(stack, level);
return mount;
}
} }

View File

@@ -52,6 +52,15 @@ import java.util.Set;
* <li><strong>Wired modems:</strong> These send messages to other any other wired modems connected to the same network * <li><strong>Wired modems:</strong> These send messages to other any other wired modems connected to the same network
* (using <em>Networking Cable</em>). They also can be used to attach additional peripherals to a computer.</li></ul> * (using <em>Networking Cable</em>). They also can be used to attach additional peripherals to a computer.</li></ul>
* *
* ## Recipes
* <div class="recipe-container">
* <mc-recipe recipe="computercraft:wireless_modem_normal"></mc-recipe>
* <mc-recipe recipe="computercraft:wireless_modem_advanced"></mc-recipe>
* <mc-recipe recipe="computercraft:wired_modem"></mc-recipe>
* <mc-recipe recipe="computercraft:cable"></mc-recipe>
* <mc-recipe recipe="computercraft:wired_modem_full_from"></mc-recipe>
* </div>
*
* @cc.module modem * @cc.module modem
* @cc.see modem_message Queued when a modem receives a message on an {@link #open(int) open channel}. * @cc.see modem_message Queued when a modem receives a message on an {@link #open(int) open channel}.
* @cc.see rednet A networking API built on top of the modem peripheral. * @cc.see rednet A networking API built on top of the modem peripheral.
@@ -74,14 +83,6 @@ import java.util.Set;
* print("Received a reply: " .. tostring(message)) * print("Received a reply: " .. tostring(message))
* }</pre> * }</pre>
* <p> * <p>
* ## Recipes
* <div class="recipe-container">
* <mc-recipe recipe="computercraft:wireless_modem_normal"></mc-recipe>
* <mc-recipe recipe="computercraft:wireless_modem_advanced"></mc-recipe>
* <mc-recipe recipe="computercraft:wired_modem"></mc-recipe>
* <mc-recipe recipe="computercraft:cable"></mc-recipe>
* <mc-recipe recipe="computercraft:wired_modem_full_from"></mc-recipe>
* </div>
*/ */
public abstract class ModemPeripheral implements IPeripheral, PacketSender, PacketReceiver { public abstract class ModemPeripheral implements IPeripheral, PacketSender, PacketReceiver {
private @Nullable PacketNetwork network; private @Nullable PacketNetwork network;

View File

@@ -97,45 +97,46 @@ public class CableBlock extends Block implements SimpleWaterloggedBlock, EntityB
@ForgeOverride @ForgeOverride
public boolean onDestroyedByPlayer(BlockState state, Level world, BlockPos pos, Player player, boolean willHarvest, FluidState fluid) { public boolean onDestroyedByPlayer(BlockState state, Level world, BlockPos pos, Player player, boolean willHarvest, FluidState fluid) {
playerWillDestroy(world, pos, state, player); playerWillDestroy(world, pos, state, player);
return onDestroyedByPlayer(state, world, pos, player, fluid); if (onCustomDestroyBlock(state, world, pos, player)) {
} return false;
public boolean onDestroyedByPlayer(BlockState state, Level world, BlockPos pos, Player player, FluidState fluid) {
if (state.getValue(CABLE) && state.getValue(MODEM).getFacing() != null) {
var hit = world.clip(new ClipContext(
WorldUtil.getRayStart(player), WorldUtil.getRayEnd(player),
ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player
));
if (hit.getType() == HitResult.Type.BLOCK) {
var tile = world.getBlockEntity(pos);
if (tile instanceof CableBlockEntity cable && tile.hasLevel()) {
ItemStack item;
BlockState newState;
if (WorldUtil.isVecInside(CableShapes.getModemShape(state), hit.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ()))) {
newState = state.setValue(MODEM, CableModemVariant.None);
item = new ItemStack(ModRegistry.Items.WIRED_MODEM.get());
} else {
newState = state.setValue(CABLE, false);
item = new ItemStack(ModRegistry.Items.CABLE.get());
}
world.setBlock(pos, correctConnections(world, pos, newState), 3);
cable.modemChanged();
cable.connectionsChanged();
if (!world.isClientSide && !player.getAbilities().instabuild) {
Block.popResource(world, pos, item);
}
return false;
}
}
} }
return world.setBlock(pos, fluid.createLegacyBlock(), world.isClientSide ? UPDATE_ALL_IMMEDIATE : UPDATE_ALL); return world.setBlock(pos, fluid.createLegacyBlock(), world.isClientSide ? UPDATE_ALL_IMMEDIATE : UPDATE_ALL);
} }
public boolean onCustomDestroyBlock(BlockState state, Level world, BlockPos pos, Player player) {
if (!state.getValue(CABLE) || state.getValue(MODEM).getFacing() == null) return false;
var hit = world.clip(new ClipContext(
WorldUtil.getRayStart(player), WorldUtil.getRayEnd(player),
ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player
));
if (hit.getType() != HitResult.Type.BLOCK) return false;
var tile = world.getBlockEntity(pos);
if (!(tile instanceof CableBlockEntity cable) || !tile.hasLevel()) return false;
ItemStack item;
BlockState newState;
if (WorldUtil.isVecInside(CableShapes.getModemShape(state), hit.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ()))) {
newState = state.setValue(MODEM, CableModemVariant.None);
item = new ItemStack(ModRegistry.Items.WIRED_MODEM.get());
} else {
newState = state.setValue(CABLE, false);
item = new ItemStack(ModRegistry.Items.CABLE.get());
}
world.setBlock(pos, correctConnections(world, pos, newState), 3);
cable.modemChanged();
cable.connectionsChanged();
if (!world.isClientSide && !player.getAbilities().instabuild) {
Block.popResource(world, pos, item);
}
return true;
}
@Override @Override
@Deprecated @Deprecated
public ItemStack getCloneItemStack(BlockGetter world, BlockPos pos, BlockState state) { public ItemStack getCloneItemStack(BlockGetter world, BlockPos pos, BlockState state) {

View File

@@ -219,7 +219,7 @@ public class WiredModemFullBlockEntity extends BlockEntity {
if (!world.isLoaded(offset)) continue; if (!world.isLoaded(offset)) continue;
var element = connectedElements.get((ServerLevel) getLevel(), getBlockPos(), facing); var element = connectedElements.get((ServerLevel) getLevel(), getBlockPos(), facing);
if (element == null) return; if (element == null) continue;
node.connectTo(element.getNode()); node.connectTo(element.getNode());
} }

View File

@@ -8,7 +8,6 @@ package dan200.computercraft.shared.peripheral.speaker;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaTable; import dan200.computercraft.api.lua.LuaTable;
import dan200.computercraft.shared.util.PauseAwareTimer; import dan200.computercraft.shared.util.PauseAwareTimer;
import net.minecraft.util.Mth;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -16,6 +15,7 @@ import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral.SAMPLE_RATE; import static dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral.SAMPLE_RATE;
import static dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral.clampVolume;
/** /**
* Internal state of the DFPWM decoder and the state of playback. * Internal state of the DFPWM decoder and the state of playback.
@@ -82,7 +82,7 @@ class DfpwmState {
buffer.flip(); buffer.flip();
pendingAudio = buffer; pendingAudio = buffer;
pendingVolume = Mth.clamp(volume.orElse((double) pendingVolume).floatValue(), 0.0f, 3.0f); pendingVolume = (float) clampVolume(volume.orElse((double) pendingVolume));
return true; return true;
} }

View File

@@ -27,6 +27,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument; import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -203,7 +204,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
*/ */
@LuaFunction @LuaFunction
public final boolean playNote(ILuaContext context, String instrumentA, Optional<Double> volumeA, Optional<Double> pitchA) throws LuaException { public final boolean playNote(ILuaContext context, String instrumentA, Optional<Double> volumeA, Optional<Double> pitchA) throws LuaException {
var volume = (float) checkFinite(1, volumeA.orElse(1.0)); var volume = (float) clampVolume(checkFinite(1, volumeA.orElse(1.0)));
var pitch = (float) checkFinite(2, pitchA.orElse(1.0)); var pitch = (float) checkFinite(2, pitchA.orElse(1.0));
NoteBlockInstrument instrument = null; NoteBlockInstrument instrument = null;
@@ -241,14 +242,14 @@ public abstract class SpeakerPeripheral implements IPeripheral {
* @throws LuaException If the sound name was invalid. * @throws LuaException If the sound name was invalid.
* @cc.usage Play a creeper hiss with the speaker. * @cc.usage Play a creeper hiss with the speaker.
* *
* <pre>{@code * <pre data-peripheral="speaker">{@code
* local speaker = peripheral.find("speaker") * local speaker = peripheral.find("speaker")
* speaker.playSound("entity.creeper.primed") * speaker.playSound("entity.creeper.primed")
* }</pre> * }</pre>
*/ */
@LuaFunction @LuaFunction
public final boolean playSound(ILuaContext context, String name, Optional<Double> volumeA, Optional<Double> pitchA) throws LuaException { public final boolean playSound(ILuaContext context, String name, Optional<Double> volumeA, Optional<Double> pitchA) throws LuaException {
var volume = (float) checkFinite(1, volumeA.orElse(1.0)); var volume = (float) clampVolume(checkFinite(1, volumeA.orElse(1.0)));
var pitch = (float) checkFinite(2, pitchA.orElse(1.0)); var pitch = (float) checkFinite(2, pitchA.orElse(1.0));
ResourceLocation identifier; ResourceLocation identifier;
@@ -293,7 +294,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
* @cc.since 1.100 * @cc.since 1.100
* @cc.usage Read an audio file, decode it using @{cc.audio.dfpwm}, and play it using the speaker. * @cc.usage Read an audio file, decode it using @{cc.audio.dfpwm}, and play it using the speaker.
* *
* <pre>{@code * <pre data-peripheral="speaker">{@code
* local dfpwm = require("cc.audio.dfpwm") * local dfpwm = require("cc.audio.dfpwm")
* local speaker = peripheral.find("speaker") * local speaker = peripheral.find("speaker")
* *
@@ -361,6 +362,10 @@ public abstract class SpeakerPeripheral implements IPeripheral {
} }
} }
static double clampVolume(double volume) {
return Mth.clamp(volume, 0, 3);
}
private record PendingSound<T>(T sound, float volume, float pitch) { private record PendingSound<T>(T sound, float volume, float pitch) {
} }
} }

View File

@@ -5,6 +5,7 @@
*/ */
package dan200.computercraft.shared.platform; package dan200.computercraft.shared.platform;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.network.wired.WiredElement;
@@ -96,6 +97,16 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
@Nullable @Nullable
<T> T tryGetRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id); <T> T tryGetRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id);
/**
* Determine if this resource should be loaded, based on platform-specific loot conditions.
* <p>
* This should only be called from the {@code apply} stage of a reload listener.
*
* @param object The root JSON object of this resource.
* @return If this resource should be loaded.
*/
boolean shouldLoadResource(JsonObject object);
/** /**
* Create a new block entity type which serves a particular block. * Create a new block entity type which serves a particular block.
* *

View File

@@ -69,7 +69,7 @@ public class PocketAPI implements ILuaAPI {
if (newUpgrade == null) return new Object[]{ false, "Cannot find a valid upgrade" }; if (newUpgrade == null) return new Object[]{ false, "Cannot find a valid upgrade" };
// Remove the current upgrade // Remove the current upgrade
if (previousUpgrade != null) storeItem(player, previousUpgrade.getCraftingItem()); if (previousUpgrade != null) storeItem(player, previousUpgrade.getCraftingItem().copy());
// Set the new upgrade // Set the new upgrade
computer.setUpgrade(newUpgrade); computer.setUpgrade(newUpgrade);
@@ -88,14 +88,13 @@ public class PocketAPI implements ILuaAPI {
public final Object[] unequipBack() { public final Object[] unequipBack() {
var entity = computer.getEntity(); var entity = computer.getEntity();
if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" }; if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" };
var inventory = player.getInventory();
var previousUpgrade = computer.getUpgrade(); var previousUpgrade = computer.getUpgrade();
if (previousUpgrade == null) return new Object[]{ false, "Nothing to unequip" }; if (previousUpgrade == null) return new Object[]{ false, "Nothing to unequip" };
computer.setUpgrade(null); computer.setUpgrade(null);
storeItem(player, previousUpgrade.getCraftingItem()); storeItem(player, previousUpgrade.getCraftingItem().copy());
return new Object[]{ true }; return new Object[]{ true };
} }

View File

@@ -25,16 +25,14 @@ public class PocketModem extends AbstractPocketUpgrade {
@Nullable @Nullable
@Override @Override
public IPeripheral createPeripheral(IPocketAccess access) { public IPeripheral createPeripheral(IPocketAccess access) {
return new PocketModemPeripheral(advanced); return new PocketModemPeripheral(advanced, access);
} }
@Override @Override
public void update(IPocketAccess access, @Nullable IPeripheral peripheral) { public void update(IPocketAccess access, @Nullable IPeripheral peripheral) {
if (!(peripheral instanceof PocketModemPeripheral modem)) return; if (!(peripheral instanceof PocketModemPeripheral modem)) return;
var entity = access.getEntity(); modem.setLocation(access);
if (entity != null) modem.setLocation(entity.getCommandSenderWorld(), entity.getEyePosition(1));
var state = modem.getModemState(); var state = modem.getModemState();
if (state.pollChanged()) access.setLight(state.isOpen() ? 0xBA0000 : -1); if (state.pollChanged()) access.setLight(state.isOpen() ? 0xBA0000 : -1);

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.shared.pocket.peripherals; package dan200.computercraft.shared.pocket.peripherals;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@@ -17,13 +18,17 @@ public class PocketModemPeripheral extends WirelessModemPeripheral {
private @Nullable Level level = null; private @Nullable Level level = null;
private Vec3 position = Vec3.ZERO; private Vec3 position = Vec3.ZERO;
public PocketModemPeripheral(boolean advanced) { public PocketModemPeripheral(boolean advanced, IPocketAccess access) {
super(new ModemState(), advanced); super(new ModemState(), advanced);
setLocation(access);
} }
void setLocation(Level level, Vec3 position) { void setLocation(IPocketAccess access) {
this.position = position; var entity = access.getEntity();
this.level = level; if (entity != null) {
level = entity.getCommandSenderWorld();
position = entity.getEyePosition(1);
}
} }
@Override @Override

View File

@@ -46,7 +46,7 @@ public class TurtleUtil {
public static void storeItemOrDrop(ITurtleAccess turtle, ItemStack stack) { public static void storeItemOrDrop(ITurtleAccess turtle, ItemStack stack) {
if (stack.isEmpty()) return; if (stack.isEmpty()) return;
if (turtle.isRemoved()) { if (turtle.isRemoved()) {
WorldUtil.dropItemStack(stack, turtle.getLevel(), turtle.getPosition(), null); WorldUtil.dropItemStack(turtle.getLevel(), turtle.getPosition(), null, stack);
return; return;
} }
@@ -54,7 +54,7 @@ public class TurtleUtil {
var remainder = InventoryUtil.storeItemsFromOffset(turtle.getInventory(), stack, turtle.getSelectedSlot()); var remainder = InventoryUtil.storeItemsFromOffset(turtle.getInventory(), stack, turtle.getSelectedSlot());
if (remainder.isEmpty()) return; if (remainder.isEmpty()) return;
WorldUtil.dropItemStack(remainder, turtle.getLevel(), turtle.getPosition(), turtle.getDirection().getOpposite()); WorldUtil.dropItemStack(turtle.getLevel(), turtle.getPosition(), turtle.getDirection().getOpposite(), remainder);
} }
public static Function<ItemStack, ItemStack> dropConsumer(ITurtleAccess turtle) { public static Function<ItemStack, ItemStack> dropConsumer(ITurtleAccess turtle) {

View File

@@ -7,16 +7,13 @@ package dan200.computercraft.shared.turtle.apis;
import dan200.computercraft.api.detail.VanillaDetailRegistries; import dan200.computercraft.api.detail.VanillaDetailRegistries;
import dan200.computercraft.api.lua.*; import dan200.computercraft.api.lua.*;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleCommand; import dan200.computercraft.api.turtle.TurtleCommand;
import dan200.computercraft.api.turtle.TurtleCommandResult; import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.metrics.Metrics; import dan200.computercraft.core.metrics.Metrics;
import dan200.computercraft.shared.details.ItemDetails;
import dan200.computercraft.shared.turtle.core.*; import dan200.computercraft.shared.turtle.core.*;
import java.util.HashMap;
import java.util.Optional; import java.util.Optional;
/** /**
@@ -70,9 +67,9 @@ import java.util.Optional;
*/ */
public class TurtleAPI implements ILuaAPI { public class TurtleAPI implements ILuaAPI {
private final IAPIEnvironment environment; private final IAPIEnvironment environment;
private final ITurtleAccess turtle; private final TurtleAccessInternal turtle;
public TurtleAPI(IAPIEnvironment environment, ITurtleAccess turtle) { public TurtleAPI(IAPIEnvironment environment, TurtleAccessInternal turtle) {
this.environment = environment; this.environment = environment;
this.turtle = turtle; this.turtle = turtle;
} }
@@ -136,7 +133,7 @@ public class TurtleAPI implements ILuaAPI {
} }
/** /**
* Rotate the turtle 90 degress to the left. * Rotate the turtle 90 degrees to the left.
* *
* @return The turtle command result. * @return The turtle command result.
* @cc.treturn boolean Whether the turtle could successfully turn. * @cc.treturn boolean Whether the turtle could successfully turn.
@@ -148,7 +145,7 @@ public class TurtleAPI implements ILuaAPI {
} }
/** /**
* Rotate the turtle 90 degress to the right. * Rotate the turtle 90 degrees to the right.
* *
* @return The turtle command result. * @return The turtle command result.
* @cc.treturn boolean Whether the turtle could successfully turn. * @cc.treturn boolean Whether the turtle could successfully turn.
@@ -555,7 +552,7 @@ public class TurtleAPI implements ILuaAPI {
* @throws LuaException If the refuel count is out of range. * @throws LuaException If the refuel count is out of range.
* @cc.treturn [1] true If the turtle was refuelled. * @cc.treturn [1] true If the turtle was refuelled.
* @cc.treturn [2] false If the turtle was not refuelled. * @cc.treturn [2] false If the turtle was not refuelled.
* @cc.treturn [2] string The reason the turtle was not refuelled ( * @cc.treturn [2] string The reason the turtle was not refuelled.
* @cc.usage Refuel a turtle from the currently selected slot. * @cc.usage Refuel a turtle from the currently selected slot.
* <pre>{@code * <pre>{@code
* local level = turtle.getFuelLevel() * local level = turtle.getFuelLevel()
@@ -652,7 +649,7 @@ public class TurtleAPI implements ILuaAPI {
* previous upgrade is removed and placed into the turtle's inventory. If there is no item in the slot, the previous * previous upgrade is removed and placed into the turtle's inventory. If there is no item in the slot, the previous
* upgrade is removed, but no new one is equipped. * upgrade is removed, but no new one is equipped.
* *
* @return Whether an item was equiped or not. * @return Whether an item was equipped or not.
* @cc.treturn [1] true If the item was equipped. * @cc.treturn [1] true If the item was equipped.
* @cc.treturn [2] false If we could not equip the item. * @cc.treturn [2] false If we could not equip the item.
* @cc.treturn [2] string The reason equipping this item failed. * @cc.treturn [2] string The reason equipping this item failed.
@@ -671,7 +668,7 @@ public class TurtleAPI implements ILuaAPI {
* previous upgrade is removed and placed into the turtle's inventory. If there is no item in the slot, the previous * previous upgrade is removed and placed into the turtle's inventory. If there is no item in the slot, the previous
* upgrade is removed, but no new one is equipped. * upgrade is removed, but no new one is equipped.
* *
* @return Whether an item was equiped or not. * @return Whether an item was equipped or not.
* @cc.treturn [1] true If the item was equipped. * @cc.treturn [1] true If the item was equipped.
* @cc.treturn [2] false If we could not equip the item. * @cc.treturn [2] false If we could not equip the item.
* @cc.treturn [2] string The reason equipping this item failed. * @cc.treturn [2] string The reason equipping this item failed.
@@ -760,20 +757,15 @@ public class TurtleAPI implements ILuaAPI {
@LuaFunction @LuaFunction
public final MethodResult getItemDetail(ILuaContext context, Optional<Integer> slot, Optional<Boolean> detailed) throws LuaException { public final MethodResult getItemDetail(ILuaContext context, Optional<Integer> slot, Optional<Boolean> detailed) throws LuaException {
int actualSlot = checkSlot(slot).orElse(turtle.getSelectedSlot()); int actualSlot = checkSlot(slot).orElse(turtle.getSelectedSlot());
return detailed.orElse(false) if (detailed.orElse(false)) {
? context.executeMainThreadTask(() -> getItemDetail(actualSlot, true)) return context.executeMainThreadTask(() -> {
: MethodResult.of(getItemDetail(actualSlot, false)); var stack = turtle.getInventory().getItem(actualSlot);
} return new Object[]{ stack.isEmpty() ? null : VanillaDetailRegistries.ITEM_STACK.getDetails(stack) };
});
private Object[] getItemDetail(int slot, boolean detailed) { } else {
var stack = turtle.getInventory().getItem(slot); var stack = turtle.getItemSnapshot(actualSlot);
if (stack.isEmpty()) return new Object[]{ null }; return MethodResult.of(stack.isEmpty() ? null : VanillaDetailRegistries.ITEM_STACK.getBasicDetails(stack));
}
var table = detailed
? VanillaDetailRegistries.ITEM_STACK.getDetails(stack)
: ItemDetails.fillBasicSafe(new HashMap<>(), stack);
return new Object[]{ table };
} }

View File

@@ -113,9 +113,8 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
public final void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) { public final void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
if (state.is(newState.getBlock())) return; if (state.is(newState.getBlock())) return;
if (level.getBlockEntity(pos) instanceof TurtleBlockEntity turtle) { if (!level.isClientSide && level.getBlockEntity(pos) instanceof TurtleBlockEntity turtle && !turtle.hasMoved()) {
if (!level.isClientSide) Containers.dropContents(level, pos, turtle); Containers.dropContents(level, pos, turtle);
level.updateNeighbourForOutputSignal(pos, this);
} }
super.onRemove(state, level, pos, newState, isMoving); super.onRemove(state, level, pos, newState, isMoving);

View File

@@ -29,14 +29,10 @@ import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.DyeItem;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@@ -56,7 +52,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
} }
private final NonNullList<ItemStack> inventory = NonNullList.withSize(INVENTORY_SIZE, ItemStack.EMPTY); private final NonNullList<ItemStack> inventory = NonNullList.withSize(INVENTORY_SIZE, ItemStack.EMPTY);
private final NonNullList<ItemStack> previousInventory = NonNullList.withSize(INVENTORY_SIZE, ItemStack.EMPTY); private final NonNullList<ItemStack> inventorySnapshot = NonNullList.withSize(INVENTORY_SIZE, ItemStack.EMPTY);
private boolean inventoryChanged = false; private boolean inventoryChanged = false;
private TurtleBrain brain = new TurtleBrain(this); private TurtleBrain brain = new TurtleBrain(this);
private MoveState moveState = MoveState.NOT_MOVED; private MoveState moveState = MoveState.NOT_MOVED;
@@ -67,7 +63,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
super(type, pos, state, family); super(type, pos, state, family);
} }
private boolean hasMoved() { boolean hasMoved() {
return moveState == MoveState.MOVED; return moveState == MoveState.MOVED;
} }
@@ -78,7 +74,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
getFamily(), Config.turtleTermWidth, getFamily(), Config.turtleTermWidth,
Config.turtleTermHeight Config.turtleTermHeight
); );
computer.addAPI(new TurtleAPI(computer.getAPIEnvironment(), getAccess())); computer.addAPI(new TurtleAPI(computer.getAPIEnvironment(), brain));
brain.setupComputer(computer); brain.setupComputer(computer);
return computer; return computer;
} }
@@ -88,42 +84,6 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
if (!hasMoved()) super.unload(); if (!hasMoved()) super.unload();
} }
@Override
public InteractionResult use(Player player, InteractionHand hand) {
// Apply dye
var currentItem = player.getItemInHand(hand);
if (!currentItem.isEmpty()) {
if (currentItem.getItem() instanceof DyeItem dyeItem) {
// Dye to change turtle colour
if (!getLevel().isClientSide) {
var dye = dyeItem.getDyeColor();
if (brain.getDyeColour() != dye) {
brain.setDyeColour(dye);
if (!player.isCreative()) {
currentItem.shrink(1);
}
}
}
return InteractionResult.sidedSuccess(getLevel().isClientSide);
} else if (currentItem.getItem() == Items.WATER_BUCKET && brain.getColour() != -1) {
// Water to remove turtle colour
if (!getLevel().isClientSide) {
if (brain.getColour() != -1) {
brain.setColour(-1);
if (!player.isCreative()) {
player.setItemInHand(hand, new ItemStack(Items.BUCKET));
player.getInventory().setChanged();
}
}
}
return InteractionResult.sidedSuccess(getLevel().isClientSide);
}
}
// Open GUI or whatever
return super.use(player, hand);
}
@Override @Override
protected boolean canNameWithTag(Player player) { protected boolean canNameWithTag(Player player) {
return true; return true;
@@ -141,11 +101,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
if (inventoryChanged) { if (inventoryChanged) {
var computer = getServerComputer(); var computer = getServerComputer();
if (computer != null) computer.queueEvent("turtle_inventory"); if (computer != null) computer.queueEvent("turtle_inventory");
inventoryChanged = false; inventoryChanged = false;
for (var n = 0; n < getContainerSize(); n++) {
previousInventory.set(n, getItem(n).copy());
}
} }
} }
@@ -178,13 +134,13 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
// Read inventory // Read inventory
var nbttaglist = nbt.getList("Items", Tag.TAG_COMPOUND); var nbttaglist = nbt.getList("Items", Tag.TAG_COMPOUND);
inventory.clear(); inventory.clear();
previousInventory.clear(); inventorySnapshot.clear();
for (var i = 0; i < nbttaglist.size(); i++) { for (var i = 0; i < nbttaglist.size(); i++) {
var tag = nbttaglist.getCompound(i); var tag = nbttaglist.getCompound(i);
var slot = tag.getByte("Slot") & 0xff; var slot = tag.getByte("Slot") & 0xff;
if (slot < getContainerSize()) { if (slot < getContainerSize()) {
inventory.set(slot, ItemStack.of(tag)); inventory.set(slot, ItemStack.of(tag));
previousInventory.set(slot, inventory.get(slot).copy()); inventorySnapshot.set(slot, inventory.get(slot).copy());
} }
} }
@@ -273,7 +229,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
void setOwningPlayer(GameProfile player) { void setOwningPlayer(GameProfile player) {
brain.setOwningPlayer(player); brain.setOwningPlayer(player);
setChanged(); onTileEntityChange();
} }
// IInventory // IInventory
@@ -283,16 +239,20 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
return inventory; return inventory;
} }
public ItemStack getItemSnapshot(int slot) {
return slot >= 0 && slot < inventorySnapshot.size() ? inventorySnapshot.get(slot) : ItemStack.EMPTY;
}
@Override @Override
public void setChanged() { public void setChanged() {
super.setChanged(); super.setChanged();
if (!inventoryChanged) {
for (var n = 0; n < getContainerSize(); n++) { for (var slot = 0; slot < getContainerSize(); slot++) {
if (!ItemStack.matches(getItem(n), previousInventory.get(n))) { var item = getItem(slot);
inventoryChanged = true; if (ItemStack.matches(item, inventorySnapshot.get(slot))) continue;
break;
} inventoryChanged = true;
} inventorySnapshot.set(slot, item.copy());
} }
} }
@@ -340,7 +300,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
public void transferStateFrom(TurtleBlockEntity copy) { public void transferStateFrom(TurtleBlockEntity copy) {
super.transferStateFrom(copy); super.transferStateFrom(copy);
Collections.copy(inventory, copy.inventory); Collections.copy(inventory, copy.inventory);
Collections.copy(previousInventory, copy.previousInventory); Collections.copy(inventorySnapshot, copy.inventorySnapshot);
inventoryChanged = copy.inventoryChanged; inventoryChanged = copy.inventoryChanged;
brain = copy.brain; brain = copy.brain;
brain.setOwner(this); brain.setOwner(this);

View File

@@ -0,0 +1,27 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.turtle.core;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.world.item.ItemStack;
/**
* An internal version of {@link ITurtleAccess}.
* <p>
* This exposes additional functionality we don't want in the public API, but where we don't want access to the full
* {@link TurtleBrain} interface.
*/
public interface TurtleAccessInternal extends ITurtleAccess {
/**
* Get an immutable snapshot of an item in the inventory. This is a thread-safe version of
* {@code getInventory().getItem()}.
*
* @param slot The slot
* @return The current item. This should NOT be modified.
* @see net.minecraft.world.Container#getItem(int)
*/
ItemStack getItemSnapshot(int slot);
}

View File

@@ -10,7 +10,10 @@ import com.mojang.authlib.GameProfile;
import dan200.computercraft.api.lua.ILuaCallback; import dan200.computercraft.api.lua.ILuaCallback;
import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.*; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleAnimation;
import dan200.computercraft.api.turtle.TurtleCommand;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.computer.ComputerSide;
import dan200.computercraft.core.util.Colour; import dan200.computercraft.core.util.Colour;
import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.impl.TurtleUpgrades;
@@ -21,7 +24,6 @@ import dan200.computercraft.shared.container.InventoryDelegate;
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity; import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
import dan200.computercraft.shared.util.BlockEntityHelpers; import dan200.computercraft.shared.util.BlockEntityHelpers;
import dan200.computercraft.shared.util.Holiday; import dan200.computercraft.shared.util.Holiday;
import dan200.computercraft.shared.util.HolidayUtil;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.particles.ParticleTypes;
@@ -34,6 +36,7 @@ import net.minecraft.world.Container;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.MoverType;
import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.material.PushReaction; import net.minecraft.world.level.material.PushReaction;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
@@ -47,7 +50,7 @@ import java.util.function.Predicate;
import static dan200.computercraft.shared.common.IColouredItem.NBT_COLOUR; import static dan200.computercraft.shared.common.IColouredItem.NBT_COLOUR;
import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED; import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED;
public class TurtleBrain implements ITurtleAccess { public class TurtleBrain implements TurtleAccessInternal {
public static final String NBT_RIGHT_UPGRADE = "RightUpgrade"; public static final String NBT_RIGHT_UPGRADE = "RightUpgrade";
public static final String NBT_RIGHT_UPGRADE_DATA = "RightUpgradeNbt"; public static final String NBT_RIGHT_UPGRADE_DATA = "RightUpgradeNbt";
public static final String NBT_LEFT_UPGRADE = "LeftUpgrade"; public static final String NBT_LEFT_UPGRADE = "LeftUpgrade";
@@ -456,7 +459,7 @@ public class TurtleBrain implements ITurtleAccess {
return overlay; return overlay;
} }
public void setOverlay(ResourceLocation overlay) { public void setOverlay(@Nullable ResourceLocation overlay) {
if (!Objects.equal(this.overlay, overlay)) { if (!Objects.equal(this.overlay, overlay)) {
this.overlay = overlay; this.overlay = overlay;
BlockEntityHelpers.updateBlock(owner); BlockEntityHelpers.updateBlock(owner);
@@ -735,7 +738,7 @@ public class TurtleBrain implements ITurtleAccess {
// Advance valentines day easter egg // Advance valentines day easter egg
if (world.isClientSide && animation == TurtleAnimation.MOVE_FORWARD && animationProgress == 4) { if (world.isClientSide && animation == TurtleAnimation.MOVE_FORWARD && animationProgress == 4) {
// Spawn love pfx if valentines day // Spawn love pfx if valentines day
var currentHoliday = HolidayUtil.getCurrentHoliday(); var currentHoliday = Holiday.getCurrent();
if (currentHoliday == Holiday.VALENTINES) { if (currentHoliday == Holiday.VALENTINES) {
var position = getVisualPosition(1.0f); var position = getVisualPosition(1.0f);
if (position != null) { if (position != null) {
@@ -768,6 +771,11 @@ public class TurtleBrain implements ITurtleAccess {
return previous + (next - previous) * f; return previous + (next - previous) * f;
} }
@Override
public ItemStack getItemSnapshot(int slot) {
return owner.getItemSnapshot(slot);
}
private static final class CommandCallback implements ILuaCallback { private static final class CommandCallback implements ILuaCallback {
final MethodResult pull = MethodResult.pullEvent("turtle_response", this); final MethodResult pull = MethodResult.pullEvent("turtle_response", this);
private final int command; private final int command;

View File

@@ -58,7 +58,7 @@ public class TurtleDropCommand implements TurtleCommand {
turtle.getInventory().setChanged(); turtle.getInventory().setChanged();
transferred = stack.getCount(); transferred = stack.getCount();
WorldUtil.dropItemStack(stack, world, oldPosition, direction); WorldUtil.dropItemStack(world, oldPosition, direction, stack);
world.globalLevelEvent(LevelEvent.SOUND_DISPENSER_DISPENSE, newPosition, 0); world.globalLevelEvent(LevelEvent.SOUND_DISPENSER_DISPENSE, newPosition, 0);
} }
} }

View File

@@ -8,7 +8,6 @@ package dan200.computercraft.shared.turtle.core;
import dan200.computercraft.api.turtle.*; import dan200.computercraft.api.turtle.*;
import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.impl.TurtleUpgrades;
import dan200.computercraft.shared.turtle.TurtleUtil; import dan200.computercraft.shared.turtle.TurtleUtil;
import net.minecraft.world.item.ItemStack;
public class TurtleEquipCommand implements TurtleCommand { public class TurtleEquipCommand implements TurtleCommand {
private final TurtleSide side; private final TurtleSide side;
@@ -19,32 +18,22 @@ public class TurtleEquipCommand implements TurtleCommand {
@Override @Override
public TurtleCommandResult execute(ITurtleAccess turtle) { public TurtleCommandResult execute(ITurtleAccess turtle) {
// Determine the upgrade to replace
var oldUpgrade = turtle.getUpgrade(side);
// Determine the upgrade to equipLeft // Determine the upgrade to equipLeft
ITurtleUpgrade newUpgrade; ITurtleUpgrade newUpgrade;
ItemStack newUpgradeStack;
var selectedStack = turtle.getInventory().getItem(turtle.getSelectedSlot()); var selectedStack = turtle.getInventory().getItem(turtle.getSelectedSlot());
if (!selectedStack.isEmpty()) { if (!selectedStack.isEmpty()) {
newUpgradeStack = selectedStack.copy(); newUpgrade = TurtleUpgrades.instance().get(selectedStack);
newUpgrade = TurtleUpgrades.instance().get(newUpgradeStack);
if (newUpgrade == null) return TurtleCommandResult.failure("Not a valid upgrade"); if (newUpgrade == null) return TurtleCommandResult.failure("Not a valid upgrade");
} else { } else {
newUpgradeStack = null;
newUpgrade = null; newUpgrade = null;
} }
// Determine the upgrade to replace
ItemStack oldUpgradeStack;
var oldUpgrade = turtle.getUpgrade(side);
if (oldUpgrade != null) {
var craftingItem = oldUpgrade.getCraftingItem();
oldUpgradeStack = !craftingItem.isEmpty() ? craftingItem.copy() : null;
} else {
oldUpgradeStack = null;
}
// Do the swapping: // Do the swapping:
if (newUpgradeStack != null) turtle.getInventory().removeItem(turtle.getSelectedSlot(), 1); if (newUpgrade != null) turtle.getInventory().removeItem(turtle.getSelectedSlot(), 1);
if (oldUpgradeStack != null) TurtleUtil.storeItemOrDrop(turtle, oldUpgradeStack); if (oldUpgrade != null) TurtleUtil.storeItemOrDrop(turtle, oldUpgrade.getCraftingItem().copy());
turtle.setUpgrade(side, newUpgrade); turtle.setUpgrade(side, newUpgrade);
// Animate // Animate

View File

@@ -6,6 +6,7 @@
package dan200.computercraft.shared.turtle.core; package dan200.computercraft.shared.turtle.core;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleAnimation; import dan200.computercraft.api.turtle.TurtleAnimation;
import dan200.computercraft.api.turtle.TurtleCommand; import dan200.computercraft.api.turtle.TurtleCommand;
@@ -21,9 +22,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.*;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.SignItem;
import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@@ -209,7 +208,7 @@ public class TurtlePlaceCommand implements TurtleCommand {
// We special case some items which we allow to place "normally". Yes, this is very ugly. // We special case some items which we allow to place "normally". Yes, this is very ugly.
var item = stack.getItem(); var item = stack.getItem();
if (item.getUseDuration(stack) == 0) { if (item instanceof BucketItem || item instanceof PlaceOnWaterBlockItem || stack.is(ComputerCraftTags.Items.TURTLE_CAN_PLACE)) {
return turtlePlayer.player().gameMode.useItem(turtlePlayer.player(), turtlePlayer.player().level, stack, InteractionHand.MAIN_HAND); return turtlePlayer.player().gameMode.useItem(turtlePlayer.player(), turtlePlayer.player().level, stack, InteractionHand.MAIN_HAND);
} }

View File

@@ -61,7 +61,7 @@ public final class DropConsumer {
public static void clearAndDrop(Level world, BlockPos pos, @Nullable Direction direction) { public static void clearAndDrop(Level world, BlockPos pos, @Nullable Direction direction) {
var remainingDrops = clear(); var remainingDrops = clear();
for (var remaining : remainingDrops) WorldUtil.dropItemStack(remaining, world, pos, direction); for (var remaining : remainingDrops) WorldUtil.dropItemStack(world, pos, direction, remaining);
} }
private static void handleDrops(ItemStack stack) { private static void handleDrops(ItemStack stack) {

View File

@@ -5,10 +5,30 @@
*/ */
package dan200.computercraft.shared.util; package dan200.computercraft.shared.util;
import java.time.LocalDateTime;
import java.time.Month;
public enum Holiday { public enum Holiday {
NONE, NONE,
/**
* 14th February.
*/
VALENTINES, VALENTINES,
APRIL_FOOLS_DAY,
HALLOWEEN, /**
CHRISTMAS, * 24th-26th December.
*
* @see net.minecraft.client.renderer.blockentity.ChestRenderer
*/
CHRISTMAS;
public static Holiday getCurrent() {
var calendar = LocalDateTime.now();
var month = calendar.getMonth();
var day = calendar.getDayOfMonth();
if (month == Month.FEBRUARY && day == 14) return VALENTINES;
if (month == Month.DECEMBER && day >= 24 && day <= 26) return CHRISTMAS;
return NONE;
}
} }

View File

@@ -1,27 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.util;
import java.util.Calendar;
public final class HolidayUtil {
private HolidayUtil() {
}
public static Holiday getCurrentHoliday() {
return getHoliday(Calendar.getInstance());
}
private static Holiday getHoliday(Calendar calendar) {
var month = calendar.get(Calendar.MONTH);
var day = calendar.get(Calendar.DAY_OF_MONTH);
if (month == Calendar.FEBRUARY && day == 14) return Holiday.VALENTINES;
if (month == Calendar.APRIL && day == 1) return Holiday.APRIL_FOOLS_DAY;
if (month == Calendar.OCTOBER && day == 31) return Holiday.HALLOWEEN;
if (month == Calendar.DECEMBER && day >= 24 && day <= 30) return Holiday.CHRISTMAS;
return Holiday.NONE;
}
}

View File

@@ -51,14 +51,13 @@ public final class InventoryUtil {
} }
private static ItemStack storeItemsImpl(Container container, ItemStack stack, int offset, int slotCount) { private static ItemStack storeItemsImpl(Container container, ItemStack stack, int offset, int slotCount) {
var limit = container.getMaxStackSize(); var limit = container.getContainerSize();
var maxSize = Math.min(stack.getMaxStackSize(), container.getMaxStackSize()); var maxSize = Math.min(stack.getMaxStackSize(), container.getMaxStackSize());
if (maxSize <= 0) return stack; if (maxSize <= 0) return stack;
for (var i = 0; i < slotCount; i++) { for (var i = 0; i < slotCount; i++) {
var slot = i + offset; var slot = i + offset;
if (slot > limit) slot -= limit; if (slot >= limit) slot -= limit;
var currentStack = container.getItem(slot); var currentStack = container.getItem(slot);
if (currentStack.isEmpty()) { if (currentStack.isEmpty()) {
// If the current slot is empty and we can place them item then there's two cases: // If the current slot is empty and we can place them item then there's two cases:

View File

@@ -8,6 +8,8 @@ package dan200.computercraft.shared.util;
import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.platform.PlatformHelper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Position;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.item.ItemEntity;
@@ -118,11 +120,21 @@ public final class WorldUtil {
return getRayStart(player).add(look.x * reach, look.y * reach, look.z * reach); return getRayStart(player).add(look.x * reach, look.y * reach, look.z * reach);
} }
public static void dropItemStack(ItemStack stack, Level world, BlockPos pos) { private static final double DROP_SPEED = 0.0172275 * 6;
dropItemStack(stack, world, pos, null);
}
public static void dropItemStack(ItemStack stack, Level world, BlockPos pos, @Nullable Direction direction) { /**
* Drop an item stack into the world from a block.
* <p>
* This behaves similarly to {@link DefaultDispenseItemBehavior#spawnItem(Level, ItemStack, int, Direction, Position)},
* though supports a {@code null} direction (in which case the item will have no velocity) and produces a slightly
* different arc.
*
* @param level The level to drop the item in.
* @param pos The position to drop the stack from.
* @param direction The direction to drop in, or {@code null}.
* @param stack The stack to drop.
*/
public static void dropItemStack(Level level, BlockPos pos, @Nullable Direction direction, ItemStack stack) {
double xDir; double xDir;
double yDir; double yDir;
double zDir; double zDir;
@@ -136,25 +148,20 @@ public final class WorldUtil {
zDir = 0.0; zDir = 0.0;
} }
var xPos = pos.getX() + 0.5 + xDir * 0.4; var xPos = pos.getX() + 0.5 + xDir * 0.7;
var yPos = pos.getY() + 0.5 + yDir * 0.4; var yPos = pos.getY() + 0.5 + yDir * 0.7;
var zPos = pos.getZ() + 0.5 + zDir * 0.4; var zPos = pos.getZ() + 0.5 + zDir * 0.7;
dropItemStack(stack, world, new Vec3(xPos, yPos, zPos), xDir, yDir, zDir);
}
public static void dropItemStack(ItemStack stack, Level world, Vec3 pos) { var item = new ItemEntity(level, xPos, yPos, zPos, stack.copy());
dropItemStack(stack, world, pos, 0.0, 0.0, 0.0); var baseSpeed = level.random.nextDouble() * 0.1 + 0.2;
}
public static void dropItemStack(ItemStack stack, Level world, Vec3 pos, double xDir, double yDir, double zDir) {
var item = new ItemEntity(world, pos.x, pos.y, pos.z, stack.copy());
item.setDeltaMovement( item.setDeltaMovement(
xDir * 0.7 + world.getRandom().nextFloat() * 0.2 - 0.1, level.random.triangle(xDir * baseSpeed, DROP_SPEED),
yDir * 0.7 + world.getRandom().nextFloat() * 0.2 - 0.1, // Vanilla ignores the yDir and does a constant 0.2, but that gives the item a higher arc than we want.
zDir * 0.7 + world.getRandom().nextFloat() * 0.2 - 0.1 level.random.triangle(yDir * baseSpeed, DROP_SPEED),
level.random.triangle(zDir * baseSpeed, DROP_SPEED)
); );
item.setDefaultPickUpDelay(); item.setDefaultPickUpDelay();
world.addFreshEntity(item); level.addFreshEntity(item);
} }
/** /**
@@ -171,8 +178,8 @@ public final class WorldUtil {
} }
@Override @Override
public VoxelShape getBlockShape(BlockState state, BlockGetter levle, BlockPos pos) { public VoxelShape getBlockShape(BlockState state, BlockGetter level, BlockPos pos) {
return block.get(state, levle, pos, CollisionContext.empty()); return block.get(state, level, pos, CollisionContext.empty());
} }
} }
} }

View File

@@ -139,5 +139,48 @@
"upgrade.minecraft.diamond_hoe.adjective": "Contadina", "upgrade.minecraft.diamond_hoe.adjective": "Contadina",
"upgrade.minecraft.diamond_pickaxe.adjective": "Minatrice", "upgrade.minecraft.diamond_pickaxe.adjective": "Minatrice",
"upgrade.minecraft.diamond_shovel.adjective": "Scavatrice", "upgrade.minecraft.diamond_shovel.adjective": "Scavatrice",
"upgrade.minecraft.diamond_sword.adjective": "Da Combattimento" "upgrade.minecraft.diamond_sword.adjective": "Da Combattimento",
"gui.computercraft.config.term_sizes.computer.width.tooltip": "Intervallo: 1 ~ 255",
"gui.computercraft.config.turtle.need_fuel.tooltip": "Imposta se la Tartaruga richiede del carburante per muoversi.",
"gui.computercraft.config.command_require_creative": "Il computer Comando richiede la modalità creativa",
"tracking_field.computercraft.http_requests.name": "Richieste HTTP",
"gui.computercraft.config.command_require_creative.tooltip": "Richiede che i giocatori siano in modalità creativa e che siano operatori in ordine per interagire con\ni computer di commando. Questo è il comportamento predefinito dei blocchi di comando.",
"gui.computercraft.config.disable_lua51_features.tooltip": "Imposta a \"true\" per disattivare le funzionalità di Lua 5.1 che saranno rimosse in un\naggiornamento futuro. Utile per assicurare futura compatibilità con i tuoi programmi.",
"gui.computercraft.config.execution": "Esecuzione",
"gui.computercraft.config.execution.max_main_computer_time": "Limite di tempo nei tick del server dei computer",
"gui.computercraft.config.execution.max_main_computer_time.tooltip": "Il tempo massimo ideale che un computer può eseguire in un tick, in millisecondi.\nNota, potremmo andare ben sopra questo limite, perché non c'è modo di sapere\nquanto impiega, questa configurazione mira ad essere il limite maggiore del tempo medio.\nRange: > 1",
"gui.computercraft.config.execution.max_main_global_time": "Limite tempo globale dei tick del server",
"gui.computercraft.config.execution.tooltip": "Controlla comportamento esecuzione dei computer. Questo è largamente utilizzato\nper ritoccare la performance dei server, e generale non dovrebbe essere toccato.",
"gui.computercraft.config.floppy_space_limit.tooltip": "Limite di spazio di archiviazione per i dischi floppy, in byte.",
"gui.computercraft.config.http.bandwidth": "Banda larga",
"gui.computercraft.config.http.bandwidth.global_download": "Limite download globale",
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "Numero di byte che possono essere scaricati in un secondo. Questo è condiviso tra tutti i computer. (bytes/s).\nRange: > 1",
"gui.computercraft.config.http.bandwidth.global_upload": "Limite upload globale",
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "Numero di byte che possono essere caricati in un secondo. Questo è condiviso tra tutti i computer. (bytes/s).\nRange: > 1",
"gui.computercraft.config.http.bandwidth.tooltip": "Limita la banda larga usato dai computer.",
"gui.computercraft.config.http.max_requests.tooltip": "Il numero di richieste http che un computer può fare alla volta. Ulteriori richieste\nverranno messe in coda, ed inviate quando le richieste correnti sono terminate.\nImposta a 0 per illimitato.\nRange: > 0",
"gui.computercraft.config.http.max_websockets.tooltip": "Il numero di websocket che un computer può avere aperte allo stesso momento.\nImposta a 0 per illimitato.\nRange: > 1",
"gui.computercraft.config.http.rules": "Concedi/nega regole",
"gui.computercraft.config.maximum_open_files.tooltip": "Imposta quanti file possono essere aperti allo stesso momento su un computer. Imposta a 0 per illimitato.\nRange: > 0",
"gui.computercraft.config.monitor_distance": "Distanza monitor",
"gui.computercraft.config.monitor_distance.tooltip": "Distanza massima che i monitor vengono renderizzati. Questo è predefinito al limite\nstandard dei tile entity, ma può essere esteso se desideri construire monitor più grandi.\nRange: 16 ~ 1024",
"gui.computercraft.config.monitor_renderer": "Renderizzatore monitor",
"gui.computercraft.config.peripheral.command_block_enabled.tooltip": "Attiva il supporto ai blocchi di comando come periferiche",
"gui.computercraft.config.peripheral.max_notes_per_tick.tooltip": "Quantità massima di note che un altoparlante può riprodurre allo stesso momento.\nRange: > 1",
"gui.computercraft.config.peripheral.modem_high_altitude_range.tooltip": "La distanza massima dei modem wireless all'altitudine massima durante il meteo soleggiato. in metri.\nRange: 0 ~ 100000",
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm.tooltip": "La distanza massima dei modem wireless all'altitudine massima durante una tempesta. in metri.\nRange: 0 ~ 100000",
"gui.computercraft.config.peripheral.modem_range.tooltip": "La distanza massima dei modem wireless ad altitudini basse durante il meteo soleggiato. in metri.\nRange: 0 ~ 100000",
"gui.computercraft.config.peripheral.modem_range_during_storm.tooltip": "La distanza massima dei modem wireless ad altitudini basse durante una tempesta. in metri.\nRange: 0 ~ 100000",
"gui.computercraft.config.peripheral.monitor_bandwidth": "Banda larga monitor",
"gui.computercraft.config.computer_space_limit.tooltip": "Limite di spazio di archiviazione per i computer e le tartarughe, in byte.",
"gui.computercraft.config.default_computer_settings.tooltip": "Una lista di impostazioni predefinite per i nuovi computer, separate da virgola.\nEsempio: \"shell.autocomplete=false,lua.autocomplete=false,edit.autocomplete=false\"\ndisattiverà tutti gli autocompletamenti.",
"gui.computercraft.config.execution.computer_threads.tooltip": "Imposta la quantità di thread che possono eseguire i computer. Un numero più alto significa\nche più computer possono essere eseguiti alla volta, ma può indurre a lag. Alcune mod potrebbero\nnon funzionare con numeri di thread maggiore a 1. Usare con cautela.\nRange: > 1",
"gui.computercraft.config.execution.max_main_global_time.tooltip": "Il limite massimo di tempo che può essere usato per eseguire task in un singolo tick,\nin millisecondi.\nNota, potremmo andare ben sopra questo limite, perché non c'è modo di sapere\nquanto impiega, questa configurazione mira ad essere il limite maggiore del tempo medio.\nRange: > 1",
"gui.computercraft.config.http.enabled.tooltip": "Attiva l'API \"http\" sui computer. Questo disabilita i programmi \"pastebin\" e \"wget\", \nche molti utenti dipendono. È raccomandato lasciarlo attivo ed utlizzare l'opzione \n\"rules\" per imporre controlli più adeguati.",
"gui.computercraft.config.http.rules.tooltip": "Una lista di regole che controllano il comportamento dell'API \"http\" per specifici domini\no indirizzi IP. Ogni regola è un elemento con un 'host' da confrontare, e una serie di\nproprietà. Le regole sono valutate in ordine, cioè le prime regole sovvrascrivono le successive.\nL'host può essere un dominio (\"pastebin.com\"), wildcard (\"*.pastebin.com\") oppure\nuna notazione CIDR (\"127.0.0.0/8\").\nSe non è presente nessuna regola, il dominio è bloccato.",
"gui.computercraft.config.http.tooltip": "Controlla l'API HTTP",
"gui.computercraft.config.http.websocket_enabled.tooltip": "Attiva l'uso di websocket http. Questo richiede che l'opzione \"http_enable\" sia attiva.",
"gui.computercraft.config.log_computer_errors.tooltip": "Registra le eccezioni lanciate dalle periferiche e altri oggetti di Lua. Questo rende più facile\nper gli autori di mod per il debug di problemi, ma potrebbe risultare in spam di log durante\nl'uso di metodi buggati.",
"gui.computercraft.config.monitor_renderer.tooltip": "Il renderizzatore da usare per i monitor. Generalmente si dovrebbe lasciare su \"best\", se\ni monitor hanno problemi di performance, puoi sperimentare con renderizzatori alternativi.\nValori concessi: BEST, TBO, VBO",
"gui.computercraft.config.peripheral.monitor_bandwidth.tooltip": "Il limite di quanti dati dei monitor possono essere inviati *al tick*. Nota:\n - La banda larga è misurata prima della compressione, così che il dato inviato al client è\n più piccolo."
} }

View File

@@ -0,0 +1,128 @@
{
"block.computercraft.disk_drive": "tomo pi lipu sona",
"block.computercraft.printer": "ilo sitelen",
"block.computercraft.monitor_normal": "ilo lukin",
"block.computercraft.monitor_advanced": "ilo lukin wawa",
"block.computercraft.speaker": "ilo kalama",
"commands.computercraft.generic.exception": "pakala awen ala (%s)",
"block.computercraft.computer_command": "ilo sona pi toki wawa",
"block.computercraft.turtle_advanced": "ilo sona pali wawa",
"block.computercraft.turtle_normal": "ilo sona pali",
"block.computercraft.computer_advanced": "ilo sona wawa",
"block.computercraft.computer_normal": "ilo sona",
"argument.computercraft.argument_expected": "mi wile e ijo",
"block.computercraft.turtle_advanced.upgraded": "ilo sona pali wawa (%s)",
"block.computercraft.turtle_normal.upgraded": "ilo sona pali (%s)",
"commands.computercraft.dump.action": "o lukin e sona mute pi ilo sona ni",
"commands.computercraft.generic.no_position": "<lon ala>",
"chat.computercraft.wired_modem.peripheral_connected": "ijo sona \"%s\" li linja tawa kulupu",
"chat.computercraft.wired_modem.peripheral_disconnected": "ijo sona \"%s\" li linja ala tawa kulupu",
"block.computercraft.wired_modem": "ilo toki linja",
"block.computercraft.wired_modem_full": "ilo toki linja",
"block.computercraft.wireless_modem_normal": "ilo toki linja ala",
"commands.computercraft.generic.additional_rows": "mute %d…",
"argument.computercraft.computer.no_matching": "mi ken ala alasa e ilo sona pi nimi '%s'",
"commands.computercraft.dump.open_path": "o lukin e lipu pi ilo sona ni",
"commands.computercraft.dump.synopsis": "mi pana e sona pi ilo sona.",
"block.computercraft.cable": "linja toki",
"block.computercraft.wireless_modem_advanced": "ilo toki pi ma End",
"commands.computercraft.generic.position": "%s, %s, %s",
"commands.computercraft.help.no_children": "toki wawa '%s' li jo ala e toki wawa insa",
"commands.computercraft.help.no_command": "toki wawa '%s' li lon ala",
"commands.computercraft.help.desc": "mi pana e toki ni",
"commands.computercraft.help.synopsis": "mi pana e sona pi toki wawa ale",
"commands.computercraft.reload.done": "mi kama open sin e ante nasin",
"commands.computercraft.shutdown.done": "mi kama pini e ilo sona %s/%s",
"commands.computercraft.shutdown.synopsis": "mi pini weka e ilo sona.",
"commands.computercraft.queue.synopsis": "mi pana e toki computer_command tawa ilo sona pi toki wawa",
"commands.computercraft.tp.desc": "o tawa ilo sona. ilo sona la, sina ken pana e nanpa pi ijo (sama 123) anu nanpa (sama #123).",
"commands.computercraft.synopsis": "mi jo e toki wawa ante tawa ni: sina lawa e ilo sona.",
"commands.computercraft.tp.action": "o tawa pi ilo sona",
"commands.computercraft.tp.not_player": "jan ala la, mi ken ala open e sitelen pi ilo sona",
"commands.computercraft.track.dump.computer": "ilo sona",
"commands.computercraft.tp.synopsis": "mi tawa pi ilo sona e sina.",
"commands.computercraft.tp.not_there": "mi ken ala alasa e ilo sona pi lon ma",
"commands.computercraft.track.desc": "ilo sona la, mi sitelen e tenpo pali e mute toki. toki wawa /forge track la, mi pana pi nasin sama e sona. mi pona tawa alasa pi tenpo ike.",
"commands.computercraft.track.dump.no_timings": "mi ken ala alasa e tenpo",
"commands.computercraft.track.stop.synopsis": "mi pini e sitelen sona ale",
"commands.computercraft.track.start.synopsis": "mi open sitelen sona e ilo sona ale",
"commands.computercraft.track.stop.action": "o luka tawa ni: mi pini sitelen sona",
"commands.computercraft.track.stop.not_enabled": "tenpo ni la, mi sitelen ala sona e ilo sona",
"commands.computercraft.track.dump.synopsis": "mi pana e sona tan sitelen sona pi sin mute",
"commands.computercraft.track.stop.desc": "ilo sona la, mi pini sitelen sona e tenpo pali e mute toki",
"commands.computercraft.track.synopsis": "mi sitelen tenpo pali e ilo sona.",
"commands.computercraft.track.dump.desc": "mi pana e sona tan sitelen sona pi sin mute.",
"commands.computercraft.view.action": "o lukin e ilo sona ni",
"commands.computercraft.view.synopsis": "o lukin e sitelen pi ilo sona.",
"commands.computercraft.turn_on.done": "mi kama open e ilo sona %s/%s",
"commands.computercraft.turn_on.synopsis": "mi open weka e ilo sona.",
"commands.computercraft.view.not_player": "jan ala la, mi ken ala open e sitelen pi ilo sona",
"gui.computercraft.config.command_require_creative": "ilo sona pi toki wawa li wile e jan sewi",
"gui.computercraft.config.execution.computer_threads": "linja pi ilo sona",
"gui.computercraft.config.http.bandwidth": "mute sona",
"gui.computercraft.config.http": "ijo HTTP",
"gui.computercraft.config.execution": "pali",
"gui.computercraft.config.monitor_renderer": "nasin sitelen pi ilo lukin",
"gui.computercraft.config.term_sizes.pocket_computer.width": "suli poka pi ilo sitelen",
"gui.computercraft.config.term_sizes": "suli pi ilo sitelen",
"gui.computercraft.config.term_sizes.computer": "ilo sona",
"gui.computercraft.config.term_sizes.monitor": "ilo lukin",
"gui.computercraft.config.term_sizes.pocket_computer": "ilo sona lili",
"gui.computercraft.config.term_sizes.pocket_computer.height": "suli sewi pi ilo sitelen",
"gui.computercraft.config.term_sizes.computer.height": "suli sewi pi ilo sitelen",
"gui.computercraft.config.term_sizes.computer.width": "suli poka pi ito sitelen",
"gui.computercraft.config.term_sizes.computer.tooltip": "suli pi ilo sitelen pi ilo sona.",
"gui.computercraft.config.peripheral.monitor_bandwidth": "mute sona pi ilo lukin",
"gui.computercraft.config.peripheral.modem_range": "suli ken ilo pi ilo toki (pona)",
"gui.computercraft.config.peripheral.modem_high_altitude_range": "suli ken ilo pi ilo toki (sewi mute)",
"item.computercraft.printed_page": "lipu pi ilo sitelen",
"item.computercraft.printed_pages": "lipu mute pi ilo sitelen",
"item.computercraft.treasure_disk": "lipu sona",
"gui.computercraft.tooltip.turn_on": "o open e ilo sona ni",
"gui.computercraft.tooltip.computer_id": "nanpa pi ilo sona: %s",
"gui.computercraft.tooltip.turn_off": "o pini e ilo sona ni",
"item.computercraft.pocket_computer_advanced": "ilo sona lili wawa",
"item.computercraft.pocket_computer_advanced.upgraded": "ilo sona lili wawa (%s)",
"item.computercraft.pocket_computer_normal": "ilo sona lili",
"item.computercraft.pocket_computer_normal.upgraded": "ilo sona lili (%s)",
"gui.computercraft.terminal": "ilo sitelen pi ilo sona",
"itemGroup.computercraft": "ComputerCraft",
"tracking_field.computercraft.computer_tasks.name": "pali",
"tracking_field.computercraft.turtle_ops.name": "pali pi ilo sona pali",
"upgrade.computercraft.speaker.adjective": "kalama",
"tracking_field.computercraft.count": "%s (mute)",
"tracking_field.computercraft.coroutines_created.name": "open linja",
"tracking_field.computercraft.coroutines_dead.name": "pini linja",
"tracking_field.computercraft.max": "%s (sewi)",
"upgrade.minecraft.crafting_table.adjective": "pali",
"upgrade.minecraft.diamond_axe.adjective": "ilo kipisi",
"upgrade.minecraft.diamond_hoe.adjective": "ilo kasi",
"upgrade.computercraft.wireless_modem_normal.adjective": "toki linja ala",
"upgrade.minecraft.diamond_pickaxe.adjective": "ilo pakala",
"upgrade.minecraft.diamond_shovel.adjective": "ilo ma",
"upgrade.minecraft.diamond_sword.adjective": "utala",
"block.computercraft.turtle_advanced.upgraded_twice": "ilo sona pali wawa (%s, %s)",
"block.computercraft.turtle_normal.upgraded_twice": "ilo sona pali (%s, %s)",
"gui.computercraft.config.turtle": "ilo sona pali",
"item.computercraft.disk": "lipu sona",
"item.computercraft.printed_book": "lipu suli pi ilo sitelen",
"upgrade.computercraft.wireless_modem_advanced.adjective": "toki pi ma End",
"gui.computercraft.tooltip.disk_id": "nanpa pi lipu sona: %s",
"gui.computercraft.config.peripheral": "ijo sona",
"gui.computercraft.config.peripheral.modem_range_during_storm": "suli ken ilo pi ilo toki (sewi ike)",
"argument.computercraft.tracking_field.no_field": "mi sona ala e nimi '%s'",
"argument.computercraft.computer.many_matching": "mi alasa e ilo sona mute pi nimi '%s' (ijo %s)",
"commands.computercraft.desc": "ilo sona la, toki wawa /computercraft li lawa li luka li pakala ala.",
"commands.computercraft.reload.desc": "mi open sin e ante nasin",
"commands.computercraft.reload.synopsis": "mi open sin e ante nasin",
"commands.computercraft.shutdown.desc": "mi pini e ilo sona pi pana sina anu ale. ilo sona la, sina ken pana e nanpa pi ijo (sama 123) anu nanpa (sama #123) anu nimi (sama \"@ilo sona mi\").",
"gui.computercraft.config.term_sizes.pocket_computer.tooltip": "suli pi ilo sitelen pi ilo sona lili.",
"gui.computercraft.config.peripheral.modem_high_altitude_range_during_storm": "suli ken ilo pi ilo toki (sewi mute, sewi ike)",
"gui.computercraft.pocket_computer_overlay": "sina open e ilo sona lili. o luka e nena ESC tawa pini.",
"commands.computercraft.dump.desc": "sona pi ilo sona la, mi pana e ale anu wan. ilo sona la, sina ken pana e nanpa pi ijo (sama 123) anu nanpa (sama #123) anu nimi (sama \"@ilo sona mi\").",
"commands.computercraft.turn_on.desc": "mi open e ilo sona pi pana sina. ilo sona la, sina ken pana e nanpa pi ijo (sama 123) anu nanpa (sama #123) anu nimi (sama \"@ilo sona mi\").",
"commands.computercraft.track.start.stop": "o toki wawa %s tawa ni: sina pini sitelen sona. sina lukin e sona ni",
"commands.computercraft.track.start.desc": "ilo sona la, mi open sitelen sona e tenpo pali e mute toki. mi weka e sitelen sona pini.",
"commands.computercraft.view.desc": "mi open e sitelen pi ilo sona tawa ni: sina lawa weka e ilo sona. taso, sina ken ala open e jo pi ilo sona pali. ilo sona la, sina ken pana e nanpa pi ijo (sama 123) anu nanpa (sama #123).",
"gui.computercraft.config.disable_lua51_features": "ijo pona pi ilo Lua 5.1 li weka"
}

View File

@@ -6,6 +6,7 @@
package dan200.computercraft; package dan200.computercraft;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.network.wired.WiredElement;
@@ -77,6 +78,17 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat
throw new UnsupportedOperationException("Cannot query registry inside tests"); throw new UnsupportedOperationException("Cannot query registry inside tests");
} }
@Nullable
@Override
public <T> T tryGetRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id) {
throw new UnsupportedOperationException("Cannot query registries");
}
@Override
public boolean shouldLoadResource(JsonObject object) {
throw new UnsupportedOperationException("Cannot use loot conditions");
}
@Override @Override
public <T extends BlockEntity> BlockEntityType<T> createBlockEntityType(BiFunction<BlockPos, BlockState, T> factory, Block block) { public <T extends BlockEntity> BlockEntityType<T> createBlockEntityType(BiFunction<BlockPos, BlockState, T> factory, Block block) {
throw new UnsupportedOperationException("Cannot create BlockEntityType inside tests"); throw new UnsupportedOperationException("Cannot create BlockEntityType inside tests");
@@ -219,10 +231,4 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat
public String getInstalledVersion() { public String getInstalledVersion() {
return "1.0"; return "1.0";
} }
@Nullable
@Override
public <T> T tryGetRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id) {
throw new UnsupportedOperationException("Cannot query registries");
}
} }

View File

@@ -0,0 +1,38 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.util;
import dan200.computercraft.test.shared.WithMinecraft;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import org.junit.jupiter.api.Test;
import static dan200.computercraft.test.shared.ItemStackMatcher.isStack;
import static org.hamcrest.MatcherAssert.assertThat;
@WithMinecraft
public class InventoryUtilTest {
@Test
public void testStoreOffset() {
var container = new SimpleContainer(9);
var remainder = InventoryUtil.storeItemsFromOffset(container, new ItemStack(Items.COBBLESTONE, 32), 4);
assertThat("Remainder is empty", remainder, isStack(ItemStack.EMPTY));
assertThat("Was inserted into slot", container.getItem(4), isStack(new ItemStack(Items.COBBLESTONE, 32)));
}
@Test
public void testStoreOffsetWraps() {
var container = new SimpleContainer(9);
container.setItem(0, new ItemStack(Items.DIRT));
for (var slot = 4; slot < 9; slot++) container.setItem(slot, new ItemStack(Items.DIRT));
var remainder = InventoryUtil.storeItemsFromOffset(container, new ItemStack(Items.COBBLESTONE, 32), 4);
assertThat("Remainder is empty", remainder, isStack(ItemStack.EMPTY));
assertThat("Was inserted into slot", container.getItem(1), isStack(new ItemStack(Items.COBBLESTONE, 32)));
}
}

View File

@@ -130,6 +130,22 @@ public interface ContainerTransferContract {
assertNoOverlap(source, destination); assertNoOverlap(source, destination);
} }
@Test
default void testMoveRotateWraps() {
var source = new SimpleContainer(1);
source.setItem(0, new ItemStack(Items.COBBLESTONE, 32));
var destination = new SimpleContainer(9);
destination.setItem(0, new ItemStack(Items.DIRT));
for (var slot = 4; slot < 9; slot++) destination.setItem(slot, new ItemStack(Items.DIRT));
var move = wrap(source).moveTo(wrap(destination).rotate(4), 64);
assertEquals(32, move);
assertThat("Source is empty", source.getItem(0), isStack(ItemStack.EMPTY));
assertThat("Was inserted into slot", destination.getItem(1), isStack(new ItemStack(Items.COBBLESTONE, 32)));
}
static void assertNoOverlap(Container... containers) { static void assertNoOverlap(Container... containers) {
Set<ItemStack> stacks = Collections.newSetFromMap(new IdentityHashMap<>()); Set<ItemStack> stacks = Collections.newSetFromMap(new IdentityHashMap<>());
for (var container : containers) { for (var container : containers) {

View File

@@ -8,6 +8,7 @@ package dan200.computercraft.gametest.core;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.mixin.gametest.TestCommandAccessor; import dan200.computercraft.mixin.gametest.TestCommandAccessor;
import dan200.computercraft.shared.ModRegistry;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.gametest.framework.GameTestRegistry; import net.minecraft.gametest.framework.GameTestRegistry;
@@ -81,6 +82,27 @@ class CCTestCommand {
player.getLevel().addFreshEntity(armorStand); player.getLevel().addFreshEntity(armorStand);
return 0; return 0;
})) }))
.then(literal("give-computer").executes(context -> {
var player = context.getSource().getPlayerOrException();
var pos = StructureUtils.findNearestStructureBlock(player.blockPosition(), 15, player.getLevel());
if (pos == null) return error(context.getSource(), "No nearby test");
var structureBlock = (StructureBlockEntity) player.getLevel().getBlockEntity(pos);
if (structureBlock == null) return error(context.getSource(), "No nearby structure block");
var info = GameTestRegistry.getTestFunction(structureBlock.getStructurePath());
var item = ModRegistry.Items.COMPUTER_ADVANCED.get().create(1, info.getTestName());
if (!player.getInventory().add(item)) {
var itemEntity = player.drop(item, false);
if (itemEntity != null) {
itemEntity.setNoPickUpDelay();
itemEntity.setOwner(player.getUUID());
}
}
return 1;
}))
); );
} }

View File

@@ -8,6 +8,7 @@ package dan200.computercraft.gametest
import dan200.computercraft.core.apis.FSAPI import dan200.computercraft.core.apis.FSAPI
import dan200.computercraft.gametest.api.* import dan200.computercraft.gametest.api.*
import dan200.computercraft.shared.ModRegistry import dan200.computercraft.shared.ModRegistry
import dan200.computercraft.shared.media.items.DiskItem
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlock import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlock
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveState import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveState
import dan200.computercraft.test.core.assertArrayEquals import dan200.computercraft.test.core.assertArrayEquals
@@ -45,10 +46,14 @@ class Disk_Drive_Test {
thenWaitUntil { helper.assertItemEntityPresent(Items.MUSIC_DISC_13, stackAt, 0.0) } thenWaitUntil { helper.assertItemEntityPresent(Items.MUSIC_DISC_13, stackAt, 0.0) }
} }
/**
* A mount is initially attached, and then removed when the disk is ejected.
*/
@GameTest @GameTest
fun Adds_removes_mount(helper: GameTestHelper) = helper.sequence { fun Adds_removes_mount(helper: GameTestHelper) = helper.sequence {
thenIdle(2) thenOnComputer { } // Wait for the computer to start up
thenOnComputer { thenIdle(2) // Let the disk drive tick once to create the mount
thenOnComputer { // Then actually assert things!
getApi<FSAPI>().getDrive("disk").assertArrayEquals("right") getApi<FSAPI>().getDrive("disk").assertArrayEquals("right")
callPeripheral("right", "ejectDisk") callPeripheral("right", "ejectDisk")
} }
@@ -56,6 +61,18 @@ class Disk_Drive_Test {
thenOnComputer { assertEquals(null, getApi<FSAPI>().getDrive("disk")) } thenOnComputer { assertEquals(null, getApi<FSAPI>().getDrive("disk")) }
} }
/**
* When creating a new mount, the item is with a new disk ID.
*/
@GameTest
fun Creates_disk_id(helper: GameTestHelper) = helper.sequence {
val drivePos = BlockPos(2, 2, 2)
thenWaitUntil {
val drive = helper.getBlockEntity(drivePos, ModRegistry.BlockEntities.DISK_DRIVE.get())
if (DiskItem.getDiskID(drive.getItem(0)) == -1) helper.fail("Disk has no item", drivePos)
}
}
/** /**
* Check comparators can read the contents of the disk drive * Check comparators can read the contents of the disk drive
*/ */

View File

@@ -66,4 +66,19 @@ class Inventory_Test {
helper.assertContainerExactly(BlockPos(3, 2, 2), NonNullList.withSize(27, ItemStack(Items.POLISHED_ANDESITE))) helper.assertContainerExactly(BlockPos(3, 2, 2), NonNullList.withSize(27, ItemStack(Items.POLISHED_ANDESITE)))
} }
} }
/**
* Ensures inventory methods see a complete double chest
*
* @see <https://github.com/cc-tweaked/CC-Tweaked/issues/1279>
*/
@GameTest
fun Double_chest_size(helper: GameTestHelper) = helper.sequence {
thenOnComputer {
getApi<PeripheralAPI>().call(context, ObjectArguments("left", "size")).await()
.assertArrayEquals(54, message = "Has 54 slots")
getApi<PeripheralAPI>().call(context, ObjectArguments("left", "pushItems", "right", 1)).await()
.assertArrayEquals(32, message = "Moved 32 items into a double chest")
}
}
} }

View File

@@ -70,6 +70,20 @@ class Modem_Test {
.assertArrayEquals("modem_message", "left", 12, 34, "Hello", 4, message = "Modem message") .assertArrayEquals("modem_message", "left", 12, 34, "Hello", 4, message = "Modem message")
} }
} }
/**
* Assert that full block modems act like cables.
*
* @see [#1278](https://github.com/cc-tweaked/CC-Tweaked/issues/1278)
*/
@GameTest(setupTicks = 1)
fun Full_modems_form_networks(helper: GameTestHelper) = helper.sequence {
thenExecute {
val modem1 = helper.getBlockEntity(BlockPos(1, 2, 1), ModRegistry.BlockEntities.WIRED_MODEM_FULL.get())
val modem2 = helper.getBlockEntity(BlockPos(3, 2, 1), ModRegistry.BlockEntities.WIRED_MODEM_FULL.get())
assertEquals(modem1.element.node.network, modem2.element.node.network, "On the same network")
}
}
} }
private fun LuaTaskContext.findPeripheral(type: String): String? { private fun LuaTaskContext.findPeripheral(type: String): String? {

View File

@@ -63,7 +63,7 @@ class Monitor_Test {
} }
/** /**
* Test * Test monitors render correctly
*/ */
@GameTestGenerator @GameTestGenerator
fun Render_monitor_tests(): List<TestFunction> { fun Render_monitor_tests(): List<TestFunction> {
@@ -72,7 +72,7 @@ class Monitor_Test {
fun addTest(label: String, renderer: MonitorRenderer, time: Long = Times.NOON, tag: String = TestTags.CLIENT) { fun addTest(label: String, renderer: MonitorRenderer, time: Long = Times.NOON, tag: String = TestTags.CLIENT) {
if (!TestTags.isEnabled(tag)) return if (!TestTags.isEnabled(tag)) return
val className = Monitor_Test::class.java.simpleName.lowercase() val className = this::class.java.simpleName.lowercase()
val testName = "$className.render_monitor" val testName = "$className.render_monitor"
tests.add( tests.add(

View File

@@ -0,0 +1,53 @@
package dan200.computercraft.gametest
import dan200.computercraft.gametest.api.*
import net.minecraft.gametest.framework.GameTestGenerator
import net.minecraft.gametest.framework.GameTestHelper
import net.minecraft.gametest.framework.TestFunction
class Printout_Test {
/**
* Test printouts render correctly
*/
@GameTestGenerator
fun Render_in_frame(): List<TestFunction> {
val tests = mutableListOf<TestFunction>()
fun addTest(label: String, time: Long = Times.NOON, tag: String = TestTags.CLIENT) {
if (!TestTags.isEnabled(tag)) return
val className = this::class.java.simpleName.lowercase()
val testName = "$className.render_in_frame"
tests.add(
TestFunction(
"$testName.$label",
"$testName.$label",
testName,
Timeouts.DEFAULT,
0,
true,
) { renderPrintout(it, time) },
)
}
addTest("noon", Times.NOON)
addTest("midnight", Times.MIDNIGHT)
addTest("sodium", tag = "sodium")
addTest("iris_noon", Times.NOON, tag = "iris")
addTest("iris_midnight", Times.MIDNIGHT, tag = "iris")
return tests
}
private fun renderPrintout(helper: GameTestHelper, time: Long) = helper.sequence {
thenExecute {
helper.level.dayTime = time
helper.positionAtArmorStand()
}
thenScreenshot()
}
}

View File

@@ -15,6 +15,8 @@ import dan200.computercraft.mixin.gametest.GameTestHelperAccessor
import dan200.computercraft.mixin.gametest.GameTestInfoAccessor import dan200.computercraft.mixin.gametest.GameTestInfoAccessor
import dan200.computercraft.shared.ModRegistry import dan200.computercraft.shared.ModRegistry
import dan200.computercraft.shared.media.items.PrintoutItem import dan200.computercraft.shared.media.items.PrintoutItem
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant
import dan200.computercraft.shared.peripheral.monitor.MonitorBlock import dan200.computercraft.shared.peripheral.monitor.MonitorBlock
import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState
import dan200.computercraft.shared.turtle.apis.TurtleAPI import dan200.computercraft.shared.turtle.apis.TurtleAPI
@@ -89,7 +91,7 @@ class Turtle_Test {
.assertArrayEquals(true, message = "Placed oak fence") .assertArrayEquals(true, message = "Placed oak fence")
} }
thenExecute { thenExecute {
helper.assertBlockIs(BlockPos(2, 2, 2), { it.block == Blocks.OAK_FENCE && it.getValue(FenceBlock.WATERLOGGED) }) helper.assertBlockIs(BlockPos(2, 2, 2)) { it.block == Blocks.OAK_FENCE && it.getValue(FenceBlock.WATERLOGGED) }
} }
} }
@@ -123,6 +125,33 @@ class Turtle_Test {
thenExecute { helper.assertBlockPresent(Blocks.FARMLAND, BlockPos(1, 2, 1)) } thenExecute { helper.assertBlockPresent(Blocks.FARMLAND, BlockPos(1, 2, 1)) }
} }
/**
* Checks turtles break cables in two parts.
*/
@GameTest
fun Break_cable(helper: GameTestHelper) = helper.sequence {
thenOnComputer { turtle.dig(Optional.empty()).await() }
thenExecute {
helper.assertBlockIs(BlockPos(2, 2, 3)) {
it.block == ModRegistry.Blocks.CABLE.get() && !it.getValue(CableBlock.CABLE) && it.getValue(CableBlock.MODEM) == CableModemVariant.DownOff
}
helper.assertContainerExactly(BlockPos(2, 2, 2), listOf(ItemStack(ModRegistry.Items.CABLE.get())))
}
thenOnComputer { turtle.dig(Optional.empty()).await().assertArrayEquals(true) }
thenExecute {
helper.assertBlockPresent(Blocks.AIR, BlockPos(2, 2, 3))
helper.assertContainerExactly(
BlockPos(2, 2, 2),
listOf(
ItemStack(ModRegistry.Items.CABLE.get()),
ItemStack(ModRegistry.Items.WIRED_MODEM.get()),
),
)
}
}
/** /**
* Checks turtles can place monitors * Checks turtles can place monitors
* *
@@ -289,6 +318,23 @@ class Turtle_Test {
} }
} }
/**
* Test calling `turtle.refuel` on non-fuels
*/
@GameTest
fun Refuel_fail(helper: GameTestHelper) = helper.sequence {
val turtlePos = BlockPos(2, 2, 2)
thenOnComputer {
assertEquals(0, turtle.fuelLevel)
turtle.refuel(Optional.empty()).await().assertArrayEquals(false, "Items not combustible")
assertEquals(0, turtle.fuelLevel)
}
thenExecute {
helper.assertContainerExactly(turtlePos, listOf(ItemStack(Items.DIRT, 32)))
}
}
/** /**
* Test calling `turtle.refuel` with a bucket of lava * Test calling `turtle.refuel` with a bucket of lava
*/ */
@@ -307,6 +353,26 @@ class Turtle_Test {
} }
} }
/**
* Test moving a turtle forwards preserves the turtle's inventory.
*
* @see [#1276](https://github.com/cc-tweaked/CC-Tweaked/pull/1276)
*/
@GameTest
fun Move_preserves_state(helper: GameTestHelper) = helper.sequence {
thenOnComputer { turtle.forward().await().assertArrayEquals(true, message = "Turtle moved forward") }
thenExecute {
helper.assertContainerExactly(BlockPos(2, 2, 3), listOf(ItemStack(Items.DIRT, 32)))
val turtle = helper.getBlockEntity(BlockPos(2, 2, 3), ModRegistry.BlockEntities.TURTLE_NORMAL.get())
assertEquals(1, turtle.computerID)
assertEquals("turtle_test.move_preserves_state", turtle.label)
assertEquals(79, turtle.access.fuelLevel)
helper.assertEntityNotPresent(EntityType.ITEM)
}
}
/** /**
* Test turtles are not obstructed by plants and instead replace them. * Test turtles are not obstructed by plants and instead replace them.
*/ */
@@ -329,7 +395,7 @@ class Turtle_Test {
thenOnComputer { turtle.forward().await().assertArrayEquals(true, message = "Turtle moved forward") } thenOnComputer { turtle.forward().await().assertArrayEquals(true, message = "Turtle moved forward") }
thenExecute { thenExecute {
// Assert we're no longer waterlogged and we've left a source block. // Assert we're no longer waterlogged and we've left a source block.
helper.assertBlockIs(BlockPos(2, 2, 2), { it.block == Blocks.WATER && it.fluidState.isSource }) helper.assertBlockIs(BlockPos(2, 2, 2)) { it.block == Blocks.WATER && it.fluidState.isSource }
helper.assertBlockHas(BlockPos(2, 2, 3), WaterloggableHelpers.WATERLOGGED, false) helper.assertBlockHas(BlockPos(2, 2, 3), WaterloggableHelpers.WATERLOGGED, false)
} }
} }

View File

@@ -143,7 +143,12 @@ private fun GameTestHelper.fail(message: String?, detail: String, pos: BlockPos)
/** /**
* A version of [GameTestHelper.assertBlockState] which also includes the current block state. * A version of [GameTestHelper.assertBlockState] which also includes the current block state.
*/ */
fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boolean, message: String = "") { fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boolean) = assertBlockIs(pos, predicate, "")
/**
* A version of [GameTestHelper.assertBlockState] which also includes the current block state.
*/
fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boolean, message: String) {
val state = getBlockState(pos) val state = getBlockState(pos)
if (!predicate(state)) fail(message, state.toString(), pos) if (!predicate(state)) fail(message, state.toString(), pos)
} }

View File

@@ -80,6 +80,7 @@ object TestHooks {
Monitor_Test::class.java, Monitor_Test::class.java,
Pocket_Computer_Test::class.java, Pocket_Computer_Test::class.java,
Printer_Test::class.java, Printer_Test::class.java,
Printout_Test::class.java,
Recipe_Test::class.java, Recipe_Test::class.java,
Turtle_Test::class.java, Turtle_Test::class.java,
) )

View File

@@ -0,0 +1,138 @@
{
DataVersion: 3218,
size: [5, 5, 5],
data: [
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
{pos: [0, 1, 0], state: "minecraft:air"},
{pos: [0, 1, 1], state: "minecraft:air"},
{pos: [0, 1, 2], state: "minecraft:air"},
{pos: [0, 1, 3], state: "minecraft:air"},
{pos: [0, 1, 4], state: "minecraft:air"},
{pos: [1, 1, 0], state: "minecraft:air"},
{pos: [1, 1, 1], state: "minecraft:air"},
{pos: [1, 1, 2], state: "computercraft:computer_advanced{facing:north,state:on}", nbt: {ComputerId: 1, Label: "disk_drive_test.creates_disk_id", On: 1b, id: "computercraft:computer_advanced"}},
{pos: [1, 1, 3], state: "minecraft:air"},
{pos: [1, 1, 4], state: "minecraft:air"},
{pos: [2, 1, 0], state: "minecraft:air"},
{pos: [2, 1, 1], state: "minecraft:air"},
{pos: [2, 1, 2], state: "computercraft:disk_drive{facing:north,state:full}", nbt: {Item: {Count: 1b, id: "computercraft:disk", tag: {Color: 1118481}}, id: "computercraft:disk_drive"}},
{pos: [2, 1, 3], state: "minecraft:air"},
{pos: [2, 1, 4], state: "minecraft:air"},
{pos: [3, 1, 0], state: "minecraft:air"},
{pos: [3, 1, 1], state: "minecraft:air"},
{pos: [3, 1, 2], state: "minecraft:air"},
{pos: [3, 1, 3], state: "minecraft:air"},
{pos: [3, 1, 4], state: "minecraft:air"},
{pos: [4, 1, 0], state: "minecraft:air"},
{pos: [4, 1, 1], state: "minecraft:air"},
{pos: [4, 1, 2], state: "minecraft:air"},
{pos: [4, 1, 3], state: "minecraft:air"},
{pos: [4, 1, 4], state: "minecraft:air"},
{pos: [0, 2, 0], state: "minecraft:air"},
{pos: [0, 2, 1], state: "minecraft:air"},
{pos: [0, 2, 2], state: "minecraft:air"},
{pos: [0, 2, 3], state: "minecraft:air"},
{pos: [0, 2, 4], state: "minecraft:air"},
{pos: [1, 2, 0], state: "minecraft:air"},
{pos: [1, 2, 1], state: "minecraft:air"},
{pos: [1, 2, 2], state: "minecraft:air"},
{pos: [1, 2, 3], state: "minecraft:air"},
{pos: [1, 2, 4], state: "minecraft:air"},
{pos: [2, 2, 0], state: "minecraft:air"},
{pos: [2, 2, 1], state: "minecraft:air"},
{pos: [2, 2, 2], state: "minecraft:air"},
{pos: [2, 2, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:air"},
{pos: [3, 2, 0], state: "minecraft:air"},
{pos: [3, 2, 1], state: "minecraft:air"},
{pos: [3, 2, 2], state: "minecraft:air"},
{pos: [3, 2, 3], state: "minecraft:air"},
{pos: [3, 2, 4], state: "minecraft:air"},
{pos: [4, 2, 0], state: "minecraft:air"},
{pos: [4, 2, 1], state: "minecraft:air"},
{pos: [4, 2, 2], state: "minecraft:air"},
{pos: [4, 2, 3], state: "minecraft:air"},
{pos: [4, 2, 4], state: "minecraft:air"},
{pos: [0, 3, 0], state: "minecraft:air"},
{pos: [0, 3, 1], state: "minecraft:air"},
{pos: [0, 3, 2], state: "minecraft:air"},
{pos: [0, 3, 3], state: "minecraft:air"},
{pos: [0, 3, 4], state: "minecraft:air"},
{pos: [1, 3, 0], state: "minecraft:air"},
{pos: [1, 3, 1], state: "minecraft:air"},
{pos: [1, 3, 2], state: "minecraft:air"},
{pos: [1, 3, 3], state: "minecraft:air"},
{pos: [1, 3, 4], state: "minecraft:air"},
{pos: [2, 3, 0], state: "minecraft:air"},
{pos: [2, 3, 1], state: "minecraft:air"},
{pos: [2, 3, 2], state: "minecraft:air"},
{pos: [2, 3, 3], state: "minecraft:air"},
{pos: [2, 3, 4], state: "minecraft:air"},
{pos: [3, 3, 0], state: "minecraft:air"},
{pos: [3, 3, 1], state: "minecraft:air"},
{pos: [3, 3, 2], state: "minecraft:air"},
{pos: [3, 3, 3], state: "minecraft:air"},
{pos: [3, 3, 4], state: "minecraft:air"},
{pos: [4, 3, 0], state: "minecraft:air"},
{pos: [4, 3, 1], state: "minecraft:air"},
{pos: [4, 3, 2], state: "minecraft:air"},
{pos: [4, 3, 3], state: "minecraft:air"},
{pos: [4, 3, 4], state: "minecraft:air"},
{pos: [0, 4, 0], state: "minecraft:air"},
{pos: [0, 4, 1], state: "minecraft:air"},
{pos: [0, 4, 2], state: "minecraft:air"},
{pos: [0, 4, 3], state: "minecraft:air"},
{pos: [0, 4, 4], state: "minecraft:air"},
{pos: [1, 4, 0], state: "minecraft:air"},
{pos: [1, 4, 1], state: "minecraft:air"},
{pos: [1, 4, 2], state: "minecraft:air"},
{pos: [1, 4, 3], state: "minecraft:air"},
{pos: [1, 4, 4], state: "minecraft:air"},
{pos: [2, 4, 0], state: "minecraft:air"},
{pos: [2, 4, 1], state: "minecraft:air"},
{pos: [2, 4, 2], state: "minecraft:air"},
{pos: [2, 4, 3], state: "minecraft:air"},
{pos: [2, 4, 4], state: "minecraft:air"},
{pos: [3, 4, 0], state: "minecraft:air"},
{pos: [3, 4, 1], state: "minecraft:air"},
{pos: [3, 4, 2], state: "minecraft:air"},
{pos: [3, 4, 3], state: "minecraft:air"},
{pos: [3, 4, 4], state: "minecraft:air"},
{pos: [4, 4, 0], state: "minecraft:air"},
{pos: [4, 4, 1], state: "minecraft:air"},
{pos: [4, 4, 2], state: "minecraft:air"},
{pos: [4, 4, 3], state: "minecraft:air"},
{pos: [4, 4, 4], state: "minecraft:air"}
],
entities: [],
palette: [
"minecraft:polished_andesite",
"minecraft:air",
"computercraft:computer_advanced{facing:north,state:on}",
"computercraft:disk_drive{facing:north,state:full}"
]
}

View File

@@ -0,0 +1,139 @@
{
DataVersion: 3218,
size: [5, 5, 5],
data: [
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
{pos: [0, 1, 0], state: "minecraft:air"},
{pos: [0, 1, 1], state: "minecraft:air"},
{pos: [0, 1, 2], state: "minecraft:chest{facing:north,type:left,waterlogged:false}", nbt: {Items: [], id: "minecraft:chest"}},
{pos: [0, 1, 3], state: "minecraft:air"},
{pos: [0, 1, 4], state: "minecraft:air"},
{pos: [1, 1, 0], state: "minecraft:air"},
{pos: [1, 1, 1], state: "minecraft:air"},
{pos: [1, 1, 2], state: "minecraft:chest{facing:north,type:right,waterlogged:false}", nbt: {Items: [{Count: 64b, Slot: 0b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 1b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 2b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 3b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 4b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 5b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 6b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 7b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 8b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 9b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 10b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 11b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 12b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 13b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 14b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 15b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 16b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 17b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 18b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 19b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 20b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 21b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 22b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 23b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 24b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 25b, id: "minecraft:polished_andesite"}, {Count: 64b, Slot: 26b, id: "minecraft:polished_andesite"}], id: "minecraft:chest"}},
{pos: [1, 1, 3], state: "minecraft:air"},
{pos: [1, 1, 4], state: "minecraft:air"},
{pos: [2, 1, 0], state: "minecraft:air"},
{pos: [2, 1, 1], state: "minecraft:air"},
{pos: [2, 1, 2], state: "computercraft:computer_advanced{facing:north,state:on}", nbt: {ComputerId: 1, Label: "inventory_test.double_chest_size", On: 1b, id: "computercraft:computer_advanced"}},
{pos: [2, 1, 3], state: "minecraft:air"},
{pos: [2, 1, 4], state: "minecraft:air"},
{pos: [3, 1, 0], state: "minecraft:air"},
{pos: [3, 1, 1], state: "minecraft:air"},
{pos: [3, 1, 2], state: "minecraft:chest{facing:north,type:left,waterlogged:false}", nbt: {Items: [], id: "minecraft:chest"}},
{pos: [3, 1, 3], state: "minecraft:air"},
{pos: [3, 1, 4], state: "minecraft:air"},
{pos: [4, 1, 0], state: "minecraft:air"},
{pos: [4, 1, 1], state: "minecraft:air"},
{pos: [4, 1, 2], state: "minecraft:chest{facing:north,type:right,waterlogged:false}", nbt: {Items: [{Count: 32b, Slot: 0b, id: "minecraft:dirt"}], id: "minecraft:chest"}},
{pos: [4, 1, 3], state: "minecraft:air"},
{pos: [4, 1, 4], state: "minecraft:air"},
{pos: [0, 2, 0], state: "minecraft:air"},
{pos: [0, 2, 1], state: "minecraft:air"},
{pos: [0, 2, 2], state: "minecraft:air"},
{pos: [0, 2, 3], state: "minecraft:air"},
{pos: [0, 2, 4], state: "minecraft:air"},
{pos: [1, 2, 0], state: "minecraft:air"},
{pos: [1, 2, 1], state: "minecraft:air"},
{pos: [1, 2, 2], state: "minecraft:air"},
{pos: [1, 2, 3], state: "minecraft:air"},
{pos: [1, 2, 4], state: "minecraft:air"},
{pos: [2, 2, 0], state: "minecraft:air"},
{pos: [2, 2, 1], state: "minecraft:air"},
{pos: [2, 2, 2], state: "minecraft:air"},
{pos: [2, 2, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:air"},
{pos: [3, 2, 0], state: "minecraft:air"},
{pos: [3, 2, 1], state: "minecraft:air"},
{pos: [3, 2, 2], state: "minecraft:air"},
{pos: [3, 2, 3], state: "minecraft:air"},
{pos: [3, 2, 4], state: "minecraft:air"},
{pos: [4, 2, 0], state: "minecraft:air"},
{pos: [4, 2, 1], state: "minecraft:air"},
{pos: [4, 2, 2], state: "minecraft:air"},
{pos: [4, 2, 3], state: "minecraft:air"},
{pos: [4, 2, 4], state: "minecraft:air"},
{pos: [0, 3, 0], state: "minecraft:air"},
{pos: [0, 3, 1], state: "minecraft:air"},
{pos: [0, 3, 2], state: "minecraft:air"},
{pos: [0, 3, 3], state: "minecraft:air"},
{pos: [0, 3, 4], state: "minecraft:air"},
{pos: [1, 3, 0], state: "minecraft:air"},
{pos: [1, 3, 1], state: "minecraft:air"},
{pos: [1, 3, 2], state: "minecraft:air"},
{pos: [1, 3, 3], state: "minecraft:air"},
{pos: [1, 3, 4], state: "minecraft:air"},
{pos: [2, 3, 0], state: "minecraft:air"},
{pos: [2, 3, 1], state: "minecraft:air"},
{pos: [2, 3, 2], state: "minecraft:air"},
{pos: [2, 3, 3], state: "minecraft:air"},
{pos: [2, 3, 4], state: "minecraft:air"},
{pos: [3, 3, 0], state: "minecraft:air"},
{pos: [3, 3, 1], state: "minecraft:air"},
{pos: [3, 3, 2], state: "minecraft:air"},
{pos: [3, 3, 3], state: "minecraft:air"},
{pos: [3, 3, 4], state: "minecraft:air"},
{pos: [4, 3, 0], state: "minecraft:air"},
{pos: [4, 3, 1], state: "minecraft:air"},
{pos: [4, 3, 2], state: "minecraft:air"},
{pos: [4, 3, 3], state: "minecraft:air"},
{pos: [4, 3, 4], state: "minecraft:air"},
{pos: [0, 4, 0], state: "minecraft:air"},
{pos: [0, 4, 1], state: "minecraft:air"},
{pos: [0, 4, 2], state: "minecraft:air"},
{pos: [0, 4, 3], state: "minecraft:air"},
{pos: [0, 4, 4], state: "minecraft:air"},
{pos: [1, 4, 0], state: "minecraft:air"},
{pos: [1, 4, 1], state: "minecraft:air"},
{pos: [1, 4, 2], state: "minecraft:air"},
{pos: [1, 4, 3], state: "minecraft:air"},
{pos: [1, 4, 4], state: "minecraft:air"},
{pos: [2, 4, 0], state: "minecraft:air"},
{pos: [2, 4, 1], state: "minecraft:air"},
{pos: [2, 4, 2], state: "minecraft:air"},
{pos: [2, 4, 3], state: "minecraft:air"},
{pos: [2, 4, 4], state: "minecraft:air"},
{pos: [3, 4, 0], state: "minecraft:air"},
{pos: [3, 4, 1], state: "minecraft:air"},
{pos: [3, 4, 2], state: "minecraft:air"},
{pos: [3, 4, 3], state: "minecraft:air"},
{pos: [3, 4, 4], state: "minecraft:air"},
{pos: [4, 4, 0], state: "minecraft:air"},
{pos: [4, 4, 1], state: "minecraft:air"},
{pos: [4, 4, 2], state: "minecraft:air"},
{pos: [4, 4, 3], state: "minecraft:air"},
{pos: [4, 4, 4], state: "minecraft:air"}
],
entities: [],
palette: [
"minecraft:polished_andesite",
"minecraft:air",
"minecraft:chest{facing:north,type:left,waterlogged:false}",
"minecraft:chest{facing:north,type:right,waterlogged:false}",
"computercraft:computer_advanced{facing:north,state:on}"
]
}

View File

@@ -0,0 +1,137 @@
{
DataVersion: 3218,
size: [5, 5, 5],
data: [
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
{pos: [0, 1, 0], state: "minecraft:air"},
{pos: [0, 1, 1], state: "minecraft:air"},
{pos: [0, 1, 2], state: "minecraft:air"},
{pos: [0, 1, 3], state: "minecraft:air"},
{pos: [0, 1, 4], state: "minecraft:air"},
{pos: [1, 1, 0], state: "minecraft:air"},
{pos: [1, 1, 1], state: "computercraft:wired_modem_full{modem:false,peripheral:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:wired_modem_full"}},
{pos: [1, 1, 2], state: "computercraft:wired_modem_full{modem:false,peripheral:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:wired_modem_full"}},
{pos: [1, 1, 3], state: "computercraft:wired_modem_full{modem:false,peripheral:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:wired_modem_full"}},
{pos: [1, 1, 4], state: "minecraft:air"},
{pos: [2, 1, 0], state: "minecraft:air"},
{pos: [2, 1, 1], state: "minecraft:air"},
{pos: [2, 1, 2], state: "minecraft:air"},
{pos: [2, 1, 3], state: "computercraft:wired_modem_full{modem:false,peripheral:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:wired_modem_full"}},
{pos: [2, 1, 4], state: "minecraft:air"},
{pos: [3, 1, 0], state: "minecraft:air"},
{pos: [3, 1, 1], state: "computercraft:wired_modem_full{modem:false,peripheral:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:wired_modem_full"}},
{pos: [3, 1, 2], state: "computercraft:wired_modem_full{modem:false,peripheral:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:wired_modem_full"}},
{pos: [3, 1, 3], state: "computercraft:wired_modem_full{modem:false,peripheral:false}", nbt: {PeripheralAccess: 0b, id: "computercraft:wired_modem_full"}},
{pos: [3, 1, 4], state: "minecraft:air"},
{pos: [4, 1, 0], state: "minecraft:air"},
{pos: [4, 1, 1], state: "minecraft:air"},
{pos: [4, 1, 2], state: "minecraft:air"},
{pos: [4, 1, 3], state: "minecraft:air"},
{pos: [4, 1, 4], state: "minecraft:air"},
{pos: [0, 2, 0], state: "minecraft:air"},
{pos: [0, 2, 1], state: "minecraft:air"},
{pos: [0, 2, 2], state: "minecraft:air"},
{pos: [0, 2, 3], state: "minecraft:air"},
{pos: [0, 2, 4], state: "minecraft:air"},
{pos: [1, 2, 0], state: "minecraft:air"},
{pos: [1, 2, 1], state: "minecraft:air"},
{pos: [1, 2, 2], state: "minecraft:air"},
{pos: [1, 2, 3], state: "minecraft:air"},
{pos: [1, 2, 4], state: "minecraft:air"},
{pos: [2, 2, 0], state: "minecraft:air"},
{pos: [2, 2, 1], state: "minecraft:air"},
{pos: [2, 2, 2], state: "minecraft:air"},
{pos: [2, 2, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:air"},
{pos: [3, 2, 0], state: "minecraft:air"},
{pos: [3, 2, 1], state: "minecraft:air"},
{pos: [3, 2, 2], state: "minecraft:air"},
{pos: [3, 2, 3], state: "minecraft:air"},
{pos: [3, 2, 4], state: "minecraft:air"},
{pos: [4, 2, 0], state: "minecraft:air"},
{pos: [4, 2, 1], state: "minecraft:air"},
{pos: [4, 2, 2], state: "minecraft:air"},
{pos: [4, 2, 3], state: "minecraft:air"},
{pos: [4, 2, 4], state: "minecraft:air"},
{pos: [0, 3, 0], state: "minecraft:air"},
{pos: [0, 3, 1], state: "minecraft:air"},
{pos: [0, 3, 2], state: "minecraft:air"},
{pos: [0, 3, 3], state: "minecraft:air"},
{pos: [0, 3, 4], state: "minecraft:air"},
{pos: [1, 3, 0], state: "minecraft:air"},
{pos: [1, 3, 1], state: "minecraft:air"},
{pos: [1, 3, 2], state: "minecraft:air"},
{pos: [1, 3, 3], state: "minecraft:air"},
{pos: [1, 3, 4], state: "minecraft:air"},
{pos: [2, 3, 0], state: "minecraft:air"},
{pos: [2, 3, 1], state: "minecraft:air"},
{pos: [2, 3, 2], state: "minecraft:air"},
{pos: [2, 3, 3], state: "minecraft:air"},
{pos: [2, 3, 4], state: "minecraft:air"},
{pos: [3, 3, 0], state: "minecraft:air"},
{pos: [3, 3, 1], state: "minecraft:air"},
{pos: [3, 3, 2], state: "minecraft:air"},
{pos: [3, 3, 3], state: "minecraft:air"},
{pos: [3, 3, 4], state: "minecraft:air"},
{pos: [4, 3, 0], state: "minecraft:air"},
{pos: [4, 3, 1], state: "minecraft:air"},
{pos: [4, 3, 2], state: "minecraft:air"},
{pos: [4, 3, 3], state: "minecraft:air"},
{pos: [4, 3, 4], state: "minecraft:air"},
{pos: [0, 4, 0], state: "minecraft:air"},
{pos: [0, 4, 1], state: "minecraft:air"},
{pos: [0, 4, 2], state: "minecraft:air"},
{pos: [0, 4, 3], state: "minecraft:air"},
{pos: [0, 4, 4], state: "minecraft:air"},
{pos: [1, 4, 0], state: "minecraft:air"},
{pos: [1, 4, 1], state: "minecraft:air"},
{pos: [1, 4, 2], state: "minecraft:air"},
{pos: [1, 4, 3], state: "minecraft:air"},
{pos: [1, 4, 4], state: "minecraft:air"},
{pos: [2, 4, 0], state: "minecraft:air"},
{pos: [2, 4, 1], state: "minecraft:air"},
{pos: [2, 4, 2], state: "minecraft:air"},
{pos: [2, 4, 3], state: "minecraft:air"},
{pos: [2, 4, 4], state: "minecraft:air"},
{pos: [3, 4, 0], state: "minecraft:air"},
{pos: [3, 4, 1], state: "minecraft:air"},
{pos: [3, 4, 2], state: "minecraft:air"},
{pos: [3, 4, 3], state: "minecraft:air"},
{pos: [3, 4, 4], state: "minecraft:air"},
{pos: [4, 4, 0], state: "minecraft:air"},
{pos: [4, 4, 1], state: "minecraft:air"},
{pos: [4, 4, 2], state: "minecraft:air"},
{pos: [4, 4, 3], state: "minecraft:air"},
{pos: [4, 4, 4], state: "minecraft:air"}
],
entities: [],
palette: [
"minecraft:polished_andesite",
"minecraft:air",
"computercraft:wired_modem_full{modem:false,peripheral:false}"
]
}

View File

@@ -0,0 +1,139 @@
{
DataVersion: 3218,
size: [5, 5, 5],
data: [
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
{pos: [0, 1, 0], state: "minecraft:air"},
{pos: [0, 1, 1], state: "minecraft:air"},
{pos: [0, 1, 2], state: "minecraft:air"},
{pos: [0, 1, 3], state: "minecraft:air"},
{pos: [0, 1, 4], state: "minecraft:air"},
{pos: [1, 1, 0], state: "minecraft:air"},
{pos: [1, 1, 1], state: "minecraft:air"},
{pos: [1, 1, 2], state: "minecraft:air"},
{pos: [1, 1, 3], state: "minecraft:air"},
{pos: [1, 1, 4], state: "minecraft:air"},
{pos: [2, 1, 0], state: "minecraft:air"},
{pos: [2, 1, 1], state: "minecraft:air"},
{pos: [2, 1, 2], state: "minecraft:air"},
{pos: [2, 1, 3], state: "minecraft:air"},
{pos: [2, 1, 4], state: "minecraft:polished_andesite"},
{pos: [3, 1, 0], state: "minecraft:air"},
{pos: [3, 1, 1], state: "minecraft:air"},
{pos: [3, 1, 2], state: "minecraft:air"},
{pos: [3, 1, 3], state: "minecraft:air"},
{pos: [3, 1, 4], state: "minecraft:air"},
{pos: [4, 1, 0], state: "minecraft:air"},
{pos: [4, 1, 1], state: "minecraft:air"},
{pos: [4, 1, 2], state: "minecraft:air"},
{pos: [4, 1, 3], state: "minecraft:air"},
{pos: [4, 1, 4], state: "minecraft:air"},
{pos: [0, 2, 0], state: "minecraft:air"},
{pos: [0, 2, 1], state: "minecraft:air"},
{pos: [0, 2, 2], state: "minecraft:air"},
{pos: [0, 2, 3], state: "minecraft:air"},
{pos: [0, 2, 4], state: "minecraft:air"},
{pos: [1, 2, 0], state: "minecraft:air"},
{pos: [1, 2, 1], state: "minecraft:air"},
{pos: [1, 2, 2], state: "minecraft:air"},
{pos: [1, 2, 3], state: "minecraft:air"},
{pos: [1, 2, 4], state: "minecraft:air"},
{pos: [2, 2, 0], state: "minecraft:air"},
{pos: [2, 2, 1], state: "minecraft:air"},
{pos: [2, 2, 2], state: "minecraft:air"},
{pos: [2, 2, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:polished_andesite"},
{pos: [3, 2, 0], state: "minecraft:air"},
{pos: [3, 2, 1], state: "minecraft:air"},
{pos: [3, 2, 2], state: "minecraft:air"},
{pos: [3, 2, 3], state: "minecraft:air"},
{pos: [3, 2, 4], state: "minecraft:air"},
{pos: [4, 2, 0], state: "minecraft:air"},
{pos: [4, 2, 1], state: "minecraft:air"},
{pos: [4, 2, 2], state: "minecraft:air"},
{pos: [4, 2, 3], state: "minecraft:air"},
{pos: [4, 2, 4], state: "minecraft:air"},
{pos: [0, 3, 0], state: "minecraft:air"},
{pos: [0, 3, 1], state: "minecraft:air"},
{pos: [0, 3, 2], state: "minecraft:air"},
{pos: [0, 3, 3], state: "minecraft:air"},
{pos: [0, 3, 4], state: "minecraft:air"},
{pos: [1, 3, 0], state: "minecraft:air"},
{pos: [1, 3, 1], state: "minecraft:air"},
{pos: [1, 3, 2], state: "minecraft:air"},
{pos: [1, 3, 3], state: "minecraft:air"},
{pos: [1, 3, 4], state: "minecraft:air"},
{pos: [2, 3, 0], state: "minecraft:air"},
{pos: [2, 3, 1], state: "minecraft:air"},
{pos: [2, 3, 2], state: "minecraft:air"},
{pos: [2, 3, 3], state: "minecraft:air"},
{pos: [2, 3, 4], state: "minecraft:air"},
{pos: [3, 3, 0], state: "minecraft:air"},
{pos: [3, 3, 1], state: "minecraft:air"},
{pos: [3, 3, 2], state: "minecraft:air"},
{pos: [3, 3, 3], state: "minecraft:air"},
{pos: [3, 3, 4], state: "minecraft:air"},
{pos: [4, 3, 0], state: "minecraft:air"},
{pos: [4, 3, 1], state: "minecraft:air"},
{pos: [4, 3, 2], state: "minecraft:air"},
{pos: [4, 3, 3], state: "minecraft:air"},
{pos: [4, 3, 4], state: "minecraft:air"},
{pos: [0, 4, 0], state: "minecraft:air"},
{pos: [0, 4, 1], state: "minecraft:air"},
{pos: [0, 4, 2], state: "minecraft:air"},
{pos: [0, 4, 3], state: "minecraft:air"},
{pos: [0, 4, 4], state: "minecraft:air"},
{pos: [1, 4, 0], state: "minecraft:air"},
{pos: [1, 4, 1], state: "minecraft:air"},
{pos: [1, 4, 2], state: "minecraft:air"},
{pos: [1, 4, 3], state: "minecraft:air"},
{pos: [1, 4, 4], state: "minecraft:air"},
{pos: [2, 4, 0], state: "minecraft:air"},
{pos: [2, 4, 1], state: "minecraft:air"},
{pos: [2, 4, 2], state: "minecraft:air"},
{pos: [2, 4, 3], state: "minecraft:air"},
{pos: [2, 4, 4], state: "minecraft:air"},
{pos: [3, 4, 0], state: "minecraft:air"},
{pos: [3, 4, 1], state: "minecraft:air"},
{pos: [3, 4, 2], state: "minecraft:air"},
{pos: [3, 4, 3], state: "minecraft:air"},
{pos: [3, 4, 4], state: "minecraft:air"},
{pos: [4, 4, 0], state: "minecraft:air"},
{pos: [4, 4, 1], state: "minecraft:air"},
{pos: [4, 4, 2], state: "minecraft:air"},
{pos: [4, 4, 3], state: "minecraft:air"},
{pos: [4, 4, 4], state: "minecraft:air"}
],
entities: [
{blockPos: [2, 1, 2], pos: [2.583196949396921d, 1.0d, 2.608974919959593d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CustomName: '{"text":"printouttest.in_frame_at_night"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [221.58319694939692d, -58.0d, 92.60897491995959d], Pose: {}, Rotation: [1.3504658f, 6.7031174f], ShowArms: 0b, Small: 0b, UUID: [I; 1854159985, -991539606, -1317309541, 1483112386], id: "minecraft:armor_stand"}},
{blockPos: [2, 2, 3], pos: [2.5d, 2.5d, 3.96875d], nbt: {Air: 300s, Facing: 2b, FallDistance: 0.0f, Fire: -1s, Fixed: 0b, Invisible: 0b, Invulnerable: 0b, Item: {Count: 1b, id: "computercraft:printed_page", tag: {Color0: "eeeeeeeeeeeeeeeeeeeeeeeee", Color1: "eeeeeeeeeeeeeeeeeeeeeeeee", Color10: "eeeeeeeeeeeeeeeeeeeeeeeee", Color11: "eeeeeeeeeeeeeeeeeeeeeeeee", Color12: "eeeeeeeeeeeeeeeeeeeeeeeee", Color13: "eeeeeeeeeeeeeeeeeeeeeeeee", Color14: "eeeeeeeeeeeeeeeeeeeeeeeee", Color15: "eeeeeeeeeeeeeeeeeeeeeeeee", Color16: "eeeeeeeeeeeeeeeeeeeeeeeee", Color17: "eeeeeeeeeeeeeeeeeeeeeeeee", Color18: "eeeeeeeeeeeeeeeeeeeeeeeee", Color19: "eeeeeeeeeeeeeeeeeeeeeeeee", Color2: "eeeeeeeeeeeeeeeeeeeeeeeee", Color20: "eeeeeeeeeeeeeeeeeeeeeeeee", Color3: "eeeeeeeeeeeeeeeeeeeeeeeee", Color4: "eeeeeeeeeeeeeeeeeeeeeeeee", Color5: "eeeeeeeeeeeeeeeeeeeeeeeee", Color6: "eeeeeeeeeeeeeeeeeeeeeeeee", Color7: "eeeeeeeeeeeeeeeeeeeeeeeee", Color8: "eeeeeeeeeeeeeeeeeeeeeeeee", Color9: "eeeeeeeeeeeeeeeeeeeeeeeee", Pages: 1, Text0: "If you're reading this, ", Text1: "the test failed. ", Text10: " ", Text11: " ", Text12: " ", Text13: " ", Text14: " ", Text15: " ", Text16: " ", Text17: " ", Text18: " ", Text19: " ", Text2: " ", Text20: " ", Text3: " ", Text4: " ", Text5: " ", Text6: " ", Text7: " ", Text8: " ", Text9: " ", Title: "a.lua"}}, ItemDropChance: 1.0f, ItemRotation: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [221.5d, -56.5d, 93.96875d], Rotation: [-540.0f, 0.0f], TileX: 221, TileY: -57, TileZ: 93, UUID: [I; 1972443954, 193152445, -1823446000, -1684171214], id: "minecraft:item_frame"}}
],
palette: [
"minecraft:polished_andesite",
"minecraft:air"
]
}

View File

@@ -1,140 +0,0 @@
{
DataVersion: 2730,
size: [5, 5, 5],
data: [
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
{pos: [0, 1, 0], state: "minecraft:white_concrete"},
{pos: [0, 1, 1], state: "minecraft:white_concrete"},
{pos: [0, 1, 2], state: "minecraft:white_concrete"},
{pos: [0, 1, 3], state: "minecraft:white_concrete"},
{pos: [0, 1, 4], state: "minecraft:white_concrete"},
{pos: [1, 1, 0], state: "minecraft:white_concrete"},
{pos: [1, 1, 1], state: "minecraft:air"},
{pos: [1, 1, 2], state: "minecraft:air"},
{pos: [1, 1, 3], state: "minecraft:air"},
{pos: [1, 1, 4], state: "minecraft:white_concrete"},
{pos: [2, 1, 0], state: "minecraft:white_concrete"},
{pos: [2, 1, 1], state: "minecraft:air"},
{pos: [2, 1, 2], state: "minecraft:air"},
{pos: [2, 1, 3], state: "minecraft:air"},
{pos: [2, 1, 4], state: "minecraft:white_concrete"},
{pos: [3, 1, 0], state: "minecraft:white_concrete"},
{pos: [3, 1, 1], state: "minecraft:air"},
{pos: [3, 1, 2], state: "minecraft:air"},
{pos: [3, 1, 3], state: "minecraft:air"},
{pos: [3, 1, 4], state: "minecraft:white_concrete"},
{pos: [4, 1, 0], state: "minecraft:white_concrete"},
{pos: [4, 1, 1], state: "minecraft:white_concrete"},
{pos: [4, 1, 2], state: "minecraft:white_concrete"},
{pos: [4, 1, 3], state: "minecraft:white_concrete"},
{pos: [4, 1, 4], state: "minecraft:white_concrete"},
{pos: [0, 2, 0], state: "minecraft:white_concrete"},
{pos: [0, 2, 1], state: "minecraft:white_concrete"},
{pos: [0, 2, 2], state: "minecraft:white_concrete"},
{pos: [0, 2, 3], state: "minecraft:white_concrete"},
{pos: [0, 2, 4], state: "minecraft:white_concrete"},
{pos: [1, 2, 0], state: "minecraft:white_concrete"},
{pos: [1, 2, 1], state: "minecraft:air"},
{pos: [1, 2, 2], state: "minecraft:air"},
{pos: [1, 2, 3], state: "minecraft:air"},
{pos: [1, 2, 4], state: "minecraft:white_concrete"},
{pos: [2, 2, 0], state: "minecraft:white_concrete"},
{pos: [2, 2, 1], state: "minecraft:air"},
{pos: [2, 2, 2], state: "minecraft:air"},
{pos: [2, 2, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:white_concrete"},
{pos: [3, 2, 0], state: "minecraft:white_concrete"},
{pos: [3, 2, 1], state: "minecraft:air"},
{pos: [3, 2, 2], state: "minecraft:air"},
{pos: [3, 2, 3], state: "minecraft:air"},
{pos: [3, 2, 4], state: "minecraft:white_concrete"},
{pos: [4, 2, 0], state: "minecraft:white_concrete"},
{pos: [4, 2, 1], state: "minecraft:white_concrete"},
{pos: [4, 2, 2], state: "minecraft:white_concrete"},
{pos: [4, 2, 3], state: "minecraft:white_concrete"},
{pos: [4, 2, 4], state: "minecraft:white_concrete"},
{pos: [0, 3, 0], state: "minecraft:white_concrete"},
{pos: [0, 3, 1], state: "minecraft:white_concrete"},
{pos: [0, 3, 2], state: "minecraft:white_concrete"},
{pos: [0, 3, 3], state: "minecraft:white_concrete"},
{pos: [0, 3, 4], state: "minecraft:white_concrete"},
{pos: [1, 3, 0], state: "minecraft:white_concrete"},
{pos: [1, 3, 1], state: "minecraft:air"},
{pos: [1, 3, 2], state: "minecraft:air"},
{pos: [1, 3, 3], state: "minecraft:air"},
{pos: [1, 3, 4], state: "minecraft:white_concrete"},
{pos: [2, 3, 0], state: "minecraft:white_concrete"},
{pos: [2, 3, 1], state: "minecraft:air"},
{pos: [2, 3, 2], state: "minecraft:air"},
{pos: [2, 3, 3], state: "minecraft:air"},
{pos: [2, 3, 4], state: "minecraft:white_concrete"},
{pos: [3, 3, 0], state: "minecraft:white_concrete"},
{pos: [3, 3, 1], state: "minecraft:air"},
{pos: [3, 3, 2], state: "minecraft:air"},
{pos: [3, 3, 3], state: "minecraft:air"},
{pos: [3, 3, 4], state: "minecraft:white_concrete"},
{pos: [4, 3, 0], state: "minecraft:white_concrete"},
{pos: [4, 3, 1], state: "minecraft:white_concrete"},
{pos: [4, 3, 2], state: "minecraft:white_concrete"},
{pos: [4, 3, 3], state: "minecraft:white_concrete"},
{pos: [4, 3, 4], state: "minecraft:white_concrete"},
{pos: [0, 4, 0], state: "minecraft:white_concrete"},
{pos: [0, 4, 1], state: "minecraft:white_concrete"},
{pos: [0, 4, 2], state: "minecraft:white_concrete"},
{pos: [0, 4, 3], state: "minecraft:white_concrete"},
{pos: [0, 4, 4], state: "minecraft:white_concrete"},
{pos: [1, 4, 0], state: "minecraft:white_concrete"},
{pos: [1, 4, 1], state: "minecraft:white_concrete"},
{pos: [1, 4, 2], state: "minecraft:white_concrete"},
{pos: [1, 4, 3], state: "minecraft:white_concrete"},
{pos: [1, 4, 4], state: "minecraft:white_concrete"},
{pos: [2, 4, 0], state: "minecraft:white_concrete"},
{pos: [2, 4, 1], state: "minecraft:white_concrete"},
{pos: [2, 4, 2], state: "minecraft:white_concrete"},
{pos: [2, 4, 3], state: "minecraft:white_concrete"},
{pos: [2, 4, 4], state: "minecraft:white_concrete"},
{pos: [3, 4, 0], state: "minecraft:white_concrete"},
{pos: [3, 4, 1], state: "minecraft:white_concrete"},
{pos: [3, 4, 2], state: "minecraft:white_concrete"},
{pos: [3, 4, 3], state: "minecraft:white_concrete"},
{pos: [3, 4, 4], state: "minecraft:white_concrete"},
{pos: [4, 4, 0], state: "minecraft:white_concrete"},
{pos: [4, 4, 1], state: "minecraft:white_concrete"},
{pos: [4, 4, 2], state: "minecraft:white_concrete"},
{pos: [4, 4, 3], state: "minecraft:white_concrete"},
{pos: [4, 4, 4], state: "minecraft:white_concrete"}
],
entities: [
{blockPos: [2, 2, 3], pos: [2.5d, 2.5d, 3.96875d], nbt: {Air: 300s, CanUpdate: 1b, Facing: 2b, FallDistance: 0.0f, Fire: -1s, Fixed: 0b, Invisible: 0b, Invulnerable: 0b, Item: {Count: 1b, id: "computercraft:printed_page", tag: {Color0: "eeeeeeeeeeeeeeeeeeeeeeeee", Color1: "eeeeeeeeeeeeeeeeeeeeeeeee", Color10: "eeeeeeeeeeeeeeeeeeeeeeeee", Color11: "eeeeeeeeeeeeeeeeeeeeeeeee", Color12: "eeeeeeeeeeeeeeeeeeeeeeeee", Color13: "eeeeeeeeeeeeeeeeeeeeeeeee", Color14: "eeeeeeeeeeeeeeeeeeeeeeeee", Color15: "eeeeeeeeeeeeeeeeeeeeeeeee", Color16: "eeeeeeeeeeeeeeeeeeeeeeeee", Color17: "eeeeeeeeeeeeeeeeeeeeeeeee", Color18: "eeeeeeeeeeeeeeeeeeeeeeeee", Color19: "eeeeeeeeeeeeeeeeeeeeeeeee", Color2: "eeeeeeeeeeeeeeeeeeeeeeeee", Color20: "eeeeeeeeeeeeeeeeeeeeeeeee", Color3: "eeeeeeeeeeeeeeeeeeeeeeeee", Color4: "eeeeeeeeeeeeeeeeeeeeeeeee", Color5: "eeeeeeeeeeeeeeeeeeeeeeeee", Color6: "eeeeeeeeeeeeeeeeeeeeeeeee", Color7: "eeeeeeeeeeeeeeeeeeeeeeeee", Color8: "eeeeeeeeeeeeeeeeeeeeeeeee", Color9: "eeeeeeeeeeeeeeeeeeeeeeeee", Pages: 1, Text0: "If you're reading this, ", Text1: "the test failed. ", Text10: " ", Text11: " ", Text12: " ", Text13: " ", Text14: " ", Text15: " ", Text16: " ", Text17: " ", Text18: " ", Text19: " ", Text2: " ", Text20: " ", Text3: " ", Text4: " ", Text5: " ", Text6: " ", Text7: " ", Text8: " ", Text9: " ", Title: "a.lua"}}, ItemDropChance: 1.0f, ItemRotation: 0b, Motion: [0.0d, 0.0d, 0.0d], OnGround: 0b, PortalCooldown: 0, Pos: [10.5d, 7.5d, 44.96875d], Rotation: [180.0f, 0.0f], TileX: 10, TileY: 7, TileZ: 44, UUID: [I; 1043973837, -2076424529, -1762893135, -165665834], id: "minecraft:item_frame"}},
{blockPos: [2, 1, 2], pos: [2.583196949396914d, 1.0d, 2.6089749199596d], nbt: {AbsorptionAmount: 0.0f, Air: 300s, ArmorItems: [{}, {}, {}, {}], Attributes: [{Base: 0.699999988079071d, Name: "minecraft:generic.movement_speed"}], Brain: {memories: {}}, CanUpdate: 1b, CustomName: '{"text":"printouttest.in_frame_at_night"}', DeathTime: 0s, DisabledSlots: 0, FallDistance: 0.0f, FallFlying: 0b, Fire: -1s, HandItems: [{}, {}], Health: 20.0f, HurtByTimestamp: 0, HurtTime: 0s, Invisible: 1b, Invulnerable: 0b, Marker: 1b, Motion: [0.0d, 0.0d, 0.0d], NoBasePlate: 0b, OnGround: 0b, PortalCooldown: 0, Pos: [10.583196949396914d, 6.0d, 43.6089749199596d], Pose: {}, Rotation: [1.3504658f, 6.7031174f], ShowArms: 0b, Small: 0b, UUID: [I; -1917933016, 1390888530, -2109873447, -2136052677], id: "minecraft:armor_stand"}}
],
palette: [
"minecraft:polished_andesite",
"minecraft:white_concrete",
"minecraft:air"
]
}

View File

@@ -0,0 +1,138 @@
{
DataVersion: 3218,
size: [5, 5, 5],
data: [
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
{pos: [0, 1, 0], state: "minecraft:air"},
{pos: [0, 1, 1], state: "minecraft:air"},
{pos: [0, 1, 2], state: "minecraft:air"},
{pos: [0, 1, 3], state: "minecraft:air"},
{pos: [0, 1, 4], state: "minecraft:air"},
{pos: [1, 1, 0], state: "minecraft:air"},
{pos: [1, 1, 1], state: "minecraft:air"},
{pos: [1, 1, 2], state: "minecraft:air"},
{pos: [1, 1, 3], state: "minecraft:air"},
{pos: [1, 1, 4], state: "minecraft:air"},
{pos: [2, 1, 0], state: "minecraft:air"},
{pos: [2, 1, 1], state: "minecraft:air"},
{pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 80, Items: [], Label: "turtle_test.break_cable", LeftUpgrade: "minecraft:diamond_pickaxe", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
{pos: [2, 1, 3], state: "computercraft:cable{cable:true,down:true,east:false,modem:down_off,north:false,south:false,up:false,waterlogged:false,west:false}", nbt: {PeirpheralAccess: 0b, id: "computercraft:cable"}},
{pos: [2, 1, 4], state: "minecraft:air"},
{pos: [3, 1, 0], state: "minecraft:air"},
{pos: [3, 1, 1], state: "minecraft:air"},
{pos: [3, 1, 2], state: "minecraft:air"},
{pos: [3, 1, 3], state: "minecraft:air"},
{pos: [3, 1, 4], state: "minecraft:air"},
{pos: [4, 1, 0], state: "minecraft:air"},
{pos: [4, 1, 1], state: "minecraft:air"},
{pos: [4, 1, 2], state: "minecraft:air"},
{pos: [4, 1, 3], state: "minecraft:air"},
{pos: [4, 1, 4], state: "minecraft:air"},
{pos: [0, 2, 0], state: "minecraft:air"},
{pos: [0, 2, 1], state: "minecraft:air"},
{pos: [0, 2, 2], state: "minecraft:air"},
{pos: [0, 2, 3], state: "minecraft:air"},
{pos: [0, 2, 4], state: "minecraft:air"},
{pos: [1, 2, 0], state: "minecraft:air"},
{pos: [1, 2, 1], state: "minecraft:air"},
{pos: [1, 2, 2], state: "minecraft:air"},
{pos: [1, 2, 3], state: "minecraft:air"},
{pos: [1, 2, 4], state: "minecraft:air"},
{pos: [2, 2, 0], state: "minecraft:air"},
{pos: [2, 2, 1], state: "minecraft:air"},
{pos: [2, 2, 2], state: "minecraft:air"},
{pos: [2, 2, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:air"},
{pos: [3, 2, 0], state: "minecraft:air"},
{pos: [3, 2, 1], state: "minecraft:air"},
{pos: [3, 2, 2], state: "minecraft:air"},
{pos: [3, 2, 3], state: "minecraft:air"},
{pos: [3, 2, 4], state: "minecraft:air"},
{pos: [4, 2, 0], state: "minecraft:air"},
{pos: [4, 2, 1], state: "minecraft:air"},
{pos: [4, 2, 2], state: "minecraft:air"},
{pos: [4, 2, 3], state: "minecraft:air"},
{pos: [4, 2, 4], state: "minecraft:air"},
{pos: [0, 3, 0], state: "minecraft:air"},
{pos: [0, 3, 1], state: "minecraft:air"},
{pos: [0, 3, 2], state: "minecraft:air"},
{pos: [0, 3, 3], state: "minecraft:air"},
{pos: [0, 3, 4], state: "minecraft:air"},
{pos: [1, 3, 0], state: "minecraft:air"},
{pos: [1, 3, 1], state: "minecraft:air"},
{pos: [1, 3, 2], state: "minecraft:air"},
{pos: [1, 3, 3], state: "minecraft:air"},
{pos: [1, 3, 4], state: "minecraft:air"},
{pos: [2, 3, 0], state: "minecraft:air"},
{pos: [2, 3, 1], state: "minecraft:air"},
{pos: [2, 3, 2], state: "minecraft:air"},
{pos: [2, 3, 3], state: "minecraft:air"},
{pos: [2, 3, 4], state: "minecraft:air"},
{pos: [3, 3, 0], state: "minecraft:air"},
{pos: [3, 3, 1], state: "minecraft:air"},
{pos: [3, 3, 2], state: "minecraft:air"},
{pos: [3, 3, 3], state: "minecraft:air"},
{pos: [3, 3, 4], state: "minecraft:air"},
{pos: [4, 3, 0], state: "minecraft:air"},
{pos: [4, 3, 1], state: "minecraft:air"},
{pos: [4, 3, 2], state: "minecraft:air"},
{pos: [4, 3, 3], state: "minecraft:air"},
{pos: [4, 3, 4], state: "minecraft:air"},
{pos: [0, 4, 0], state: "minecraft:air"},
{pos: [0, 4, 1], state: "minecraft:air"},
{pos: [0, 4, 2], state: "minecraft:air"},
{pos: [0, 4, 3], state: "minecraft:air"},
{pos: [0, 4, 4], state: "minecraft:air"},
{pos: [1, 4, 0], state: "minecraft:air"},
{pos: [1, 4, 1], state: "minecraft:air"},
{pos: [1, 4, 2], state: "minecraft:air"},
{pos: [1, 4, 3], state: "minecraft:air"},
{pos: [1, 4, 4], state: "minecraft:air"},
{pos: [2, 4, 0], state: "minecraft:air"},
{pos: [2, 4, 1], state: "minecraft:air"},
{pos: [2, 4, 2], state: "minecraft:air"},
{pos: [2, 4, 3], state: "minecraft:air"},
{pos: [2, 4, 4], state: "minecraft:air"},
{pos: [3, 4, 0], state: "minecraft:air"},
{pos: [3, 4, 1], state: "minecraft:air"},
{pos: [3, 4, 2], state: "minecraft:air"},
{pos: [3, 4, 3], state: "minecraft:air"},
{pos: [3, 4, 4], state: "minecraft:air"},
{pos: [4, 4, 0], state: "minecraft:air"},
{pos: [4, 4, 1], state: "minecraft:air"},
{pos: [4, 4, 2], state: "minecraft:air"},
{pos: [4, 4, 3], state: "minecraft:air"},
{pos: [4, 4, 4], state: "minecraft:air"}
],
entities: [],
palette: [
"minecraft:polished_andesite",
"minecraft:air",
"computercraft:turtle_normal{facing:south,waterlogged:false}",
"computercraft:cable{cable:true,down:true,east:false,modem:down_off,north:false,south:false,up:false,waterlogged:false,west:false}"
]
}

View File

@@ -0,0 +1,137 @@
{
DataVersion: 2975,
size: [5, 5, 5],
data: [
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
{pos: [0, 1, 0], state: "minecraft:air"},
{pos: [0, 1, 1], state: "minecraft:air"},
{pos: [0, 1, 2], state: "minecraft:air"},
{pos: [0, 1, 3], state: "minecraft:air"},
{pos: [0, 1, 4], state: "minecraft:air"},
{pos: [1, 1, 0], state: "minecraft:air"},
{pos: [1, 1, 1], state: "minecraft:air"},
{pos: [1, 1, 2], state: "minecraft:air"},
{pos: [1, 1, 3], state: "minecraft:air"},
{pos: [1, 1, 4], state: "minecraft:air"},
{pos: [2, 1, 0], state: "minecraft:air"},
{pos: [2, 1, 1], state: "minecraft:air"},
{pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Label: "turtle_test.move_preserves_state", Fuel: 80, Items: [{Count: 32b, Slot: 0b, id: "minecraft:dirt"}], On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
{pos: [2, 1, 3], state: "minecraft:air"},
{pos: [2, 1, 4], state: "minecraft:air"},
{pos: [3, 1, 0], state: "minecraft:air"},
{pos: [3, 1, 1], state: "minecraft:air"},
{pos: [3, 1, 2], state: "minecraft:air"},
{pos: [3, 1, 3], state: "minecraft:air"},
{pos: [3, 1, 4], state: "minecraft:air"},
{pos: [4, 1, 0], state: "minecraft:air"},
{pos: [4, 1, 1], state: "minecraft:air"},
{pos: [4, 1, 2], state: "minecraft:air"},
{pos: [4, 1, 3], state: "minecraft:air"},
{pos: [4, 1, 4], state: "minecraft:air"},
{pos: [0, 2, 0], state: "minecraft:air"},
{pos: [0, 2, 1], state: "minecraft:air"},
{pos: [0, 2, 2], state: "minecraft:air"},
{pos: [0, 2, 3], state: "minecraft:air"},
{pos: [0, 2, 4], state: "minecraft:air"},
{pos: [1, 2, 0], state: "minecraft:air"},
{pos: [1, 2, 1], state: "minecraft:air"},
{pos: [1, 2, 2], state: "minecraft:air"},
{pos: [1, 2, 3], state: "minecraft:air"},
{pos: [1, 2, 4], state: "minecraft:air"},
{pos: [2, 2, 0], state: "minecraft:air"},
{pos: [2, 2, 1], state: "minecraft:air"},
{pos: [2, 2, 2], state: "minecraft:air"},
{pos: [2, 2, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:air"},
{pos: [3, 2, 0], state: "minecraft:air"},
{pos: [3, 2, 1], state: "minecraft:air"},
{pos: [3, 2, 2], state: "minecraft:air"},
{pos: [3, 2, 3], state: "minecraft:air"},
{pos: [3, 2, 4], state: "minecraft:air"},
{pos: [4, 2, 0], state: "minecraft:air"},
{pos: [4, 2, 1], state: "minecraft:air"},
{pos: [4, 2, 2], state: "minecraft:air"},
{pos: [4, 2, 3], state: "minecraft:air"},
{pos: [4, 2, 4], state: "minecraft:air"},
{pos: [0, 3, 0], state: "minecraft:air"},
{pos: [0, 3, 1], state: "minecraft:air"},
{pos: [0, 3, 2], state: "minecraft:air"},
{pos: [0, 3, 3], state: "minecraft:air"},
{pos: [0, 3, 4], state: "minecraft:air"},
{pos: [1, 3, 0], state: "minecraft:air"},
{pos: [1, 3, 1], state: "minecraft:air"},
{pos: [1, 3, 2], state: "minecraft:air"},
{pos: [1, 3, 3], state: "minecraft:air"},
{pos: [1, 3, 4], state: "minecraft:air"},
{pos: [2, 3, 0], state: "minecraft:air"},
{pos: [2, 3, 1], state: "minecraft:air"},
{pos: [2, 3, 2], state: "minecraft:air"},
{pos: [2, 3, 3], state: "minecraft:air"},
{pos: [2, 3, 4], state: "minecraft:air"},
{pos: [3, 3, 0], state: "minecraft:air"},
{pos: [3, 3, 1], state: "minecraft:air"},
{pos: [3, 3, 2], state: "minecraft:air"},
{pos: [3, 3, 3], state: "minecraft:air"},
{pos: [3, 3, 4], state: "minecraft:air"},
{pos: [4, 3, 0], state: "minecraft:air"},
{pos: [4, 3, 1], state: "minecraft:air"},
{pos: [4, 3, 2], state: "minecraft:air"},
{pos: [4, 3, 3], state: "minecraft:air"},
{pos: [4, 3, 4], state: "minecraft:air"},
{pos: [0, 4, 0], state: "minecraft:air"},
{pos: [0, 4, 1], state: "minecraft:air"},
{pos: [0, 4, 2], state: "minecraft:air"},
{pos: [0, 4, 3], state: "minecraft:air"},
{pos: [0, 4, 4], state: "minecraft:air"},
{pos: [1, 4, 0], state: "minecraft:air"},
{pos: [1, 4, 1], state: "minecraft:air"},
{pos: [1, 4, 2], state: "minecraft:air"},
{pos: [1, 4, 3], state: "minecraft:air"},
{pos: [1, 4, 4], state: "minecraft:air"},
{pos: [2, 4, 0], state: "minecraft:air"},
{pos: [2, 4, 1], state: "minecraft:air"},
{pos: [2, 4, 2], state: "minecraft:air"},
{pos: [2, 4, 3], state: "minecraft:air"},
{pos: [2, 4, 4], state: "minecraft:air"},
{pos: [3, 4, 0], state: "minecraft:air"},
{pos: [3, 4, 1], state: "minecraft:air"},
{pos: [3, 4, 2], state: "minecraft:air"},
{pos: [3, 4, 3], state: "minecraft:air"},
{pos: [3, 4, 4], state: "minecraft:air"},
{pos: [4, 4, 0], state: "minecraft:air"},
{pos: [4, 4, 1], state: "minecraft:air"},
{pos: [4, 4, 2], state: "minecraft:air"},
{pos: [4, 4, 3], state: "minecraft:air"},
{pos: [4, 4, 4], state: "minecraft:air"}
],
entities: [],
palette: [
"minecraft:polished_andesite",
"minecraft:air",
"computercraft:turtle_normal{facing:south,waterlogged:false}"
]
}

View File

@@ -0,0 +1,137 @@
{
DataVersion: 2975,
size: [5, 5, 5],
data: [
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
{pos: [0, 0, 3], state: "minecraft:polished_andesite"},
{pos: [0, 0, 4], state: "minecraft:polished_andesite"},
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
{pos: [1, 0, 3], state: "minecraft:polished_andesite"},
{pos: [1, 0, 4], state: "minecraft:polished_andesite"},
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
{pos: [2, 0, 3], state: "minecraft:polished_andesite"},
{pos: [2, 0, 4], state: "minecraft:polished_andesite"},
{pos: [3, 0, 0], state: "minecraft:polished_andesite"},
{pos: [3, 0, 1], state: "minecraft:polished_andesite"},
{pos: [3, 0, 2], state: "minecraft:polished_andesite"},
{pos: [3, 0, 3], state: "minecraft:polished_andesite"},
{pos: [3, 0, 4], state: "minecraft:polished_andesite"},
{pos: [4, 0, 0], state: "minecraft:polished_andesite"},
{pos: [4, 0, 1], state: "minecraft:polished_andesite"},
{pos: [4, 0, 2], state: "minecraft:polished_andesite"},
{pos: [4, 0, 3], state: "minecraft:polished_andesite"},
{pos: [4, 0, 4], state: "minecraft:polished_andesite"},
{pos: [0, 1, 0], state: "minecraft:air"},
{pos: [0, 1, 1], state: "minecraft:air"},
{pos: [0, 1, 2], state: "minecraft:air"},
{pos: [0, 1, 3], state: "minecraft:air"},
{pos: [0, 1, 4], state: "minecraft:air"},
{pos: [1, 1, 0], state: "minecraft:air"},
{pos: [1, 1, 1], state: "minecraft:air"},
{pos: [1, 1, 2], state: "minecraft:air"},
{pos: [1, 1, 3], state: "minecraft:air"},
{pos: [1, 1, 4], state: "minecraft:air"},
{pos: [2, 1, 0], state: "minecraft:air"},
{pos: [2, 1, 1], state: "minecraft:air"},
{pos: [2, 1, 2], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Label: "turtle_test.refuel_fail", Fuel: 0, Items: [{Count: 32b, Slot: 0b, id: "minecraft:dirt"}], On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
{pos: [2, 1, 3], state: "minecraft:air"},
{pos: [2, 1, 4], state: "minecraft:air"},
{pos: [3, 1, 0], state: "minecraft:air"},
{pos: [3, 1, 1], state: "minecraft:air"},
{pos: [3, 1, 2], state: "minecraft:air"},
{pos: [3, 1, 3], state: "minecraft:air"},
{pos: [3, 1, 4], state: "minecraft:air"},
{pos: [4, 1, 0], state: "minecraft:air"},
{pos: [4, 1, 1], state: "minecraft:air"},
{pos: [4, 1, 2], state: "minecraft:air"},
{pos: [4, 1, 3], state: "minecraft:air"},
{pos: [4, 1, 4], state: "minecraft:air"},
{pos: [0, 2, 0], state: "minecraft:air"},
{pos: [0, 2, 1], state: "minecraft:air"},
{pos: [0, 2, 2], state: "minecraft:air"},
{pos: [0, 2, 3], state: "minecraft:air"},
{pos: [0, 2, 4], state: "minecraft:air"},
{pos: [1, 2, 0], state: "minecraft:air"},
{pos: [1, 2, 1], state: "minecraft:air"},
{pos: [1, 2, 2], state: "minecraft:air"},
{pos: [1, 2, 3], state: "minecraft:air"},
{pos: [1, 2, 4], state: "minecraft:air"},
{pos: [2, 2, 0], state: "minecraft:air"},
{pos: [2, 2, 1], state: "minecraft:air"},
{pos: [2, 2, 2], state: "minecraft:air"},
{pos: [2, 2, 3], state: "minecraft:air"},
{pos: [2, 2, 4], state: "minecraft:air"},
{pos: [3, 2, 0], state: "minecraft:air"},
{pos: [3, 2, 1], state: "minecraft:air"},
{pos: [3, 2, 2], state: "minecraft:air"},
{pos: [3, 2, 3], state: "minecraft:air"},
{pos: [3, 2, 4], state: "minecraft:air"},
{pos: [4, 2, 0], state: "minecraft:air"},
{pos: [4, 2, 1], state: "minecraft:air"},
{pos: [4, 2, 2], state: "minecraft:air"},
{pos: [4, 2, 3], state: "minecraft:air"},
{pos: [4, 2, 4], state: "minecraft:air"},
{pos: [0, 3, 0], state: "minecraft:air"},
{pos: [0, 3, 1], state: "minecraft:air"},
{pos: [0, 3, 2], state: "minecraft:air"},
{pos: [0, 3, 3], state: "minecraft:air"},
{pos: [0, 3, 4], state: "minecraft:air"},
{pos: [1, 3, 0], state: "minecraft:air"},
{pos: [1, 3, 1], state: "minecraft:air"},
{pos: [1, 3, 2], state: "minecraft:air"},
{pos: [1, 3, 3], state: "minecraft:air"},
{pos: [1, 3, 4], state: "minecraft:air"},
{pos: [2, 3, 0], state: "minecraft:air"},
{pos: [2, 3, 1], state: "minecraft:air"},
{pos: [2, 3, 2], state: "minecraft:air"},
{pos: [2, 3, 3], state: "minecraft:air"},
{pos: [2, 3, 4], state: "minecraft:air"},
{pos: [3, 3, 0], state: "minecraft:air"},
{pos: [3, 3, 1], state: "minecraft:air"},
{pos: [3, 3, 2], state: "minecraft:air"},
{pos: [3, 3, 3], state: "minecraft:air"},
{pos: [3, 3, 4], state: "minecraft:air"},
{pos: [4, 3, 0], state: "minecraft:air"},
{pos: [4, 3, 1], state: "minecraft:air"},
{pos: [4, 3, 2], state: "minecraft:air"},
{pos: [4, 3, 3], state: "minecraft:air"},
{pos: [4, 3, 4], state: "minecraft:air"},
{pos: [0, 4, 0], state: "minecraft:air"},
{pos: [0, 4, 1], state: "minecraft:air"},
{pos: [0, 4, 2], state: "minecraft:air"},
{pos: [0, 4, 3], state: "minecraft:air"},
{pos: [0, 4, 4], state: "minecraft:air"},
{pos: [1, 4, 0], state: "minecraft:air"},
{pos: [1, 4, 1], state: "minecraft:air"},
{pos: [1, 4, 2], state: "minecraft:air"},
{pos: [1, 4, 3], state: "minecraft:air"},
{pos: [1, 4, 4], state: "minecraft:air"},
{pos: [2, 4, 0], state: "minecraft:air"},
{pos: [2, 4, 1], state: "minecraft:air"},
{pos: [2, 4, 2], state: "minecraft:air"},
{pos: [2, 4, 3], state: "minecraft:air"},
{pos: [2, 4, 4], state: "minecraft:air"},
{pos: [3, 4, 0], state: "minecraft:air"},
{pos: [3, 4, 1], state: "minecraft:air"},
{pos: [3, 4, 2], state: "minecraft:air"},
{pos: [3, 4, 3], state: "minecraft:air"},
{pos: [3, 4, 4], state: "minecraft:air"},
{pos: [4, 4, 0], state: "minecraft:air"},
{pos: [4, 4, 1], state: "minecraft:air"},
{pos: [4, 4, 2], state: "minecraft:air"},
{pos: [4, 4, 3], state: "minecraft:air"},
{pos: [4, 4, 4], state: "minecraft:air"}
],
entities: [],
palette: [
"minecraft:polished_andesite",
"minecraft:air",
"computercraft:turtle_normal{facing:south,waterlogged:false}"
]
}

View File

@@ -20,6 +20,7 @@ dependencies {
implementation(libs.asm) implementation(libs.asm)
testFixturesImplementation(libs.slf4j) testFixturesImplementation(libs.slf4j)
testFixturesApi(platform(libs.kotlin.platform))
testFixturesApi(libs.bundles.test) testFixturesApi(libs.bundles.test)
testFixturesApi(libs.bundles.kotlin) testFixturesApi(libs.bundles.kotlin)
@@ -31,7 +32,7 @@ dependencies {
tasks.processResources { tasks.processResources {
inputs.property("gitHash", cct.gitHash) inputs.property("gitHash", cct.gitHash)
filesMatching("data/computercraft/lua/rom/help/credits.txt") { filesMatching("data/computercraft/lua/rom/help/credits.md") {
expand(mapOf("gitContributors" to cct.gitContributors.map { it.joinToString("\n") }.get())) expand(mapOf("gitContributors" to cct.gitContributors.map { it.joinToString("\n") }.get()))
} }
} }

View File

@@ -15,10 +15,12 @@ public final class Logging {
public static final Marker COMPUTER_ERROR = MarkerFactory.getMarker("COMPUTER_ERROR"); public static final Marker COMPUTER_ERROR = MarkerFactory.getMarker("COMPUTER_ERROR");
public static final Marker HTTP_ERROR = MarkerFactory.getMarker("COMPUTER_ERROR.HTTP"); public static final Marker HTTP_ERROR = MarkerFactory.getMarker("COMPUTER_ERROR.HTTP");
public static final Marker JAVA_ERROR = MarkerFactory.getMarker("COMPUTER_ERROR.JAVA"); public static final Marker JAVA_ERROR = MarkerFactory.getMarker("COMPUTER_ERROR.JAVA");
public static final Marker VM_ERROR = MarkerFactory.getMarker("COMPUTER_ERROR.VM");
static { static {
HTTP_ERROR.add(COMPUTER_ERROR); HTTP_ERROR.add(COMPUTER_ERROR);
JAVA_ERROR.add(JAVA_ERROR); JAVA_ERROR.add(COMPUTER_ERROR);
VM_ERROR.add(COMPUTER_ERROR);
} }
private Logging() { private Logging() {

View File

@@ -117,7 +117,7 @@ public class RedstoneAPI implements ILuaAPI {
* *
* @param side The side to set. * @param side The side to set.
* @param value The signal strength between 0 and 15. * @param value The signal strength between 0 and 15.
* @throws LuaException If {@code value} is not betwene 0 and 15. * @throws LuaException If {@code value} is not between 0 and 15.
* @cc.since 1.51 * @cc.since 1.51
*/ */
@LuaFunction({ "setAnalogOutput", "setAnalogueOutput" }) @LuaFunction({ "setAnalogOutput", "setAnalogueOutput" })

View File

@@ -42,8 +42,8 @@ public class BinaryWritableHandle extends HandleGeneric {
* *
* @param arguments The value to write. * @param arguments The value to write.
* @throws LuaException If the file has been closed. * @throws LuaException If the file has been closed.
* @cc.tparam [1] number The byte to write. * @cc.tparam [1] number charcode The byte to write.
* @cc.tparam [2] string The string to write. * @cc.tparam [2] string contents The string to write.
* @cc.changed 1.80pr1 Now accepts a string to write multiple bytes. * @cc.changed 1.80pr1 Now accepts a string to write multiple bytes.
*/ */
@LuaFunction @LuaFunction

View File

@@ -51,7 +51,7 @@ public class EncodedWritableHandle extends HandleGeneric {
} }
/** /**
* Write a string of characters to the file, follwing them with a new line character. * Write a string of characters to the file, following them with a new line character.
* *
* @param args The value to write. * @param args The value to write.
* @throws LuaException If the file has been closed. * @throws LuaException If the file has been closed.

View File

@@ -17,8 +17,8 @@ import java.net.URI;
* A version of {@link WebSocketClientHandshaker13} which doesn't add the {@link HttpHeaderNames#ORIGIN} header to the * A version of {@link WebSocketClientHandshaker13} which doesn't add the {@link HttpHeaderNames#ORIGIN} header to the
* original HTTP request. * original HTTP request.
*/ */
public class NoOriginWebSocketHanshakder extends WebSocketClientHandshaker13 { public class NoOriginWebSocketHandshaker extends WebSocketClientHandshaker13 {
public NoOriginWebSocketHanshakder(URI webSocketURL, WebSocketVersion version, String subprotocol, boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) { public NoOriginWebSocketHandshaker(URI webSocketURL, WebSocketVersion version, String subprotocol, boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) {
super(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength); super(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength);
} }

View File

@@ -133,7 +133,7 @@ public class Websocket extends Resource<Websocket> {
} }
var subprotocol = headers.get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL); var subprotocol = headers.get(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL);
WebSocketClientHandshaker handshaker = new NoOriginWebSocketHanshakder( WebSocketClientHandshaker handshaker = new NoOriginWebSocketHandshaker(
uri, WebSocketVersion.V13, subprotocol, true, headers, uri, WebSocketVersion.V13, subprotocol, true, headers,
options.websocketMessage <= 0 ? MAX_MESSAGE_SIZE : options.websocketMessage options.websocketMessage <= 0 ? MAX_MESSAGE_SIZE : options.websocketMessage
); );

View File

@@ -83,6 +83,11 @@ public class CobaltLuaMachine implements ILuaMachine {
} }
}); });
}) })
.errorReporter((e, msg) -> {
if (LOG.isErrorEnabled(Logging.VM_ERROR)) {
LOG.error(Logging.VM_ERROR, "Error occurred in the Lua runtime. Computer will continue to execute:\n{}", msg.get(), e);
}
})
.build(); .build();
globals = new LuaTable(); globals = new LuaTable();

View File

@@ -7,7 +7,7 @@ local expect
do do
local h = fs.open("rom/modules/main/cc/expect.lua", "r") local h = fs.open("rom/modules/main/cc/expect.lua", "r")
local f, err = loadstring(h.readAll(), "@expect.lua") local f, err = loadstring(h.readAll(), "@/rom/modules/main/cc/expect.lua")
h.close() h.close()
if not f then error(err) end if not f then error(err) end
@@ -467,7 +467,7 @@ function loadfile(filename, mode, env)
local file = fs.open(filename, "r") local file = fs.open(filename, "r")
if not file then return nil, "File not found" end if not file then return nil, "File not found" end
local func, err = load(file.readAll(), "@" .. fs.getName(filename), mode, env) local func, err = load(file.readAll(), "@/" .. fs.combine(filename), mode, env)
file.close() file.close()
return func, err return func, err
end end
@@ -700,7 +700,7 @@ settings.define("motd.path", {
settings.define("lua.warn_against_use_of_local", { settings.define("lua.warn_against_use_of_local", {
default = true, default = true,
description = [[Print a message when input in the Lua REPL starts with the word 'local'. Local variables defined in the Lua REPL are be inaccessable on the next input.]], description = [[Print a message when input in the Lua REPL starts with the word 'local'. Local variables defined in the Lua REPL are be inaccessible on the next input.]],
type = "boolean", type = "boolean",
}) })
settings.define("lua.function_args", { settings.define("lua.function_args", {

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