1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-04-09 04:06:41 +00:00

Merge branch 'mc-1.15.x' into mc-1.16.x

This commit is contained in:
Jonathan Coates 2021-03-12 09:26:15 +00:00
commit 8c56b6a7be
71 changed files with 4054 additions and 149 deletions

@ -51,18 +51,10 @@ illuaminate, which spits out our HTML.
For various reasons, getting the environment set up to build documentation can be pretty complex. I'd quite like to
automate this via Docker and/or nix in the future, but this needs to be done manually for now.
First, you will need JDK 9+ (in addition to JDK 8 which is required to build Minecraft itself). Sadly our version of
Gradle doesn't support multiple toolchains, and so you need to install this yourself.
Gradle needs to be told about this JDK via the `JAVA_HOME_11_X64` environment variable or adding `java11Home` to
`~/.gradle/gradle.properties`. On my system this looks like:
```properties
java11Home=/usr/lib/jvm/java-11-openjdk/
```
If you just want to build the documentation stubs for linting, this is enough. However, if you want to build the full
website, you will also need to install a few Node packages by running `npm ci`.
This tooling is only needed if you need to build the whole website. If you just want to generate the Lua stubs, you can
skp this section.
- Install Node/npm and install our Node packages with `npm ci`.
- Install [illuaminate][illuaminate-usage] as described above.
#### Building documentation
Gradle should be your entrypoint to building most documentation. There's two tasks which are of interest:

@ -7,15 +7,15 @@ buildscript {
url = "https://files.minecraftforge.net/maven"
}
maven {
name = "mixin"
url = "https://dist.creeper.host/Sponge/maven"
name = "Sponge (Mixin)"
url = "https://repo.spongepowered.org/repository/maven-public/"
content { includeGroup "org.spongepowered" }
}
}
dependencies {
classpath 'com.google.code.gson:gson:2.8.1'
classpath 'net.minecraftforge.gradle:ForgeGradle:3.0.190'
classpath 'net.minecraftforge.gradle:ForgeGradle:4.1.3'
classpath 'net.sf.proguard:proguard-gradle:6.1.0beta2'
classpath 'org.ajoberstar.grgit:grgit-gradle:3.0.0'
classpath 'org.spongepowered:mixingradle:0.7-SNAPSHOT'
}
}
@ -24,14 +24,13 @@ plugins {
id "checkstyle"
id "jacoco"
id "com.github.hierynomus.license" version "0.15.0"
id "com.matthewprenger.cursegradle" version "1.3.0"
id "com.github.breadmoirai.github-release" version "2.2.4"
id "com.matthewprenger.cursegradle" version "1.4.0"
id "com.github.breadmoirai.github-release" version "2.2.12"
id "org.jetbrains.kotlin.jvm" version "1.3.72"
}
apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'org.spongepowered.mixin'
apply plugin: 'org.ajoberstar.grgit'
apply plugin: 'maven-publish'
apply plugin: 'maven'
@ -40,7 +39,11 @@ version = mod_version
group = "org.squiddev"
archivesBaseName = "cc-tweaked-${mc_version}"
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(8)
}
}
minecraft {
runs {
@ -172,11 +175,10 @@ task luaJavadoc(type: Javadoc) {
options.docletpath = configurations.cctJavadoc.files as List
options.doclet = "cc.tweaked.javadoc.LuaDoclet"
options.noTimestamp = false
// Attempt to run under Java 11 (any Java >= 9 will work though).
if(System.getProperty("java.version").startsWith("1.")
&& (System.getenv("JAVA_HOME_11_X64") != null || project.hasProperty("java11Home"))) {
executable = "${System.getenv("JAVA_HOME_11_X64") ?: project.property("java11Home")}/bin/javadoc"
javadocTool = javaToolchains.javadocToolFor {
languageVersion = JavaLanguageVersion.of(11)
}
}
@ -214,7 +216,6 @@ import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.hierynomus.gradle.license.tasks.LicenseCheck
import com.hierynomus.gradle.license.tasks.LicenseFormat
import org.ajoberstar.grgit.Grgit
import proguard.gradle.ProGuardTask
task proguard(type: ProGuardTask, dependsOn: jar) {
@ -267,16 +268,15 @@ processResources {
def hash = 'none'
Set<String> contributors = []
try {
def grgit = Grgit.open(dir: '.')
hash = grgit.head().id
hash = ["git", "-C", projectDir, "rev-parse", "HEAD"].execute().text.trim()
def blacklist = ['GitHub', 'dan200', 'Daniel Ratcliffe']
grgit.log().each {
if (!blacklist.contains(it.author.name)) contributors.add(it.author.name)
if (!blacklist.contains(it.committer.name)) contributors.add(it.committer.name)
["git", "-C", projectDir, "log", "--format=tformat:%an%n%cn"].execute().text.split('\n').each {
if (!blacklist.contains(it)) contributors.add(it)
}
} catch(Exception ignored) { }
} catch(Exception e) {
e.printStackTrace()
}
inputs.property "commithash", hash
from(sourceSets.main.resources.srcDirs) {
@ -497,8 +497,8 @@ tasks.register('jacocoTestInGameReport', JacocoReport.class).configure {
it.dependsOn('testInGame')
it.executionData(new File(buildDir, 'jacoco/testInGame.exec'))
it.setSourceDirectories(project.files(sourceSets.main.allJava.srcDirs))
it.setClassDirectories(project.files(new File(buildDir, 'jacocoClassDump/testInGame')))
it.sourceDirectories.from(sourceSets.main.allJava.srcDirs)
it.classDirectories.from(new File(buildDir, 'jacocoClassDump/testInGame'))
it.reports {
xml.enabled true
@ -623,18 +623,23 @@ githubRelease {
token project.hasProperty('githubApiKey') ? project.githubApiKey : ''
owner 'SquidDev-CC'
repo 'CC-Tweaked'
try {
targetCommitish = Grgit.open(dir: '.').branch.current().name
} catch(Exception ignored) { }
targetCommitish.set(project.provider({
try {
return ["git", "-C", projectDir, "rev-parse", "--abbrev-ref", "HEAD"].execute().text.trim()
} catch (Exception e) {
e.printStackTrace()
}
return "master"
}))
tagName "v${mc_version}-${mod_version}"
releaseName "[${mc_version}] ${mod_version}"
body {
body.set(project.provider({
"## " + new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.txt")
.readLines()
.takeWhile { it != 'Type "help changelog" to see the full version history.' }
.join("\n").trim()
}
}))
prerelease false
}

21
doc/events/alarm.md Normal file

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

@ -0,0 +1,18 @@
---
module: [kind=event] computer_command
---
The @{computer_command} event is fired when the `/computercraft queue` command is run for the current computer.
## Return Values
1. @{string}: The event name.
... @{string}: The arguments passed to the command.
## Example
Prints the contents of messages sent:
```lua
while true do
local event = {os.pullEvent("computer_command")}
print("Received message:", table.unpack(event, 2))
end
```

19
doc/events/disk.md Normal file

@ -0,0 +1,19 @@
---
module: [kind=event] disk
see: disk_eject For the event sent when a disk is removed.
---
The @{disk} event is fired when a disk is inserted into an adjacent or networked disk drive.
## Return Values
1. @{string}: The event name.
2. @{string}: The side of the disk drive that had a disk inserted.
## Example
Prints a message when a disk is inserted:
```lua
while true do
local event, side = os.pullEvent("disk")
print("Inserted a disk on side " .. side)
end
```

19
doc/events/disk_eject.md Normal file

@ -0,0 +1,19 @@
---
module: [kind=event] disk_eject
see: disk For the event sent when a disk is inserted.
---
The @{disk_eject} event is fired when a disk is removed from an adjacent or networked disk drive.
## Return Values
1. @{string}: The event name.
2. @{string}: The side of the disk drive that had a disk removed.
## Example
Prints a message when a disk is removed:
```lua
while true do
local event, side = os.pullEvent("disk_eject")
print("Removed a disk on side " .. side)
end
```

14
doc/events/http_check.md Normal file

@ -0,0 +1,14 @@
---
module: [kind=event] http_check
see: http.checkURLAsync To check a URL asynchronously.
---
The @{http_check} event is fired when a URL check finishes.
This event is normally handled inside @{http.checkURL}, but it can still be seen when using @{http.checkURLAsync}.
## Return Values
1. @{string}: The event name.
2. @{string}: The URL requested to be checked.
3. @{boolean}: Whether the check succeeded.
4. @{string|nil}: If the check failed, a reason explaining why the check failed.

@ -0,0 +1,39 @@
---
module: [kind=event] http_failure
see: http.request To send an HTTP request.
---
The @{http_failure} event is fired when an HTTP request fails.
This event is normally handled inside @{http.get} and @{http.post}, but it can still be seen when using @{http.request}.
## Return Values
1. @{string}: The event name.
2. @{string}: The URL of the site requested.
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.
## Example
Prints an error why the website cannot be contacted:
```lua
local myURL = "https://does.not.exist.tweaked.cc"
http.request(myURL)
local event, url, err
repeat
event, url, err = os.pullEvent("http_failure")
until url == myURL
print("The URL " .. url .. " could not be reached: " .. err)
```
Prints the contents of a webpage that does not exist:
```lua
local myURL = "https://tweaked.cc/this/does/not/exist"
http.request(myURL)
local event, url, err, handle
repeat
event, url, err, handle = os.pullEvent("http_failure")
until url == myURL
print("The URL " .. url .. " could not be reached: " .. err)
print(handle.getResponseCode())
handle.close()
```

@ -0,0 +1,27 @@
---
module: [kind=event] http_success
see: http.request To make an HTTP request.
---
The @{http_success} event is fired when an HTTP request returns successfully.
This event is normally handled inside @{http.get} and @{http.post}, but it can still be seen when using @{http.request}.
## Return Values
1. @{string}: The event name.
2. @{string}: The URL of the site requested.
3. @{http.Response}: The handle for the response text.
## Example
Prints the content of a website (this may fail if the request fails):
```lua
local myURL = "https://tweaked.cc/"
http.request(myURL)
local event, url, handle
repeat
event, url, handle = os.pullEvent("http_success")
until url == myURL
print("Contents of " .. url .. ":")
print(handle.readAll())
handle.close()
```

@ -11,9 +11,9 @@ If the button pressed represented a printable character, then the @{key} event w
event. If you are consuming text input, use a @{char} event instead!
## Return values
1. [`string`]: The event name.
2. [`number`]: The numerical key value of the key pressed.
3. [`boolean`]: Whether the key event was generated while holding the key (@{true}), rather than pressing it the first time (@{false}).
1. @{string}: The event name.
2. @{number}: The numerical key value of the key pressed.
3. @{boolean}: Whether the key event was generated while holding the key (@{true}), rather than pressing it the first time (@{false}).
## Example
Prints each key when the user presses it, and if the key is being held.

@ -0,0 +1,22 @@
---
module: [kind=event] modem_message
---
The @{modem_message} event is fired when a message is received on an open channel on any modem.
## Return Values
1. @{string}: The event name.
2. @{string}: The side of the modem that received the message.
3. @{number}: The channel that the message was sent on.
4. @{number}: The reply channel set by the sender.
5. @{any}: The message as sent by the sender.
6. @{number}: The distance between the sender and the receiver, in blocks (decimal).
## Example
Prints a message when one is sent:
```lua
while true do
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)))
end
```

@ -0,0 +1,18 @@
---
module: [kind=event] monitor_resize
---
The @{monitor_resize} event is fired when an adjacent or networked monitor's size is changed.
## Return Values
1. @{string}: The event name.
2. @{string}: The side or network ID of the monitor that resized.
## Example
Prints a message when a monitor is resized:
```lua
while true do
local event, side = os.pullEvent("monitor_resize")
print("The monitor on side " .. side .. " was resized.")
end
```

@ -0,0 +1,20 @@
---
module: [kind=event] monitor_touch
---
The @{monitor_touch} event is fired when an adjacent or networked Advanced Monitor is right-clicked.
## Return Values
1. @{string}: The event name.
2. @{string}: The side or network ID of the monitor that was touched.
3. @{number}: The X coordinate of the touch, in characters.
4. @{number}: The Y coordinate of the touch, in characters.
## Example
Prints a message when a monitor is touched:
```lua
while true do
local event, side, x, y = os.pullEvent("monitor_touch")
print("The monitor on side " .. side .. " was touched at (" .. x .. ", " .. y .. ")")
end
```

@ -20,5 +20,3 @@ while true do
print(("The mouse button %s was dragged at %d, %d"):format(button, x, y))
end
```

@ -19,6 +19,3 @@ while true do
print(("The mouse button %s was released at %d, %d"):format(button, x, y))
end
```
[`string`]: string
[`number`]: number

18
doc/events/paste.md Normal file

@ -0,0 +1,18 @@
---
module: [kind=event] paste
---
The @{paste} event is fired when text is pasted into the computer through Ctrl-V (or ⌘V on Mac).
## Return values
1. @{string}: The event name.
2. @{string} The text that was pasted.
## Example
Prints pasted text:
```lua
while true do
local event, text = os.pullEvent("paste")
print('"' .. text .. '" was pasted')
end
```

19
doc/events/peripheral.md Normal file

@ -0,0 +1,19 @@
---
module: [kind=event] peripheral
see: peripheral_detach For the event fired when a peripheral is detached.
---
The @{peripheral} event is fired when a peripheral is attached on a side or to a modem.
## Return Values
1. @{string}: The event name.
2. @{string}: The side the peripheral was attached to.
## Example
Prints a message when a peripheral is attached:
```lua
while true do
local event, side = os.pullEvent("peripheral")
print("A peripheral was attached on side " .. side)
end
```

@ -0,0 +1,19 @@
---
module: [kind=event] peripheral_detach
see: peripheral For the event fired when a peripheral is attached.
---
The @{peripheral_detach} event is fired when a peripheral is detached from a side or from a modem.
## Return Values
1. @{string}: The event name.
2. @{string}: The side the peripheral was detached from.
## Example
Prints a message when a peripheral is detached:
```lua
while true do
local event, side = os.pullEvent("peripheral_detach")
print("A peripheral was detached on side " .. side)
end
```

@ -0,0 +1,30 @@
---
module: [kind=event] rednet_message
see: modem_message For raw modem messages sent outside of Rednet.
see: rednet.receive To wait for a Rednet message with an optional timeout and protocol filter.
---
The @{rednet_message} event is fired when a message is sent over Rednet.
This event is usually handled by @{rednet.receive}, but it can also be pulled manually.
@{rednet_message} events are sent by @{rednet.run} in the top-level coroutine in response to @{modem_message} events. A @{rednet_message} event is always preceded by a @{modem_message} event. They are generated inside CraftOS rather than being sent by the ComputerCraft machine.
## Return Values
1. @{string}: The event name.
2. @{number}: The ID of the sending computer.
3. @{any}: The message sent.
4. @{string|nil}: The protocol of the message, if provided.
## Example
Prints a message when one is sent:
```lua
while true do
local event, sender, message, protocol = os.pullEvent("rednet_message")
if protocol ~= nil then
print("Received message from " .. sender .. " with protocol " .. protocol .. " and message " .. tostring(message))
else
print("Received message from " .. sender .. " with message " .. tostring(message))
end
end
```

14
doc/events/redstone.md Normal file

@ -0,0 +1,14 @@
---
module: [kind=event] redstone
---
The @{redstone} event is fired whenever any redstone inputs on the computer change.
## Example
Prints a message when a redstone input changes:
```lua
while true do
os.pullEvent("redstone")
print("A redstone input has changed!")
end
```

@ -0,0 +1,28 @@
---
module: [kind=event] task_complete
see: commands.execAsync To run a command which fires a task_complete event.
---
The @{task_complete} event is fired when an asynchronous task completes. This is usually handled inside the function call that queued the task; however, functions such as @{commands.execAsync} return immediately so the user can wait for completion.
## Return Values
1. @{string}: The event name.
2. @{number}: The ID of the task that completed.
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.)
...: Any parameters returned from the command.
## Example
Prints the results of an asynchronous command:
```lua
local taskID = commands.execAsync("say Hello")
local event
repeat
event = {os.pullEvent("task_complete")}
until event[2] == taskID
if event[3] == true then
print("Task " .. event[2] .. " succeeded:", table.unpack(event, 4))
else
print("Task " .. event[2] .. " failed: " .. event[4])
end
```

15
doc/events/term_resize.md Normal file

@ -0,0 +1,15 @@
---
module: [kind=event] term_resize
---
The @{term_resize} event is fired when the main terminal is resized, mainly when a new tab is opened or closed in @{multishell}.
## Example
Prints :
```lua
while true do
os.pullEvent("term_resize")
local w, h = term.getSize()
print("The term was resized to (" .. w .. ", " .. h .. ")")
end
```

25
doc/events/terminate.md Normal file

@ -0,0 +1,25 @@
---
module: [kind=event] terminate
---
The @{terminate} event is fired when <kbd>Ctrl-T</kbd> is held down.
This event is normally handled by @{os.pullEvent}, and will not be returned. However, @{os.pullEventRaw} will return this event when fired.
@{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}.
## Example
Prints a message when Ctrl-T is held:
```lua
while true do
local event = os.pullEventRaw("terminate")
if event == "terminate" then print("Terminate requested!") end
end
```
Exits when Ctrl-T is held:
```lua
while true do
os.pullEvent()
end
```

21
doc/events/timer.md Normal file

@ -0,0 +1,21 @@
---
module: [kind=event] timer
see: os.startTimer To start a timer.
---
The @{timer} event is fired when a timer started with @{os.startTimer} completes.
## Return Values
1. @{string}: The event name.
2. @{number}: The ID of the timer that finished.
## Example
Starts a timer and then prints its ID:
```lua
local timerID = os.startTimer(2)
local event, id
repeat
event, id = os.pullEvent("timer")
until id == timerID
print("Timer with ID " .. id .. " was fired")
```

@ -0,0 +1,14 @@
---
module: [kind=event] turtle_inventory
---
The @{turtle_inventory} event is fired when a turtle's inventory is changed.
## Example
Prints a message when the inventory is changed:
```lua
while true do
os.pullEvent("turtle_inventory")
print("The inventory was changed.")
end
```

@ -0,0 +1,21 @@
---
module: [kind=event] websocket_closed
---
The @{websocket_closed} event is fired when an open WebSocket connection is closed.
## Return Values
1. @{string}: The event name.
2. @{string}: The URL of the WebSocket that was closed.
## Example
Prints a message when a WebSocket is closed (this may take a minute):
```lua
local myURL = "wss://example.tweaked.cc/echo"
local ws = http.websocket(myURL)
local event, url
repeat
event, url = os.pullEvent("websocket_closed")
until url == myURL
print("The WebSocket at " .. url .. " was closed.")
```

@ -0,0 +1,25 @@
---
module: [kind=event] websocket_failure
see: http.websocketAsync To send an HTTP request.
---
The @{websocket_failure} event is fired when a WebSocket connection request fails.
This event is normally handled inside @{http.websocket}, but it can still be seen when using @{http.websocketAsync}.
## Return Values
1. @{string}: The event name.
2. @{string}: The URL of the site requested.
3. @{string}: An error describing the failure.
## Example
Prints an error why the website cannot be contacted:
```lua
local myURL = "wss://example.tweaked.cc/not-a-websocket"
http.websocketAsync(myURL)
local event, url, err
repeat
event, url, err = os.pullEvent("websocket_failure")
until url == myURL
print("The URL " .. url .. " could not be reached: " .. err)
```

@ -0,0 +1,26 @@
---
module: [kind=event] websocket_message
---
The @{websocket_message} event is fired when a message is received on an open WebSocket connection.
This event is normally handled by @{http.Websocket.receive}, but it can also be pulled manually.
## Return Values
1. @{string}: The event name.
2. @{string}: The URL of the WebSocket.
3. @{string}: The contents of the message.
## Example
Prints a message sent by a WebSocket:
```lua
local myURL = "wss://example.tweaked.cc/echo"
local ws = http.websocket(myURL)
ws.send("Hello!")
local event, url, message
repeat
event, url, message = os.pullEvent("websocket_message")
until url == myURL
print("Received message from " .. url .. " with contents " .. message)
ws.close()
```

@ -0,0 +1,28 @@
---
module: [kind=event] websocket_success
see: http.websocketAsync To open a WebSocket asynchronously.
---
The @{websocket_success} event is fired when a WebSocket connection request returns successfully.
This event is normally handled inside @{http.websocket}, but it can still be seen when using @{http.websocketAsync}.
## Return Values
1. @{string}: The event name.
2. @{string}: The URL of the site.
3. @{http.Websocket}: The handle for the WebSocket.
## Example
Prints the content of a website (this may fail if the request fails):
```lua
local myURL = "wss://example.tweaked.cc/echo"
http.websocketAsync(myURL)
local event, url, handle
repeat
event, url, handle = os.pullEvent("websocket_success")
until url == myURL
print("Connected to " .. url)
handle.send("Hello!")
print(handle.receive())
handle.close()
```

@ -58,10 +58,10 @@ function request(...) end
-- @treturn string A message detailing why the request failed.
-- @treturn Response|nil The failing http response, if available.
--
-- @usage Make a request to [example.computercraft.cc](https://example.computercraft.cc),
-- @usage Make a request to [example.tweaked.cc](https://example.tweaked.cc),
-- and print the returned page.
-- ```lua
-- local request = http.get("https://example.computercraft.cc")
-- local request = http.get("https://example.tweaked.cc")
-- print(request.readAll())
-- -- => HTTP is working!
-- request.close()
@ -123,7 +123,7 @@ function checkURLAsync(url) end
--
-- @usage
-- ```lua
-- print(http.checkURL("https://example.computercraft.cc/"))
-- print(http.checkURL("https://example.tweaked.cc/"))
-- -- => true
-- print(http.checkURL("http://localhost/"))
-- -- => false Domain not permitted

@ -1,5 +1,5 @@
# Mod properties
mod_version=1.95.2
mod_version=1.95.3
# Minecraft properties (update mods.toml when changing)
mc_version=1.16.4

Binary file not shown.

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

53
gradlew vendored

@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@ -66,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@ -109,10 +126,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
@ -138,19 +156,19 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@ -159,14 +177,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

43
gradlew.bat vendored

@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -45,28 +64,14 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell

@ -14,6 +14,7 @@ import dan200.computercraft.core.apis.http.*;
import dan200.computercraft.core.apis.http.request.HttpRequest;
import dan200.computercraft.core.apis.http.websocket.Websocket;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
@ -179,7 +180,7 @@ public class HTTPAPI implements ILuaAPI
}
@Nonnull
private static HttpHeaders getHeaders( @Nonnull Map<?, ?> headerTable ) throws LuaException
private HttpHeaders getHeaders( @Nonnull Map<?, ?> headerTable ) throws LuaException
{
HttpHeaders headers = new DefaultHttpHeaders();
for( Map.Entry<?, ?> entry : headerTable.entrySet() )
@ -197,6 +198,11 @@ public class HTTPAPI implements ILuaAPI
}
}
}
if( !headers.contains( HttpHeaderNames.USER_AGENT ) )
{
headers.set( HttpHeaderNames.USER_AGENT, apiEnvironment.getComputerEnvironment().getUserAgent() );
}
return headers;
}
}

@ -191,7 +191,7 @@ public class RedstoneAPI implements ILuaAPI
@LuaFunction
public final int getBundledInput( ComputerSide side )
{
return environment.getBundledOutput( side );
return environment.getBundledInput( side );
}
/**

@ -80,10 +80,6 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
{
request.headers().set( HttpHeaderNames.ACCEPT_CHARSET, "UTF-8" );
}
if( !request.headers().contains( HttpHeaderNames.USER_AGENT ) )
{
request.headers().set( HttpHeaderNames.USER_AGENT, this.request.environment().getComputerEnvironment().getUserAgent() );
}
request.headers().set( HttpHeaderNames.HOST, uri.getPort() < 0 ? uri.getHost() : uri.getHost() + ":" + uri.getPort() );
request.headers().set( HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE );

@ -58,10 +58,10 @@ public class HttpResponseHandle implements ObjectSource
* If multiple headers are sent with the same name, they will be combined with a comma.
*
* @return The response's headers.
* @cc.usage Make a request to [example.computercraft.cc](https://example.computercraft.cc), and print the
* @cc.usage Make a request to [example.tweaked.cc](https://example.tweaked.cc), and print the
* returned headers.
* <pre>{@code
* local request = http.get("https://example.computercraft.cc")
* local request = http.get("https://example.tweaked.cc")
* print(textutils.serialize(request.getResponseHeaders()))
* -- => {
* -- [ "Content-Type" ] = "text/plain; charset=utf8",

@ -22,14 +22,12 @@ import javax.annotation.Nonnull;
public class BlockModelProvider extends BlockStateProvider
{
private final ModelFile monitorBase;
private final ModelFile orientable;
private ModelFile monitorBase;
private ModelFile orientable;
public BlockModelProvider( DataGenerator generator, ExistingFileHelper existingFileHelper )
{
super( generator, ComputerCraft.MOD_ID, existingFileHelper );
monitorBase = models().getExistingFile( new ResourceLocation( ComputerCraft.MOD_ID, "block/monitor_base" ) );
orientable = models().getExistingFile( new ResourceLocation( "block/orientable" ) );
}
@Nonnull
@ -42,6 +40,9 @@ public class BlockModelProvider extends BlockStateProvider
@Override
protected void registerStatesAndModels()
{
monitorBase = models().getExistingFile( new ResourceLocation( ComputerCraft.MOD_ID, "block/monitor_base" ) );
orientable = models().getExistingFile( new ResourceLocation( "block/orientable" ) );
registerMonitors( Registry.ModBlocks.MONITOR_NORMAL.get() );
registerMonitors( Registry.ModBlocks.MONITOR_ADVANCED.get() );

@ -52,7 +52,7 @@ public final class RecordMedia implements IMedia
try
{
return ObfuscationReflectionHelper.getPrivateValue( MusicDiscItem.class, (MusicDiscItem) item, "sound" );
return ObfuscationReflectionHelper.getPrivateValue( MusicDiscItem.class, (MusicDiscItem) item, "field_185076_b" );
}
catch( UnableToAccessFieldException | UnableToFindFieldException e )
{

@ -9,6 +9,7 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.network.NetworkHandler;
import dan200.computercraft.shared.network.client.MonitorClientMessage;
import dan200.computercraft.shared.network.client.TerminalState;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
@ -28,6 +29,7 @@ import java.util.Queue;
public final class MonitorWatcher
{
private static final Queue<TileMonitor> watching = new ArrayDeque<>();
private static final Queue<PlayerUpdate> playerUpdates = new ArrayDeque<>();
private MonitorWatcher()
{
@ -58,10 +60,8 @@ public final class MonitorWatcher
ServerMonitor serverMonitor = getMonitor( monitor );
if( serverMonitor == null || monitor.enqueued ) continue;
// We use the cached terminal state if available - this is guaranteed to
TerminalState state = monitor.cached;
if( state == null ) state = monitor.cached = serverMonitor.write();
NetworkHandler.sendToPlayer( event.getPlayer(), new MonitorClientMessage( monitor.getBlockPos(), state ) );
// The chunk hasn't been sent to the client yet, so we can't send an update. Do it on tick end.
playerUpdates.add( new PlayerUpdate( event.getPlayer(), monitor ) );
}
}
@ -70,6 +70,23 @@ public final class MonitorWatcher
{
if( event.phase != TickEvent.Phase.END ) return;
PlayerUpdate playerUpdate;
while( (playerUpdate = playerUpdates.poll()) != null )
{
TileMonitor tile = playerUpdate.monitor;
if( tile.enqueued || tile.isRemoved() ) continue;
ServerMonitor monitor = getMonitor( tile );
if( monitor == null ) continue;
// Some basic sanity checks to the player. It's possible they're no longer within range, but that's harder
// to track efficiently.
ServerPlayerEntity player = playerUpdate.player;
if( !player.isAlive() || player.getLevel() != tile.getLevel() ) continue;
NetworkHandler.sendToPlayer( playerUpdate.player, new MonitorClientMessage( tile.getBlockPos(), getState( tile, monitor ) ) );
}
long limit = ComputerCraft.monitorBandwidth;
boolean obeyLimit = limit > 0;
@ -90,7 +107,7 @@ public final class MonitorWatcher
continue;
}
TerminalState state = tile.cached = monitor.write();
TerminalState state = getState( tile, monitor );
NetworkHandler.sendToAllTracking( new MonitorClientMessage( pos, state ), chunk );
limit -= state.size();
@ -101,4 +118,23 @@ public final class MonitorWatcher
{
return !monitor.isRemoved() && monitor.getXIndex() == 0 && monitor.getYIndex() == 0 ? monitor.getCachedServerMonitor() : null;
}
private static TerminalState getState( TileMonitor tile, ServerMonitor monitor )
{
TerminalState state = tile.cached;
if( state == null ) state = tile.cached = monitor.write();
return state;
}
private static final class PlayerUpdate
{
final ServerPlayerEntity player;
final TileMonitor monitor;
private PlayerUpdate( ServerPlayerEntity player, TileMonitor monitor )
{
this.player = player;
this.monitor = monitor;
}
}
}

@ -182,6 +182,10 @@ public class TurtleAPI implements ILuaAPI
/**
* Place a block or item into the world in front of the turtle.
*
* "Placing" an item allows it to interact with blocks and entities in front of the turtle. For instance, buckets
* can pick up and place down fluids, and wheat can be used to breed cows. However, you cannot use {@link #place} to
* perform arbitrary block interactions, such as clicking buttons or flipping levers.
*
* @param args Arguments to place.
* @return The turtle command result.
* @cc.tparam [opt] string text When placing a sign, set its contents to this text.
@ -202,6 +206,7 @@ public class TurtleAPI implements ILuaAPI
* @cc.tparam [opt] string text When placing a sign, set its contents to this text.
* @cc.treturn boolean Whether the block could be placed.
* @cc.treturn string|nil The reason the block was not placed.
* @see #place For more information about placing items.
*/
@LuaFunction
public final MethodResult placeUp( IArguments args )
@ -217,6 +222,7 @@ public class TurtleAPI implements ILuaAPI
* @cc.tparam [opt] string text When placing a sign, set its contents to this text.
* @cc.treturn boolean Whether the block could be placed.
* @cc.treturn string|nil The reason the block was not placed.
* @see #place For more information about placing items.
*/
@LuaFunction
public final MethodResult placeDown( IArguments args )

@ -15,10 +15,8 @@ import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import javax.annotation.Nonnull;
import java.lang.reflect.Method;
import java.util.List;
public class TurtleCompareCommand implements ITurtleCommand
@ -52,19 +50,6 @@ public class TurtleCompareCommand implements ITurtleCommand
Block lookAtBlock = lookAtState.getBlock();
if( !lookAtBlock.isAir( lookAtState, world, newPosition ) )
{
// Try getSilkTouchDrop first
if( !lookAtBlock.hasTileEntity( lookAtState ) )
{
try
{
Method method = ObfuscationReflectionHelper.findMethod( Block.class, "func_180643_i", BlockState.class );
lookAtStack = (ItemStack) method.invoke( lookAtBlock, lookAtState );
}
catch( ReflectiveOperationException | RuntimeException ignored )
{
}
}
// See if the block drops anything with the same ID as itself
// (try 5 times to try and beat random number generators)
for( int i = 0; i < 5 && lookAtStack.isEmpty(); i++ )

@ -38,5 +38,21 @@
"upgrade.computercraft.speaker.adjective": "(Alto-Falante)",
"chat.computercraft.wired_modem.peripheral_connected": "Periférico \"%s\" conectado à rede",
"chat.computercraft.wired_modem.peripheral_disconnected": "Periférico \"%s\" desconectado da rede",
"gui.computercraft.tooltip.copy": "Copiar para a área de transferência"
"gui.computercraft.tooltip.copy": "Copiar para a área de transferência",
"commands.computercraft.tp.synopsis": "Teleprota para um computador específico.",
"commands.computercraft.turn_on.done": "Ligou %s/%s computadores",
"commands.computercraft.turn_on.desc": "Liga os computadores em escuta. Você pode especificar o id de instância do computador (ex.: 123), id do computador (ex.: #123) ou o rótulo (ex.: \"@MeuComputador\").",
"commands.computercraft.turn_on.synopsis": "Liga computadores remotamente.",
"commands.computercraft.shutdown.done": "Desliga %s/%s computadores",
"commands.computercraft.shutdown.desc": "Desliga os computadores em escuta ou todos caso não tenha sido especificado. Você pode especificar o id de instância do computador (ex.: 123), id do computador (ex.: #123) ou o rótulo (ex.: \"@MeuComputador\").",
"commands.computercraft.shutdown.synopsis": "Desliga computadores remotamente.",
"commands.computercraft.dump.action": "Ver mais informação sobre este computador",
"commands.computercraft.dump.desc": "Mostra o status de todos os computadores ou uma informação específica sobre um computador. Você pode especificar o id de instância do computador (ex.: 123), id do computador (ex.: #123) ou o rótulo (ex.: \"@MeuComputador\").",
"commands.computercraft.dump.synopsis": "Mostra status de computadores.",
"commands.computercraft.help.no_command": "Comando '%s' não existe",
"commands.computercraft.help.no_children": "%s não tem sub-comandos",
"commands.computercraft.help.desc": "Mostra essa mensagem de ajuda",
"commands.computercraft.help.synopsis": "Providencia ajuda para um comando específico",
"commands.computercraft.desc": "O comando /computercraft providencia várias ferramentas de depuração e administração para controle e interação com computadores.",
"commands.computercraft.synopsis": "Vários comandos para controlar computadores."
}

@ -1,3 +1,12 @@
# New features in CC: Tweaked 1.95.3
Several bug fixes:
* Correctly serialise sparse arrays into JSON (livegamer999)
* Fix hasAudio/playAudio failing on record discs.
* Fix rs.getBundledInput returning the output instead (SkyTheCodeMaster)
* Programs run via edit are now a little better behaved (Wojbie)
* Add User-Agent to a websocket's headers.
# New features in CC: Tweaked 1.95.2
* Add `isReadOnly` to `fs.attributes` (Lupus590)

@ -1,14 +1,10 @@
New features in CC: Tweaked 1.95.2
* Add `isReadOnly` to `fs.attributes` (Lupus590)
* Many more programs now support numpad enter (Wojbie)
New features in CC: Tweaked 1.95.3
Several bug fixes:
* Fix some commands failing to parse on dedicated servers.
* Fix all disk recipes appearing to produce a white disk in JEI/recipe book.
* Hopefully improve edit's behaviour with AltGr on some European keyboards.
* Prevent files being usable after their mount was removed.
* Fix the `id` program crashing on non-disk items (Wojbie).
* Preserve registration order of turtle/pocket upgrades when displaying in JEI.
* Correctly serialise sparse arrays into JSON (livegamer999)
* Fix hasAudio/playAudio failing on record discs.
* Fix rs.getBundledInput returning the output instead (SkyTheCodeMaster)
* Programs run via edit are now a little better behaved (Wojbie)
* Add User-Agent to a websocket's headers.
Type "help changelog" to see the full version history.

@ -1,5 +1,5 @@
Please report bugs at https://github.com/SquidDev-CC/CC-Tweaked. Thanks!
View the documentation at https://wiki.computercraft.cc
View the documentation at https://tweaked.cc
Show off your programs or ask for help at our forum: https://forums.computercraft.cc
You can disable these messages by running "set motd.enable false".
Use "pastebin put" to upload a program to pastebin.

@ -47,6 +47,30 @@ else
stringColour = colours.white
end
local runHandler = [[multishell.setTitle(multishell.getCurrent(), %q)
local current = term.current()
local ok, err = load(%q, %q, nil, _ENV)
if ok then ok, err = pcall(ok, ...) end
term.redirect(current)
term.setTextColor(term.isColour() and colours.yellow or colours.white)
term.setBackgroundColor(colours.black)
term.setCursorBlink(false)
local _, y = term.getCursorPos()
local _, h = term.getSize()
if not ok then
printError(err)
end
if ok and y >= h then
term.scroll(1)
end
term.setCursorPos(1, h)
if ok then
write("Program finished. ")
end
write("Press any key to continue")
os.pullEvent('key')
]]
-- Menus
local bMenu = false
local nMenuItem = 1
@ -89,7 +113,7 @@ local function load(_sPath)
end
end
local function save(_sPath)
local function save(_sPath, fWrite)
-- Create intervening folder
local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len())
if not fs.exists(sDir) then
@ -101,8 +125,8 @@ local function save(_sPath)
local function innerSave()
file, fileerr = fs.open(_sPath, "w")
if file then
for _, sLine in ipairs(tLines) do
file.write(sLine .. "\n")
if file then
fWrite(file)
end
else
error("Failed to open " .. _sPath)
@ -293,7 +317,11 @@ local tMenuFuncs = {
if bReadOnly then
sStatus = "Access denied"
else
local ok, _, fileerr = save(sPath)
local ok, _, fileerr = save(sPath, function(file)
for _, sLine in ipairs(tLines) do
file.write(sLine .. "\n")
end
end)
if ok then
sStatus = "Saved to " .. sPath
else
@ -390,8 +418,18 @@ local tMenuFuncs = {
bRunning = false
end,
Run = function()
local sTempPath = "/.temp"
local ok = save(sTempPath)
local sTitle = fs.getName(sPath)
if sTitle:sub(-4) == ".lua" then
sTitle = sTitle:sub(1, -5)
end
local sTempPath = bReadOnly and ".temp." .. sTitle or fs.combine(fs.getDir(sPath), ".temp." .. sTitle)
if fs.exists(sTempPath) then
sStatus = "Error saving to " .. sTempPath
return
end
local ok = save(sTempPath, function(file)
file.write(runHandler:format(sTitle, table.concat(tLines, "\n"), "@" .. fs.getName(sPath)))
end)
if ok then
local nTask = shell.openTab(sTempPath)
if nTask then

@ -10,7 +10,7 @@ class ComputerTest {
/**
* Ensures redstone signals do not travel through computers.
*
* @see [Issue #548](https://github.com/SquidDev-CC/CC-Tweaked/issues/548)
* @see [#548](https://github.com/SquidDev-CC/CC-Tweaked/issues/548)
*/
@GameTest
suspend fun `No through signal`(context: TestContext) {

@ -0,0 +1,27 @@
package dan200.computercraft.ingame
import dan200.computercraft.ingame.api.*
import net.minecraft.entity.item.ItemEntity
import net.minecraft.item.Items
import net.minecraft.util.math.BlockPos
import org.junit.jupiter.api.Assertions.assertEquals
class DiskDriveTest {
/**
* Ensure audio disks exist and we can play them.
*
* @see [#688](https://github.com/SquidDev-CC/CC-Tweaked/issues/688)
*/
@GameTest
suspend fun `Audio disk`(context: TestContext) = context.checkComputerOk(3)
@GameTest
suspend fun `Ejects disk`(context: TestContext) {
val stackAt = BlockPos(2, 0, 2)
context.checkComputerOk(4)
context.waitUntil { context.getEntity(stackAt) != null }
val stack = context.getEntityOfType<ItemEntity>(stackAt)!!
assertEquals(Items.MUSIC_DISC_13, stack.item.item, "Correct item stack")
}
}

@ -7,4 +7,44 @@ import dan200.computercraft.ingame.api.checkComputerOk
class TurtleTest {
@GameTest(required = false)
suspend fun `Unequip refreshes peripheral`(context: TestContext) = context.checkComputerOk(1)
/**
* Checks turtles can sheer sheep (and drop items)
*
* @see [#537](https://github.com/SquidDev-CC/CC-Tweaked/issues/537)
*/
@GameTest(required = false)
suspend fun `Shears sheep`(context: TestContext) = context.checkComputerOk(5)
/**
* Checks turtles can place lava.
*
* @see [#518](https://github.com/SquidDev-CC/CC-Tweaked/issues/518)
*/
@GameTest(required = false)
suspend fun `Place lava`(context: TestContext) = context.checkComputerOk(5)
/**
* Checks turtles can place when waterlogged.
*
* @see [#385](https://github.com/SquidDev-CC/CC-Tweaked/issues/385)
*/
@GameTest(required = false)
suspend fun `Place waterlogged`(context: TestContext) = context.checkComputerOk(7)
/**
* Checks turtles can place when waterlogged.
*
* @see [#297](https://github.com/SquidDev-CC/CC-Tweaked/issues/297)
*/
@GameTest(required = false)
suspend fun `Gather lava`(context: TestContext) = context.checkComputerOk(8)
/**
* Checks turtles can place when waterlogged.
*
* @see [#258](https://github.com/SquidDev-CC/CC-Tweaked/issues/258)
*/
@GameTest(required = false)
suspend fun `Hoe dirt`(context: TestContext) = context.checkComputerOk(9)
}

@ -3,6 +3,9 @@ package dan200.computercraft.ingame.api
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.delay
import net.minecraft.block.BlockState
import net.minecraft.entity.Entity
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.math.AxisAlignedBB
import net.minecraft.util.math.BlockPos
/**
@ -37,7 +40,7 @@ suspend fun TestContext.sleep(ticks: Int = 1) {
waitUntil { tracker.level.gameTime >= target }
}
private fun TestContext.offset(pos: BlockPos): BlockPos = tracker.structureBlockPos.offset(pos.x, pos.y + 2, pos.z)
fun TestContext.offset(pos: BlockPos): BlockPos = tracker.structureBlockPos.offset(pos.x, pos.y + 2, pos.z)
/**
* Get a block within the test structure.
@ -59,3 +62,24 @@ fun TestContext.modifyBlock(pos: BlockPos, modify: (BlockState) -> BlockState) {
val offset = offset(pos)
level.setBlockAndUpdate(offset, modify(level.getBlockState(offset)))
}
/**
* Get a tile within the test structure.
*/
fun TestContext.getTile(pos: BlockPos): TileEntity? = tracker.level.getBlockEntity(offset(pos))
/**
* Get an entity within the test structure.
*/
fun TestContext.getEntity(pos: BlockPos): Entity? {
val entities = tracker.level.getEntitiesOfClass(Entity::class.java, AxisAlignedBB(offset(pos)))
return if (entities.isEmpty()) null else entities.get(0)
}
/**
* Get an entity within the test structure.
*/
inline fun <reified T : Entity> TestContext.getEntityOfType(pos: BlockPos): T? {
val entities = tracker.level.getEntitiesOfClass(T::class.java, AxisAlignedBB(offset(pos)))
return if (entities.isEmpty()) null else entities.get(0)
}

@ -4,7 +4,6 @@ import dan200.computercraft.ingame.api.TestContext
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.minecraft.test.TestCollection
import net.minecraft.test.TestTrackerHolder
import java.lang.reflect.Method
import java.util.*
@ -50,7 +49,8 @@ internal object MainThread : AbstractCoroutineContextElement(ContinuationInterce
}
}
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = MainThreadInterception(continuation)
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
MainThreadInterception(continuation)
private class MainThreadInterception<T>(val cont: Continuation<T>) : Continuation<T> {
override val context: CoroutineContext get() = cont.context

@ -1,3 +1,5 @@
-- TurtleTest.`Unequip refreshes peripheral`
test.eq("modem", peripheral.getType("right"), "Starts with a modem")
turtle.equipRight()
test.eq("drive", peripheral.getType("right"), "Unequipping gives a drive")

@ -0,0 +1,5 @@
-- DiskDriveTest.`Audio disk`
test.eq(true, disk.hasAudio("right"), "Has audio")
test.eq("C418 - 13", disk.getAudioTitle("right"), "Audio title")
test.ok()

@ -0,0 +1,4 @@
-- DiskDriveTest.`Ejects disk`
disk.eject("right")
test.ok()

@ -0,0 +1,9 @@
-- TurtleTest.`Shears sheep`
turtle.placeDown()
local item = turtle.getItemDetail(2)
if item == nil then test.fail("Got no item") end
test.eq("minecraft:white_wool", item.name)
test.ok()

@ -0,0 +1,9 @@
-- TurtleTest.`Lava place`
test.assert(turtle.placeDown())
local ok, down = turtle.inspectDown()
test.assert(ok, "Has below")
test.eq("minecraft:lava", down.name, "Is lava")
test.ok()

@ -0,0 +1,10 @@
-- TurtleTest.`Place Waterlogged`
test.assert(turtle.place())
local has_block, block = turtle.inspect()
test.eq(true, has_block, "Has block")
test.eq("minecraft:oak_fence", block.name)
test.eq(true, block.state.waterlogged)
test.ok()

@ -0,0 +1,11 @@
-- TurtleTest.`Gather lava`
turtle.placeDown()
local item = turtle.getItemDetail()
test.eq("minecraft:lava_bucket", item.name)
local has_down, down = turtle.inspectDown()
test.eq(false, has_down, "Air below")
test.ok()

@ -0,0 +1,9 @@
-- Turtle.`Hoe dirt`
test.assert(turtle.dig())
local has_block, block = turtle.inspect()
test.assert(has_block, "Has block")
test.eq("minecraft:farmland", block.name)
test.ok()

@ -1,3 +1,3 @@
{
"computer": 2
}
"computer": 9
}

@ -0,0 +1,149 @@
{
size: [3, 3, 3],
entities: [],
blocks: [
{
pos: [0, 0, 0],
state: 0
},
{
pos: [1, 0, 0],
state: 0
},
{
pos: [2, 0, 0],
state: 0
},
{
pos: [0, 0, 1],
state: 0
},
{
pos: [1, 0, 1],
state: 0
},
{
pos: [2, 0, 1],
state: 0
},
{
pos: [0, 0, 2],
state: 0
},
{
pos: [1, 0, 2],
state: 0
},
{
pos: [2, 0, 2],
state: 0
},
{
nbt: {
Item: {
id: "minecraft:music_disc_13",
Count: 1b
},
id: "computercraft:disk_drive"
},
pos: [0, 1, 1],
state: 1
},
{
nbt: {
id: "computercraft:computer_advanced",
ComputerId: 3,
On: 1b
},
pos: [1, 1, 1],
state: 2
},
{
pos: [0, 1, 0],
state: 3
},
{
pos: [1, 1, 0],
state: 3
},
{
pos: [2, 1, 0],
state: 3
},
{
pos: [0, 2, 0],
state: 3
},
{
pos: [1, 2, 0],
state: 3
},
{
pos: [2, 2, 0],
state: 3
},
{
pos: [2, 1, 1],
state: 3
},
{
pos: [0, 2, 1],
state: 3
},
{
pos: [1, 2, 1],
state: 3
},
{
pos: [2, 2, 1],
state: 3
},
{
pos: [0, 1, 2],
state: 3
},
{
pos: [1, 1, 2],
state: 3
},
{
pos: [2, 1, 2],
state: 3
},
{
pos: [0, 2, 2],
state: 3
},
{
pos: [1, 2, 2],
state: 3
},
{
pos: [2, 2, 2],
state: 3
}
],
palette: [
{
Name: "minecraft:polished_andesite"
},
{
Properties: {
facing: "north",
state: "full"
},
Name: "computercraft:disk_drive"
},
{
Properties: {
facing: "north",
state: "blinking"
},
Name: "computercraft:computer_advanced"
},
{
Name: "minecraft:air"
}
],
DataVersion: 2230
}

@ -0,0 +1,544 @@
{
size: [5, 5, 5],
entities: [],
blocks: [
{
pos: [0, 0, 0],
state: 0
},
{
pos: [1, 0, 0],
state: 0
},
{
pos: [2, 0, 0],
state: 0
},
{
pos: [3, 0, 0],
state: 0
},
{
pos: [4, 0, 0],
state: 0
},
{
pos: [0, 0, 1],
state: 0
},
{
pos: [1, 0, 1],
state: 0
},
{
pos: [2, 0, 1],
state: 0
},
{
pos: [3, 0, 1],
state: 0
},
{
pos: [4, 0, 1],
state: 0
},
{
pos: [1, 1, 1],
state: 1
},
{
pos: [1, 2, 1],
state: 1
},
{
pos: [2, 2, 1],
state: 1
},
{
pos: [3, 2, 1],
state: 1
},
{
pos: [0, 0, 2],
state: 0
},
{
pos: [1, 0, 2],
state: 0
},
{
pos: [2, 0, 2],
state: 0
},
{
pos: [3, 0, 2],
state: 0
},
{
pos: [4, 0, 2],
state: 0
},
{
pos: [1, 1, 2],
state: 1
},
{
pos: [3, 1, 2],
state: 1
},
{
pos: [1, 2, 2],
state: 1
},
{
pos: [3, 2, 2],
state: 1
},
{
pos: [0, 0, 3],
state: 0
},
{
pos: [1, 0, 3],
state: 0
},
{
pos: [2, 0, 3],
state: 0
},
{
pos: [3, 0, 3],
state: 0
},
{
pos: [4, 0, 3],
state: 0
},
{
pos: [1, 1, 3],
state: 1
},
{
pos: [2, 1, 3],
state: 1
},
{
pos: [3, 1, 3],
state: 1
},
{
pos: [1, 2, 3],
state: 1
},
{
pos: [2, 2, 3],
state: 1
},
{
pos: [3, 2, 3],
state: 1
},
{
pos: [0, 0, 4],
state: 0
},
{
pos: [1, 0, 4],
state: 0
},
{
pos: [2, 0, 4],
state: 0
},
{
pos: [3, 0, 4],
state: 0
},
{
pos: [4, 0, 4],
state: 0
},
{
nbt: {
Item: {
id: "minecraft:music_disc_13",
Count: 1b
},
id: "computercraft:disk_drive"
},
pos: [2, 1, 1],
state: 2
},
{
nbt: {
id: "computercraft:computer_advanced",
ComputerId: 4,
On: 1b
},
pos: [3, 1, 1],
state: 3
},
{
pos: [0, 1, 0],
state: 4
},
{
pos: [1, 1, 0],
state: 4
},
{
pos: [2, 1, 0],
state: 4
},
{
pos: [3, 1, 0],
state: 4
},
{
pos: [4, 1, 0],
state: 4
},
{
pos: [0, 2, 0],
state: 4
},
{
pos: [1, 2, 0],
state: 4
},
{
pos: [2, 2, 0],
state: 4
},
{
pos: [3, 2, 0],
state: 4
},
{
pos: [4, 2, 0],
state: 4
},
{
pos: [0, 3, 0],
state: 4
},
{
pos: [1, 3, 0],
state: 4
},
{
pos: [2, 3, 0],
state: 4
},
{
pos: [3, 3, 0],
state: 4
},
{
pos: [4, 3, 0],
state: 4
},
{
pos: [0, 4, 0],
state: 4
},
{
pos: [1, 4, 0],
state: 4
},
{
pos: [2, 4, 0],
state: 4
},
{
pos: [3, 4, 0],
state: 4
},
{
pos: [4, 4, 0],
state: 4
},
{
pos: [0, 1, 1],
state: 4
},
{
pos: [4, 1, 1],
state: 4
},
{
pos: [0, 2, 1],
state: 4
},
{
pos: [4, 2, 1],
state: 4
},
{
pos: [0, 3, 1],
state: 4
},
{
pos: [1, 3, 1],
state: 4
},
{
pos: [2, 3, 1],
state: 4
},
{
pos: [3, 3, 1],
state: 4
},
{
pos: [4, 3, 1],
state: 4
},
{
pos: [0, 4, 1],
state: 4
},
{
pos: [1, 4, 1],
state: 4
},
{
pos: [2, 4, 1],
state: 4
},
{
pos: [3, 4, 1],
state: 4
},
{
pos: [4, 4, 1],
state: 4
},
{
pos: [0, 1, 2],
state: 4
},
{
pos: [2, 1, 2],
state: 4
},
{
pos: [4, 1, 2],
state: 4
},
{
pos: [0, 2, 2],
state: 4
},
{
pos: [2, 2, 2],
state: 4
},
{
pos: [4, 2, 2],
state: 4
},
{
pos: [0, 3, 2],
state: 4
},
{
pos: [1, 3, 2],
state: 4
},
{
pos: [2, 3, 2],
state: 4
},
{
pos: [3, 3, 2],
state: 4
},
{
pos: [4, 3, 2],
state: 4
},
{
pos: [0, 4, 2],
state: 4
},
{
pos: [1, 4, 2],
state: 4
},
{
pos: [2, 4, 2],
state: 4
},
{
pos: [3, 4, 2],
state: 4
},
{
pos: [4, 4, 2],
state: 4
},
{
pos: [0, 1, 3],
state: 4
},
{
pos: [4, 1, 3],
state: 4
},
{
pos: [0, 2, 3],
state: 4
},
{
pos: [4, 2, 3],
state: 4
},
{
pos: [0, 3, 3],
state: 4
},
{
pos: [1, 3, 3],
state: 4
},
{
pos: [2, 3, 3],
state: 4
},
{
pos: [3, 3, 3],
state: 4
},
{
pos: [4, 3, 3],
state: 4
},
{
pos: [0, 4, 3],
state: 4
},
{
pos: [1, 4, 3],
state: 4
},
{
pos: [2, 4, 3],
state: 4
},
{
pos: [3, 4, 3],
state: 4
},
{
pos: [4, 4, 3],
state: 4
},
{
pos: [0, 1, 4],
state: 4
},
{
pos: [1, 1, 4],
state: 4
},
{
pos: [2, 1, 4],
state: 4
},
{
pos: [3, 1, 4],
state: 4
},
{
pos: [4, 1, 4],
state: 4
},
{
pos: [0, 2, 4],
state: 4
},
{
pos: [1, 2, 4],
state: 4
},
{
pos: [2, 2, 4],
state: 4
},
{
pos: [3, 2, 4],
state: 4
},
{
pos: [4, 2, 4],
state: 4
},
{
pos: [0, 3, 4],
state: 4
},
{
pos: [1, 3, 4],
state: 4
},
{
pos: [2, 3, 4],
state: 4
},
{
pos: [3, 3, 4],
state: 4
},
{
pos: [4, 3, 4],
state: 4
},
{
pos: [0, 4, 4],
state: 4
},
{
pos: [1, 4, 4],
state: 4
},
{
pos: [2, 4, 4],
state: 4
},
{
pos: [3, 4, 4],
state: 4
},
{
pos: [4, 4, 4],
state: 4
}
],
palette: [
{
Name: "minecraft:polished_andesite"
},
{
Name: "minecraft:white_stained_glass"
},
{
Properties: {
facing: "south",
state: "full"
},
Name: "computercraft:disk_drive"
},
{
Properties: {
facing: "north",
state: "blinking"
},
Name: "computercraft:computer_advanced"
},
{
Name: "minecraft:air"
}
],
DataVersion: 2230
}

@ -0,0 +1,550 @@
{
size: [5, 5, 5],
entities: [],
blocks: [
{
pos: [0, 0, 0],
state: 0
},
{
pos: [1, 0, 0],
state: 0
},
{
pos: [2, 0, 0],
state: 0
},
{
pos: [3, 0, 0],
state: 0
},
{
pos: [4, 0, 0],
state: 0
},
{
pos: [0, 0, 1],
state: 0
},
{
pos: [1, 0, 1],
state: 0
},
{
pos: [2, 0, 1],
state: 0
},
{
pos: [3, 0, 1],
state: 0
},
{
pos: [4, 0, 1],
state: 0
},
{
pos: [1, 1, 1],
state: 1
},
{
pos: [2, 1, 1],
state: 1
},
{
pos: [3, 1, 1],
state: 1
},
{
pos: [0, 0, 2],
state: 0
},
{
pos: [1, 0, 2],
state: 0
},
{
pos: [2, 0, 2],
state: 0
},
{
pos: [3, 0, 2],
state: 0
},
{
pos: [4, 0, 2],
state: 0
},
{
pos: [1, 1, 2],
state: 1
},
{
pos: [3, 1, 2],
state: 1
},
{
pos: [0, 0, 3],
state: 0
},
{
pos: [1, 0, 3],
state: 0
},
{
pos: [2, 0, 3],
state: 0
},
{
pos: [3, 0, 3],
state: 0
},
{
pos: [4, 0, 3],
state: 0
},
{
pos: [1, 1, 3],
state: 1
},
{
pos: [2, 1, 3],
state: 1
},
{
pos: [3, 1, 3],
state: 1
},
{
pos: [0, 0, 4],
state: 0
},
{
pos: [1, 0, 4],
state: 0
},
{
pos: [2, 0, 4],
state: 0
},
{
pos: [3, 0, 4],
state: 0
},
{
pos: [4, 0, 4],
state: 0
},
{
nbt: {
Owner: {
UpperId: 4039158846114182220L,
LowerId: -6876936588741668278L,
Name: "Dev"
},
Fuel: 0,
Slot: 0,
Items: [
{
Slot: 0b,
id: "minecraft:bucket",
Count: 1b
}
],
id: "computercraft:turtle_normal",
ComputerId: 8,
On: 1b
},
pos: [2, 2, 2],
state: 2
},
{
pos: [0, 1, 0],
state: 3
},
{
pos: [1, 1, 0],
state: 3
},
{
pos: [2, 1, 0],
state: 3
},
{
pos: [3, 1, 0],
state: 3
},
{
pos: [4, 1, 0],
state: 3
},
{
pos: [0, 2, 0],
state: 3
},
{
pos: [1, 2, 0],
state: 3
},
{
pos: [2, 2, 0],
state: 3
},
{
pos: [3, 2, 0],
state: 3
},
{
pos: [4, 2, 0],
state: 3
},
{
pos: [0, 3, 0],
state: 3
},
{
pos: [1, 3, 0],
state: 3
},
{
pos: [2, 3, 0],
state: 3
},
{
pos: [3, 3, 0],
state: 3
},
{
pos: [4, 3, 0],
state: 3
},
{
pos: [0, 4, 0],
state: 3
},
{
pos: [1, 4, 0],
state: 3
},
{
pos: [2, 4, 0],
state: 3
},
{
pos: [3, 4, 0],
state: 3
},
{
pos: [4, 4, 0],
state: 3
},
{
pos: [0, 1, 1],
state: 3
},
{
pos: [4, 1, 1],
state: 3
},
{
pos: [0, 2, 1],
state: 3
},
{
pos: [1, 2, 1],
state: 3
},
{
pos: [2, 2, 1],
state: 3
},
{
pos: [3, 2, 1],
state: 3
},
{
pos: [4, 2, 1],
state: 3
},
{
pos: [0, 3, 1],
state: 3
},
{
pos: [1, 3, 1],
state: 3
},
{
pos: [2, 3, 1],
state: 3
},
{
pos: [3, 3, 1],
state: 3
},
{
pos: [4, 3, 1],
state: 3
},
{
pos: [0, 4, 1],
state: 3
},
{
pos: [1, 4, 1],
state: 3
},
{
pos: [2, 4, 1],
state: 3
},
{
pos: [3, 4, 1],
state: 3
},
{
pos: [4, 4, 1],
state: 3
},
{
pos: [0, 1, 2],
state: 3
},
{
pos: [2, 1, 2],
state: 4
},
{
pos: [4, 1, 2],
state: 3
},
{
pos: [0, 2, 2],
state: 3
},
{
pos: [1, 2, 2],
state: 3
},
{
pos: [3, 2, 2],
state: 3
},
{
pos: [4, 2, 2],
state: 3
},
{
pos: [0, 3, 2],
state: 3
},
{
pos: [1, 3, 2],
state: 3
},
{
pos: [2, 3, 2],
state: 3
},
{
pos: [3, 3, 2],
state: 3
},
{
pos: [4, 3, 2],
state: 3
},
{
pos: [0, 4, 2],
state: 3
},
{
pos: [1, 4, 2],
state: 3
},
{
pos: [2, 4, 2],
state: 3
},
{
pos: [3, 4, 2],
state: 3
},
{
pos: [4, 4, 2],
state: 3
},
{
pos: [0, 1, 3],
state: 3
},
{
pos: [4, 1, 3],
state: 3
},
{
pos: [0, 2, 3],
state: 3
},
{
pos: [1, 2, 3],
state: 3
},
{
pos: [2, 2, 3],
state: 3
},
{
pos: [3, 2, 3],
state: 3
},
{
pos: [4, 2, 3],
state: 3
},
{
pos: [0, 3, 3],
state: 3
},
{
pos: [1, 3, 3],
state: 3
},
{
pos: [2, 3, 3],
state: 3
},
{
pos: [3, 3, 3],
state: 3
},
{
pos: [4, 3, 3],
state: 3
},
{
pos: [0, 4, 3],
state: 3
},
{
pos: [1, 4, 3],
state: 3
},
{
pos: [2, 4, 3],
state: 3
},
{
pos: [3, 4, 3],
state: 3
},
{
pos: [4, 4, 3],
state: 3
},
{
pos: [0, 1, 4],
state: 3
},
{
pos: [1, 1, 4],
state: 3
},
{
pos: [2, 1, 4],
state: 3
},
{
pos: [3, 1, 4],
state: 3
},
{
pos: [4, 1, 4],
state: 3
},
{
pos: [0, 2, 4],
state: 3
},
{
pos: [1, 2, 4],
state: 3
},
{
pos: [2, 2, 4],
state: 3
},
{
pos: [3, 2, 4],
state: 3
},
{
pos: [4, 2, 4],
state: 3
},
{
pos: [0, 3, 4],
state: 3
},
{
pos: [1, 3, 4],
state: 3
},
{
pos: [2, 3, 4],
state: 3
},
{
pos: [3, 3, 4],
state: 3
},
{
pos: [4, 3, 4],
state: 3
},
{
pos: [0, 4, 4],
state: 3
},
{
pos: [1, 4, 4],
state: 3
},
{
pos: [2, 4, 4],
state: 3
},
{
pos: [3, 4, 4],
state: 3
},
{
pos: [4, 4, 4],
state: 3
}
],
palette: [
{
Name: "minecraft:polished_andesite"
},
{
Name: "minecraft:white_stained_glass"
},
{
Properties: {
waterlogged: "false",
facing: "south"
},
Name: "computercraft:turtle_normal"
},
{
Name: "minecraft:air"
},
{
Properties: {
level: "0"
},
Name: "minecraft:lava"
}
],
DataVersion: 2230
}

@ -0,0 +1,147 @@
{
size: [3, 3, 3],
entities: [],
blocks: [
{
pos: [0, 0, 0],
state: 0
},
{
pos: [1, 0, 0],
state: 0
},
{
pos: [2, 0, 0],
state: 0
},
{
pos: [0, 0, 1],
state: 0
},
{
pos: [1, 0, 1],
state: 0
},
{
pos: [2, 0, 1],
state: 0
},
{
pos: [1, 1, 1],
state: 1
},
{
pos: [0, 0, 2],
state: 0
},
{
pos: [1, 0, 2],
state: 0
},
{
pos: [2, 0, 2],
state: 0
},
{
nbt: {
Owner: {
UpperId: 4039158846114182220L,
LowerId: -6876936588741668278L,
Name: "Dev"
},
Fuel: 0,
LeftUpgrade: "minecraft:diamond_hoe",
Slot: 0,
Items: [],
id: "computercraft:turtle_normal",
ComputerId: 9,
On: 1b
},
pos: [1, 1, 0],
state: 2
},
{
pos: [0, 1, 0],
state: 3
},
{
pos: [2, 1, 0],
state: 3
},
{
pos: [0, 2, 0],
state: 3
},
{
pos: [1, 2, 0],
state: 3
},
{
pos: [2, 2, 0],
state: 3
},
{
pos: [0, 1, 1],
state: 3
},
{
pos: [2, 1, 1],
state: 3
},
{
pos: [0, 2, 1],
state: 3
},
{
pos: [1, 2, 1],
state: 3
},
{
pos: [2, 2, 1],
state: 3
},
{
pos: [0, 1, 2],
state: 3
},
{
pos: [1, 1, 2],
state: 3
},
{
pos: [2, 1, 2],
state: 3
},
{
pos: [0, 2, 2],
state: 3
},
{
pos: [1, 2, 2],
state: 3
},
{
pos: [2, 2, 2],
state: 3
}
],
palette: [
{
Name: "minecraft:polished_andesite"
},
{
Name: "minecraft:dirt"
},
{
Properties: {
waterlogged: "false",
facing: "south"
},
Name: "computercraft:turtle_normal"
},
{
Name: "minecraft:air"
}
],
DataVersion: 2230
}

@ -0,0 +1,544 @@
{
size: [5, 5, 5],
entities: [],
blocks: [
{
pos: [0, 0, 0],
state: 0
},
{
pos: [1, 0, 0],
state: 0
},
{
pos: [2, 0, 0],
state: 0
},
{
pos: [3, 0, 0],
state: 0
},
{
pos: [4, 0, 0],
state: 0
},
{
pos: [0, 0, 1],
state: 0
},
{
pos: [1, 0, 1],
state: 0
},
{
pos: [2, 0, 1],
state: 0
},
{
pos: [3, 0, 1],
state: 0
},
{
pos: [4, 0, 1],
state: 0
},
{
pos: [1, 1, 1],
state: 1
},
{
pos: [2, 1, 1],
state: 1
},
{
pos: [3, 1, 1],
state: 1
},
{
pos: [0, 0, 2],
state: 0
},
{
pos: [1, 0, 2],
state: 0
},
{
pos: [2, 0, 2],
state: 0
},
{
pos: [3, 0, 2],
state: 0
},
{
pos: [4, 0, 2],
state: 0
},
{
pos: [1, 1, 2],
state: 1
},
{
pos: [3, 1, 2],
state: 1
},
{
pos: [0, 0, 3],
state: 0
},
{
pos: [1, 0, 3],
state: 0
},
{
pos: [2, 0, 3],
state: 0
},
{
pos: [3, 0, 3],
state: 0
},
{
pos: [4, 0, 3],
state: 0
},
{
pos: [1, 1, 3],
state: 1
},
{
pos: [2, 1, 3],
state: 1
},
{
pos: [3, 1, 3],
state: 1
},
{
pos: [0, 0, 4],
state: 0
},
{
pos: [1, 0, 4],
state: 0
},
{
pos: [2, 0, 4],
state: 0
},
{
pos: [3, 0, 4],
state: 0
},
{
pos: [4, 0, 4],
state: 0
},
{
nbt: {
Owner: {
UpperId: 4039158846114182220L,
LowerId: -6876936588741668278L,
Name: "Dev"
},
Fuel: 0,
Slot: 0,
Items: [
{
Slot: 0b,
id: "minecraft:lava_bucket",
Count: 1b
}
],
id: "computercraft:turtle_normal",
ComputerId: 6,
On: 1b
},
pos: [2, 2, 2],
state: 2
},
{
pos: [0, 1, 0],
state: 3
},
{
pos: [1, 1, 0],
state: 3
},
{
pos: [2, 1, 0],
state: 3
},
{
pos: [3, 1, 0],
state: 3
},
{
pos: [4, 1, 0],
state: 3
},
{
pos: [0, 2, 0],
state: 3
},
{
pos: [1, 2, 0],
state: 3
},
{
pos: [2, 2, 0],
state: 3
},
{
pos: [3, 2, 0],
state: 3
},
{
pos: [4, 2, 0],
state: 3
},
{
pos: [0, 3, 0],
state: 3
},
{
pos: [1, 3, 0],
state: 3
},
{
pos: [2, 3, 0],
state: 3
},
{
pos: [3, 3, 0],
state: 3
},
{
pos: [4, 3, 0],
state: 3
},
{
pos: [0, 4, 0],
state: 3
},
{
pos: [1, 4, 0],
state: 3
},
{
pos: [2, 4, 0],
state: 3
},
{
pos: [3, 4, 0],
state: 3
},
{
pos: [4, 4, 0],
state: 3
},
{
pos: [0, 1, 1],
state: 3
},
{
pos: [4, 1, 1],
state: 3
},
{
pos: [0, 2, 1],
state: 3
},
{
pos: [1, 2, 1],
state: 3
},
{
pos: [2, 2, 1],
state: 3
},
{
pos: [3, 2, 1],
state: 3
},
{
pos: [4, 2, 1],
state: 3
},
{
pos: [0, 3, 1],
state: 3
},
{
pos: [1, 3, 1],
state: 3
},
{
pos: [2, 3, 1],
state: 3
},
{
pos: [3, 3, 1],
state: 3
},
{
pos: [4, 3, 1],
state: 3
},
{
pos: [0, 4, 1],
state: 3
},
{
pos: [1, 4, 1],
state: 3
},
{
pos: [2, 4, 1],
state: 3
},
{
pos: [3, 4, 1],
state: 3
},
{
pos: [4, 4, 1],
state: 3
},
{
pos: [0, 1, 2],
state: 3
},
{
pos: [2, 1, 2],
state: 3
},
{
pos: [4, 1, 2],
state: 3
},
{
pos: [0, 2, 2],
state: 3
},
{
pos: [1, 2, 2],
state: 3
},
{
pos: [3, 2, 2],
state: 3
},
{
pos: [4, 2, 2],
state: 3
},
{
pos: [0, 3, 2],
state: 3
},
{
pos: [1, 3, 2],
state: 3
},
{
pos: [2, 3, 2],
state: 3
},
{
pos: [3, 3, 2],
state: 3
},
{
pos: [4, 3, 2],
state: 3
},
{
pos: [0, 4, 2],
state: 3
},
{
pos: [1, 4, 2],
state: 3
},
{
pos: [2, 4, 2],
state: 3
},
{
pos: [3, 4, 2],
state: 3
},
{
pos: [4, 4, 2],
state: 3
},
{
pos: [0, 1, 3],
state: 3
},
{
pos: [4, 1, 3],
state: 3
},
{
pos: [0, 2, 3],
state: 3
},
{
pos: [1, 2, 3],
state: 3
},
{
pos: [2, 2, 3],
state: 3
},
{
pos: [3, 2, 3],
state: 3
},
{
pos: [4, 2, 3],
state: 3
},
{
pos: [0, 3, 3],
state: 3
},
{
pos: [1, 3, 3],
state: 3
},
{
pos: [2, 3, 3],
state: 3
},
{
pos: [3, 3, 3],
state: 3
},
{
pos: [4, 3, 3],
state: 3
},
{
pos: [0, 4, 3],
state: 3
},
{
pos: [1, 4, 3],
state: 3
},
{
pos: [2, 4, 3],
state: 3
},
{
pos: [3, 4, 3],
state: 3
},
{
pos: [4, 4, 3],
state: 3
},
{
pos: [0, 1, 4],
state: 3
},
{
pos: [1, 1, 4],
state: 3
},
{
pos: [2, 1, 4],
state: 3
},
{
pos: [3, 1, 4],
state: 3
},
{
pos: [4, 1, 4],
state: 3
},
{
pos: [0, 2, 4],
state: 3
},
{
pos: [1, 2, 4],
state: 3
},
{
pos: [2, 2, 4],
state: 3
},
{
pos: [3, 2, 4],
state: 3
},
{
pos: [4, 2, 4],
state: 3
},
{
pos: [0, 3, 4],
state: 3
},
{
pos: [1, 3, 4],
state: 3
},
{
pos: [2, 3, 4],
state: 3
},
{
pos: [3, 3, 4],
state: 3
},
{
pos: [4, 3, 4],
state: 3
},
{
pos: [0, 4, 4],
state: 3
},
{
pos: [1, 4, 4],
state: 3
},
{
pos: [2, 4, 4],
state: 3
},
{
pos: [3, 4, 4],
state: 3
},
{
pos: [4, 4, 4],
state: 3
}
],
palette: [
{
Name: "minecraft:polished_andesite"
},
{
Name: "minecraft:white_stained_glass"
},
{
Properties: {
waterlogged: "false",
facing: "south"
},
Name: "computercraft:turtle_normal"
},
{
Name: "minecraft:air"
}
],
DataVersion: 2230
}

@ -0,0 +1,550 @@
{
size: [5, 5, 5],
entities: [],
blocks: [
{
pos: [0, 0, 0],
state: 0
},
{
pos: [1, 0, 0],
state: 0
},
{
pos: [2, 0, 0],
state: 0
},
{
pos: [3, 0, 0],
state: 0
},
{
pos: [4, 0, 0],
state: 0
},
{
pos: [1, 1, 0],
state: 1
},
{
pos: [2, 1, 0],
state: 1
},
{
pos: [3, 1, 0],
state: 1
},
{
pos: [0, 0, 1],
state: 0
},
{
pos: [1, 0, 1],
state: 0
},
{
pos: [2, 0, 1],
state: 0
},
{
pos: [3, 0, 1],
state: 0
},
{
pos: [4, 0, 1],
state: 0
},
{
pos: [1, 1, 1],
state: 1
},
{
pos: [3, 1, 1],
state: 1
},
{
pos: [0, 0, 2],
state: 0
},
{
pos: [1, 0, 2],
state: 0
},
{
pos: [2, 0, 2],
state: 0
},
{
pos: [3, 0, 2],
state: 0
},
{
pos: [4, 0, 2],
state: 0
},
{
pos: [1, 1, 2],
state: 1
},
{
pos: [3, 1, 2],
state: 1
},
{
pos: [0, 0, 3],
state: 0
},
{
pos: [1, 0, 3],
state: 0
},
{
pos: [2, 0, 3],
state: 0
},
{
pos: [3, 0, 3],
state: 0
},
{
pos: [4, 0, 3],
state: 0
},
{
pos: [1, 1, 3],
state: 1
},
{
pos: [2, 1, 3],
state: 1
},
{
pos: [3, 1, 3],
state: 1
},
{
pos: [0, 0, 4],
state: 0
},
{
pos: [1, 0, 4],
state: 0
},
{
pos: [2, 0, 4],
state: 0
},
{
pos: [3, 0, 4],
state: 0
},
{
pos: [4, 0, 4],
state: 0
},
{
nbt: {
Owner: {
UpperId: 4039158846114182220L,
LowerId: -6876936588741668278L,
Name: "Dev"
},
Fuel: 0,
Slot: 0,
Items: [
{
Slot: 0b,
id: "minecraft:oak_fence",
Count: 1b
}
],
id: "computercraft:turtle_normal",
ComputerId: 7,
On: 1b
},
pos: [2, 1, 1],
state: 2
},
{
pos: [0, 1, 0],
state: 3
},
{
pos: [4, 1, 0],
state: 3
},
{
pos: [0, 2, 0],
state: 3
},
{
pos: [1, 2, 0],
state: 3
},
{
pos: [2, 2, 0],
state: 3
},
{
pos: [3, 2, 0],
state: 3
},
{
pos: [4, 2, 0],
state: 3
},
{
pos: [0, 3, 0],
state: 3
},
{
pos: [1, 3, 0],
state: 3
},
{
pos: [2, 3, 0],
state: 3
},
{
pos: [3, 3, 0],
state: 3
},
{
pos: [4, 3, 0],
state: 3
},
{
pos: [0, 4, 0],
state: 3
},
{
pos: [1, 4, 0],
state: 3
},
{
pos: [2, 4, 0],
state: 3
},
{
pos: [3, 4, 0],
state: 3
},
{
pos: [4, 4, 0],
state: 3
},
{
pos: [0, 1, 1],
state: 3
},
{
pos: [4, 1, 1],
state: 3
},
{
pos: [0, 2, 1],
state: 3
},
{
pos: [1, 2, 1],
state: 3
},
{
pos: [2, 2, 1],
state: 3
},
{
pos: [3, 2, 1],
state: 3
},
{
pos: [4, 2, 1],
state: 3
},
{
pos: [0, 3, 1],
state: 3
},
{
pos: [1, 3, 1],
state: 3
},
{
pos: [2, 3, 1],
state: 3
},
{
pos: [3, 3, 1],
state: 3
},
{
pos: [4, 3, 1],
state: 3
},
{
pos: [0, 4, 1],
state: 3
},
{
pos: [1, 4, 1],
state: 3
},
{
pos: [2, 4, 1],
state: 3
},
{
pos: [3, 4, 1],
state: 3
},
{
pos: [4, 4, 1],
state: 3
},
{
pos: [0, 1, 2],
state: 3
},
{
pos: [2, 1, 2],
state: 4
},
{
pos: [4, 1, 2],
state: 3
},
{
pos: [0, 2, 2],
state: 3
},
{
pos: [1, 2, 2],
state: 3
},
{
pos: [2, 2, 2],
state: 3
},
{
pos: [3, 2, 2],
state: 3
},
{
pos: [4, 2, 2],
state: 3
},
{
pos: [0, 3, 2],
state: 3
},
{
pos: [1, 3, 2],
state: 3
},
{
pos: [2, 3, 2],
state: 3
},
{
pos: [3, 3, 2],
state: 3
},
{
pos: [4, 3, 2],
state: 3
},
{
pos: [0, 4, 2],
state: 3
},
{
pos: [1, 4, 2],
state: 3
},
{
pos: [2, 4, 2],
state: 3
},
{
pos: [3, 4, 2],
state: 3
},
{
pos: [4, 4, 2],
state: 3
},
{
pos: [0, 1, 3],
state: 3
},
{
pos: [4, 1, 3],
state: 3
},
{
pos: [0, 2, 3],
state: 3
},
{
pos: [1, 2, 3],
state: 3
},
{
pos: [2, 2, 3],
state: 3
},
{
pos: [3, 2, 3],
state: 3
},
{
pos: [4, 2, 3],
state: 3
},
{
pos: [0, 3, 3],
state: 3
},
{
pos: [1, 3, 3],
state: 3
},
{
pos: [2, 3, 3],
state: 3
},
{
pos: [3, 3, 3],
state: 3
},
{
pos: [4, 3, 3],
state: 3
},
{
pos: [0, 4, 3],
state: 3
},
{
pos: [1, 4, 3],
state: 3
},
{
pos: [2, 4, 3],
state: 3
},
{
pos: [3, 4, 3],
state: 3
},
{
pos: [4, 4, 3],
state: 3
},
{
pos: [0, 1, 4],
state: 3
},
{
pos: [1, 1, 4],
state: 3
},
{
pos: [2, 1, 4],
state: 3
},
{
pos: [3, 1, 4],
state: 3
},
{
pos: [4, 1, 4],
state: 3
},
{
pos: [0, 2, 4],
state: 3
},
{
pos: [1, 2, 4],
state: 3
},
{
pos: [2, 2, 4],
state: 3
},
{
pos: [3, 2, 4],
state: 3
},
{
pos: [4, 2, 4],
state: 3
},
{
pos: [0, 3, 4],
state: 3
},
{
pos: [1, 3, 4],
state: 3
},
{
pos: [2, 3, 4],
state: 3
},
{
pos: [3, 3, 4],
state: 3
},
{
pos: [4, 3, 4],
state: 3
},
{
pos: [0, 4, 4],
state: 3
},
{
pos: [1, 4, 4],
state: 3
},
{
pos: [2, 4, 4],
state: 3
},
{
pos: [3, 4, 4],
state: 3
},
{
pos: [4, 4, 4],
state: 3
}
],
palette: [
{
Name: "minecraft:polished_andesite"
},
{
Name: "minecraft:white_stained_glass"
},
{
Properties: {
waterlogged: "true",
facing: "south"
},
Name: "computercraft:turtle_normal"
},
{
Name: "minecraft:air"
},
{
Properties: {
level: "0"
},
Name: "minecraft:water"
}
],
DataVersion: 2230
}

@ -0,0 +1,648 @@
{
size: [5, 5, 5],
entities: [
{
nbt: {
Brain: {
memories: {}
},
HurtByTimestamp: 0,
Attributes: [
{
Base: 8.0d,
Name: "generic.maxHealth"
},
{
Base: 0.0d,
Name: "generic.knockbackResistance"
},
{
Base: 0.23000000417232513d,
Name: "generic.movementSpeed"
},
{
Base: 0.0d,
Name: "generic.armor"
},
{
Base: 0.0d,
Name: "generic.armorToughness"
},
{
Base: 1.0d,
Name: "forge.swimSpeed"
},
{
Base: 64.0d,
Name: "forge.nameTagDistance"
},
{
Base: 0.08d,
Name: "forge.entity_gravity"
},
{
Base: 16.0d,
Modifiers: [
{
UUIDMost: 2135385928807173041L,
UUIDLeast: -7913974980122166977L,
Amount: -0.04393447767139704d,
Operation: 1,
Name: "Random spawn bonus"
}
],
Name: "generic.followRange"
},
{
Base: 0.0d,
Name: "generic.attackKnockback"
}
],
Invulnerable: 0b,
FallFlying: 0b,
ForcedAge: 0,
PortalCooldown: 0,
AbsorptionAmount: 0.0f,
FallDistance: 0.0f,
InLove: 0,
CanUpdate: 1b,
DeathTime: 0s,
HandDropChances: [0.085f, 0.085f],
PersistenceRequired: 0b,
id: "minecraft:sheep",
Age: 0,
Motion: [0.0d, -0.0784000015258789d, 0.0d],
UUIDLeast: -5245632071702074643L,
Health: 8.0f,
Color: 0b,
LeftHanded: 0b,
Air: 300s,
OnGround: 1b,
Dimension: 0,
Rotation: [99.779915f, 0.0f],
HandItems: [
{},
{}
],
ArmorDropChances: [0.085f, 0.085f, 0.085f, 0.085f],
UUIDMost: -3792049278188698748L,
Pos: [-12.5d, 6.0d, 110.5d],
Fire: -1s,
ArmorItems: [
{},
{},
{},
{}
],
CanPickUpLoot: 0b,
Sheared: 0b,
HurtTime: 0s
},
blockPos: [2, 1, 2],
pos: [2.5d, 1.0d, 2.5d]
}
],
blocks: [
{
pos: [0, 0, 0],
state: 0
},
{
pos: [1, 0, 0],
state: 0
},
{
pos: [2, 0, 0],
state: 0
},
{
pos: [3, 0, 0],
state: 0
},
{
pos: [4, 0, 0],
state: 0
},
{
pos: [0, 0, 1],
state: 0
},
{
pos: [1, 0, 1],
state: 0
},
{
pos: [2, 0, 1],
state: 0
},
{
pos: [3, 0, 1],
state: 0
},
{
pos: [4, 0, 1],
state: 0
},
{
pos: [1, 1, 1],
state: 1
},
{
pos: [2, 1, 1],
state: 1
},
{
pos: [3, 1, 1],
state: 1
},
{
pos: [1, 2, 1],
state: 1
},
{
pos: [2, 2, 1],
state: 1
},
{
pos: [3, 2, 1],
state: 1
},
{
pos: [0, 0, 2],
state: 0
},
{
pos: [1, 0, 2],
state: 0
},
{
pos: [2, 0, 2],
state: 0
},
{
pos: [3, 0, 2],
state: 0
},
{
pos: [4, 0, 2],
state: 0
},
{
pos: [1, 1, 2],
state: 1
},
{
pos: [3, 1, 2],
state: 1
},
{
pos: [1, 2, 2],
state: 1
},
{
pos: [3, 2, 2],
state: 1
},
{
pos: [0, 0, 3],
state: 0
},
{
pos: [1, 0, 3],
state: 0
},
{
pos: [2, 0, 3],
state: 0
},
{
pos: [3, 0, 3],
state: 0
},
{
pos: [4, 0, 3],
state: 0
},
{
pos: [1, 1, 3],
state: 1
},
{
pos: [2, 1, 3],
state: 1
},
{
pos: [3, 1, 3],
state: 1
},
{
pos: [1, 2, 3],
state: 1
},
{
pos: [2, 2, 3],
state: 1
},
{
pos: [3, 2, 3],
state: 1
},
{
pos: [0, 0, 4],
state: 0
},
{
pos: [1, 0, 4],
state: 0
},
{
pos: [2, 0, 4],
state: 0
},
{
pos: [3, 0, 4],
state: 0
},
{
pos: [4, 0, 4],
state: 0
},
{
nbt: {
Owner: {
UpperId: 4039158846114182220L,
LowerId: -6876936588741668278L,
Name: "Dev"
},
Fuel: 0,
Slot: 0,
Items: [
{
Slot: 0b,
id: "minecraft:shears",
Count: 1b,
tag: {
Damage: 0
}
}
],
id: "computercraft:turtle_normal",
ComputerId: 5,
On: 1b
},
pos: [2, 3, 2],
state: 2
},
{
pos: [0, 1, 0],
state: 3
},
{
pos: [1, 1, 0],
state: 3
},
{
pos: [2, 1, 0],
state: 3
},
{
pos: [3, 1, 0],
state: 3
},
{
pos: [4, 1, 0],
state: 3
},
{
pos: [0, 2, 0],
state: 3
},
{
pos: [1, 2, 0],
state: 3
},
{
pos: [2, 2, 0],
state: 3
},
{
pos: [3, 2, 0],
state: 3
},
{
pos: [4, 2, 0],
state: 3
},
{
pos: [0, 3, 0],
state: 3
},
{
pos: [1, 3, 0],
state: 3
},
{
pos: [2, 3, 0],
state: 3
},
{
pos: [3, 3, 0],
state: 3
},
{
pos: [4, 3, 0],
state: 3
},
{
pos: [0, 4, 0],
state: 3
},
{
pos: [1, 4, 0],
state: 3
},
{
pos: [2, 4, 0],
state: 3
},
{
pos: [3, 4, 0],
state: 3
},
{
pos: [4, 4, 0],
state: 3
},
{
pos: [0, 1, 1],
state: 3
},
{
pos: [4, 1, 1],
state: 3
},
{
pos: [0, 2, 1],
state: 3
},
{
pos: [4, 2, 1],
state: 3
},
{
pos: [0, 3, 1],
state: 3
},
{
pos: [1, 3, 1],
state: 3
},
{
pos: [2, 3, 1],
state: 3
},
{
pos: [3, 3, 1],
state: 3
},
{
pos: [4, 3, 1],
state: 3
},
{
pos: [0, 4, 1],
state: 3
},
{
pos: [1, 4, 1],
state: 3
},
{
pos: [2, 4, 1],
state: 3
},
{
pos: [3, 4, 1],
state: 3
},
{
pos: [4, 4, 1],
state: 3
},
{
pos: [0, 1, 2],
state: 3
},
{
pos: [2, 1, 2],
state: 3
},
{
pos: [4, 1, 2],
state: 3
},
{
pos: [0, 2, 2],
state: 3
},
{
pos: [2, 2, 2],
state: 3
},
{
pos: [4, 2, 2],
state: 3
},
{
pos: [0, 3, 2],
state: 3
},
{
pos: [1, 3, 2],
state: 3
},
{
pos: [3, 3, 2],
state: 3
},
{
pos: [4, 3, 2],
state: 3
},
{
pos: [0, 4, 2],
state: 3
},
{
pos: [1, 4, 2],
state: 3
},
{
pos: [2, 4, 2],
state: 3
},
{
pos: [3, 4, 2],
state: 3
},
{
pos: [4, 4, 2],
state: 3
},
{
pos: [0, 1, 3],
state: 3
},
{
pos: [4, 1, 3],
state: 3
},
{
pos: [0, 2, 3],
state: 3
},
{
pos: [4, 2, 3],
state: 3
},
{
pos: [0, 3, 3],
state: 3
},
{
pos: [1, 3, 3],
state: 3
},
{
pos: [2, 3, 3],
state: 3
},
{
pos: [3, 3, 3],
state: 3
},
{
pos: [4, 3, 3],
state: 3
},
{
pos: [0, 4, 3],
state: 3
},
{
pos: [1, 4, 3],
state: 3
},
{
pos: [2, 4, 3],
state: 3
},
{
pos: [3, 4, 3],
state: 3
},
{
pos: [4, 4, 3],
state: 3
},
{
pos: [0, 1, 4],
state: 3
},
{
pos: [1, 1, 4],
state: 3
},
{
pos: [2, 1, 4],
state: 3
},
{
pos: [3, 1, 4],
state: 3
},
{
pos: [4, 1, 4],
state: 3
},
{
pos: [0, 2, 4],
state: 3
},
{
pos: [1, 2, 4],
state: 3
},
{
pos: [2, 2, 4],
state: 3
},
{
pos: [3, 2, 4],
state: 3
},
{
pos: [4, 2, 4],
state: 3
},
{
pos: [0, 3, 4],
state: 3
},
{
pos: [1, 3, 4],
state: 3
},
{
pos: [2, 3, 4],
state: 3
},
{
pos: [3, 3, 4],
state: 3
},
{
pos: [4, 3, 4],
state: 3
},
{
pos: [0, 4, 4],
state: 3
},
{
pos: [1, 4, 4],
state: 3
},
{
pos: [2, 4, 4],
state: 3
},
{
pos: [3, 4, 4],
state: 3
},
{
pos: [4, 4, 4],
state: 3
}
],
palette: [
{
Name: "minecraft:polished_andesite"
},
{
Name: "minecraft:white_stained_glass"
},
{
Properties: {
waterlogged: "false",
facing: "south"
},
Name: "computercraft:turtle_normal"
},
{
Name: "minecraft:air"
}
],
DataVersion: 2230
}

@ -3,7 +3,11 @@ import pathlib, sys
problems = False
# Skip images and files without extensions
exclude = [ "*.png", "**/data/json-parsing/*.json" ]
exclude = [
"*.png",
"**/data/json-parsing/*.json",
"**/computers/ids.json",
]
for path in pathlib.Path("src").glob("**/*"):
# Ideally we'd use generated as a glob, but .match("generated/**/*.json") doesn't work!