mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-11-10 11:59:59 +00:00
Merge branch 'mc-1.18.x' into mc-1.19.x
This commit is contained in:
commit
66dff1523b
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,8 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: ComputerCraft Discord
|
||||
url: https://discord.computercraft.cc
|
||||
about: Get help on the ComputerCraft Discord.
|
||||
- name: GitHub Discussions
|
||||
url: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
about: Or ask questions on GitHub Discussions.
|
||||
about: Ask questions on GitHub Discussions.
|
||||
|
27
.github/workflows/main-ci.yml
vendored
27
.github/workflows/main-ci.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
with:
|
||||
java-version: 8
|
||||
|
||||
- name: Cache gradle dependencies
|
||||
- name: Cache Gradle dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
@ -32,7 +32,7 @@ jobs:
|
||||
run: |
|
||||
./gradlew assemble || ./gradlew assemble
|
||||
./gradlew downloadAssets || ./gradlew downloadAssets
|
||||
xvfb-run ./gradlew build
|
||||
./gradlew build
|
||||
|
||||
- name: Upload Jar
|
||||
uses: actions/upload-artifact@v2
|
||||
@ -40,31 +40,12 @@ jobs:
|
||||
name: CC-Tweaked
|
||||
path: build/libs
|
||||
|
||||
- name: Upload Screnshots
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Screenshots
|
||||
path: test-files/client/screenshots
|
||||
if-no-files-found: ignore
|
||||
retention-days: 5
|
||||
if: failure()
|
||||
|
||||
- name: Upload Coverage
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
|
||||
- name: Parse test reports
|
||||
run: ./tools/parse-reports.py
|
||||
if: ${{ failure() }}
|
||||
|
||||
- name: Cache pre-commit
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: ${{ runner.os }}-pre-commit-${{ hashFiles('config/pre-commit/config.yml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pre-commit-
|
||||
|
||||
- name: Run linters
|
||||
run: |
|
||||
pip install pre-commit
|
||||
pre-commit run --config config/pre-commit/config.yml --show-diff-on-failure --all --color=always
|
||||
uses: pre-commit/action@v3.0.0
|
||||
|
6
.github/workflows/make-doc.yml
vendored
6
.github/workflows/make-doc.yml
vendored
@ -26,12 +26,6 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
|
||||
- name: Setup illuaminate
|
||||
run: |
|
||||
test -d bin || mkdir bin
|
||||
test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate
|
||||
chmod +x bin/illuaminate
|
||||
|
||||
- name: Setup node
|
||||
run: npm ci
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
/classes
|
||||
/logs
|
||||
/build
|
||||
/buildSrc/build
|
||||
/out
|
||||
/doc/out/
|
||||
/node_modules
|
||||
|
@ -17,6 +17,6 @@ vscode:
|
||||
|
||||
tasks:
|
||||
- name: Setup pre-commit hool
|
||||
init: pre-commit install --config config/pre-commit/config.yml --allow-missing-config
|
||||
init: pre-commit install --allow-missing-config
|
||||
- name: Install npm packages
|
||||
init: npm ci
|
||||
|
@ -41,8 +41,8 @@ repos:
|
||||
- id: illuaminate
|
||||
name: Check Lua code
|
||||
files: ".*\\.(lua|java|md)"
|
||||
language: script
|
||||
entry: config/pre-commit/illuaminate-lint.sh
|
||||
language: system
|
||||
entry: ./gradlew lintLua -i
|
||||
pass_filenames: false
|
||||
require_serial: true
|
||||
|
@ -39,40 +39,30 @@ are run whenever you submit a PR, it's often useful to run this before committin
|
||||
|
||||
- **[Checkstyle]:** Checks Java code to ensure it is consistently formatted. This can be run with `./gradlew build` or
|
||||
`./gradle check`.
|
||||
- **[illuaminate]:** Checks Lua code for semantic and styleistic issues. See [the usage section][illuaminate-usage] for
|
||||
how to download and run it. You may need to generate the Java documentation stubs (see "Documentation" below) for all
|
||||
lints to pass.
|
||||
- **[illuaminate]:** Checks Lua code for semantic and styleistic issues. This can be run with `./gradlew lintLua`.
|
||||
|
||||
### Documentation
|
||||
When writing documentation for [CC: Tweaked's documentation website][docs], it may be useful to build the documentation
|
||||
and preview it yourself before submitting a PR.
|
||||
|
||||
Building all documentation is, sadly, a multi-stage process (though this is largely hidden by Gradle). First we need to
|
||||
convert Java doc-comments into Lua ones, we also generate some Javascript to embed. All of this is then finally fed into
|
||||
illuaminate, which spits out our HTML.
|
||||
Our documentation generation pipeline is rather complex, and involves invoking several external tools. Most of this
|
||||
complexity is hidden by Gradle, but you will need to perform some initial setup:
|
||||
|
||||
#### Setting up the tooling
|
||||
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.
|
||||
- Install [Node/npm][node].
|
||||
- Run `npm ci` to install our Node dependencies.
|
||||
|
||||
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:
|
||||
|
||||
- `./gradlew luaJavadoc` - Generate documentation stubs for Java methods.
|
||||
- `./gradlew docWebsite` - Generate the whole website (including Javascript pages). The resulting HTML is stored at
|
||||
`./build/docs/site/`.
|
||||
You can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code, writing the resulting
|
||||
HTML into `./build/docs/site`.
|
||||
|
||||
#### Writing documentation
|
||||
illuaminate's documentation system is not currently documented (somewhat ironic), but is _largely_ the same as
|
||||
[ldoc][ldoc]. Documentation comments are written in Markdown,
|
||||
|
||||
Our markdown engine does _not_ support GitHub flavoured markdown, and so does not support all the features one might
|
||||
expect (such as tables). It is very much recommended that you build and preview the docs locally first.
|
||||
expect. It is recommended that you build and preview the docs locally first.
|
||||
|
||||
When iterating on documentation, you can get Gradle to rebuild the website every time a file changes by running
|
||||
`./gradlew docWebsite -t`. This will take a couple of seconds to run, but definitely beats running it manually!
|
||||
|
||||
### Testing
|
||||
Thankfully running tests is much simpler than running the documentation generator! `./gradlew check` will run the
|
||||
@ -90,11 +80,10 @@ Before we get into writing tests, it's worth mentioning the various test suites
|
||||
|
||||
These tests are run by the '"Core" Java' test suite, and so are also run with `./gradlew test`.
|
||||
|
||||
- In-game (`./src/testMod/java/dan200/computercraft/ingame/`): These tests are run on an actual Minecraft server and client,
|
||||
using [the same system Mojang do][mc-test]. The aim of these is to test in-game behaviour of blocks and peripherals.
|
||||
- In-game (`./src/testMod/java/dan200/computercraft/ingame/`): These tests are run on an actual Minecraft server, using
|
||||
the same system Mojang do][mc-test]. The aim of these is to test in-game behaviour of blocks and peripherals.
|
||||
|
||||
These are run by `./gradlew testClient` and `./gradlew testServer`. You may want to run the client under `xvfb-run`
|
||||
or similar when running in a headless environment.
|
||||
These tests are run with `./gradlew testServer`.
|
||||
|
||||
## CraftOS tests
|
||||
CraftOS's tests are written using a test system called "mcfly", heavily inspired by [busted] (and thus RSpec). Groups of
|
||||
@ -107,9 +96,9 @@ asserts that your variable `foo` is equal to the expected value `"bar"`.
|
||||
[community]: README.md#Community "Get in touch with the community."
|
||||
[checkstyle]: https://checkstyle.org/
|
||||
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
||||
[illuaminate-usage]: https://github.com/SquidDev/illuaminate/blob/master/README.md#usage "Installing Illuaminate"
|
||||
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
|
||||
[docs]: https://tweaked.cc/ "CC: Tweaked documentation"
|
||||
[ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator."
|
||||
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg
|
||||
[busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing."
|
||||
[node]: https://nodejs.org/en/ "Node.js"
|
||||
|
@ -13,9 +13,8 @@ developing the mod, [check out the instructions here](CONTRIBUTING.md#developing
|
||||
|
||||
## Community
|
||||
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
|
||||
ComputerCraft we have a [forum](https://forums.computercraft.cc/) and [Discord guild](https://discord.computercraft.cc)!
|
||||
There's also a fairly populated, albeit quiet [IRC channel](http://webchat.esper.net/?channels=computercraft), if that's
|
||||
more your cup of tea.
|
||||
ComputerCraft, do check out our [forum] and [GitHub discussions page][GitHub discussions]! There's also a fairly
|
||||
populated, albeit quiet [IRC channel][irc], if that's more your cup of tea.
|
||||
|
||||
We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
|
||||
|
||||
@ -52,3 +51,6 @@ the generated documentation [can be browsed online](https://tweaked.cc/javadoc/)
|
||||
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
|
||||
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||
[ccrestitched]: https://www.curseforge.com/minecraft/mc-mods/cc-restitched "Download CC: Restitched from CurseForge"
|
||||
[forum]: https://forums.computercraft.cc/
|
||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||
|
46
build.gradle
46
build.gradle
@ -11,9 +11,12 @@ plugins {
|
||||
id "org.spongepowered.mixin" version "0.7.+"
|
||||
id "org.parchmentmc.librarian.forgegradle" version "1.+"
|
||||
id "com.github.johnrengelman.shadow" version "7.1.2"
|
||||
id "cc-tweaked.illuaminate"
|
||||
}
|
||||
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
import cc.tweaked.gradle.IlluaminateExec
|
||||
import cc.tweaked.gradle.IlluaminateExecToDir
|
||||
|
||||
version = mod_version
|
||||
|
||||
@ -172,6 +175,10 @@ dependencies {
|
||||
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.7'
|
||||
}
|
||||
|
||||
illuaminate {
|
||||
version.set("0.1.0-3-g0f40379")
|
||||
}
|
||||
|
||||
// Compile tasks
|
||||
|
||||
javadoc {
|
||||
@ -318,32 +325,36 @@ def rollup = tasks.register("rollup", Exec.class) {
|
||||
commandLine mkCommand('"node_modules/.bin/rollup" --config rollup.config.js')
|
||||
}
|
||||
|
||||
def illuaminateDocs = tasks.register("illuaminateDocs", Exec.class) {
|
||||
def illuaminateDocs = tasks.register("illuaminateDocs", IlluaminateExecToDir.class) {
|
||||
group = "documentation"
|
||||
description = "Generates docs using Illuaminate"
|
||||
dependsOn(rollup, luaJavadoc)
|
||||
dependsOn(rollup)
|
||||
|
||||
inputs.files(fileTree("doc")).withPropertyName("docs")
|
||||
inputs.files(fileTree("src/main/resources/data/computercraft/lua/rom")).withPropertyName("lua rom")
|
||||
// Config files
|
||||
inputs.file("illuaminate.sexp").withPropertyName("illuaminate.sexp")
|
||||
inputs.dir("$buildDir/docs/luaJavadoc")
|
||||
// Sources
|
||||
inputs.files(fileTree("doc")).withPropertyName("docs")
|
||||
inputs.files(fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
|
||||
inputs.files(luaJavadoc)
|
||||
// Additional assets
|
||||
inputs.file("$buildDir/rollup/index.js").withPropertyName("scripts")
|
||||
inputs.file("src/web/styles.css").withPropertyName("styles")
|
||||
outputs.dir("$buildDir/docs/lua")
|
||||
|
||||
commandLine mkCommand('"bin/illuaminate" doc-gen')
|
||||
// Output directory. Also defined in illuaminate.sexp and transform.tsx
|
||||
output.set(new File(buildDir, "docs/lua"))
|
||||
|
||||
args = ["doc-gen"]
|
||||
}
|
||||
|
||||
def jsxDocs = tasks.register("jsxDocs", Exec) {
|
||||
group = "documentation"
|
||||
description = "Post-processes documentation to statically render some dynamic content."
|
||||
dependsOn(illuaminateDocs)
|
||||
|
||||
inputs.files(fileTree("src/web")).withPropertyName("sources")
|
||||
inputs.file("src/generated/export/index.json").withPropertyName("export")
|
||||
inputs.file("package-lock.json").withPropertyName("package-lock.json")
|
||||
inputs.file("tsconfig.json").withPropertyName("Typescript config")
|
||||
inputs.files(fileTree("$buildDir/docs/lua"))
|
||||
inputs.files(illuaminateDocs)
|
||||
outputs.dir("$buildDir/docs/site")
|
||||
|
||||
commandLine mkCommand('"node_modules/.bin/ts-node" -T --esm src/web/transform.tsx')
|
||||
@ -407,6 +418,23 @@ license {
|
||||
|
||||
check.dependsOn("licenseCheck")
|
||||
|
||||
def lintLua = tasks.register("lintLua", IlluaminateExec.class) {
|
||||
group = JavaBasePlugin.VERIFICATION_GROUP
|
||||
description = "Lint Lua (and Lua docs) with illuaminate"
|
||||
|
||||
// Config files
|
||||
inputs.file("illuaminate.sexp").withPropertyName("illuaminate.sexp")
|
||||
// Sources
|
||||
inputs.files(fileTree("doc")).withPropertyName("docs")
|
||||
inputs.files(fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
|
||||
inputs.files(luaJavadoc)
|
||||
|
||||
args = ["lint"]
|
||||
|
||||
doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
|
||||
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
|
||||
}
|
||||
|
||||
def testServerClassDumpDir = new File(buildDir, "jacocoClassDump/runTestServer")
|
||||
|
||||
def testServer = tasks.register("testServer", JavaExec.class) {
|
||||
|
18
buildSrc/build.gradle.kts
Normal file
18
buildSrc/build.gradle.kts
Normal file
@ -0,0 +1,18 @@
|
||||
plugins {
|
||||
`java-gradle-plugin`
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("cc-tweaked.illuaminate") {
|
||||
id = "cc-tweaked.illuaminate"
|
||||
implementationClass = "cc.tweaked.gradle.IlluaminatePlugin"
|
||||
}
|
||||
}
|
||||
}
|
11
buildSrc/src/main/kotlin/cc/tweaked/gradle/ExecTasks.kt
Normal file
11
buildSrc/src/main/kotlin/cc/tweaked/gradle/ExecTasks.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.AbstractExecTask
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import java.io.File
|
||||
|
||||
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
|
||||
@get:OutputDirectory
|
||||
abstract val output: Property<File>
|
||||
}
|
121
buildSrc/src/main/kotlin/cc/tweaked/gradle/Illuaminate.kt
Normal file
121
buildSrc/src/main/kotlin/cc/tweaked/gradle/Illuaminate.kt
Normal file
@ -0,0 +1,121 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.AbstractExecTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
|
||||
abstract class IlluaminateExtension {
|
||||
/** The version of illuaminate to use. */
|
||||
abstract val version: Property<String>
|
||||
|
||||
/** The path to illuaminate. If not given, illuaminate will be downloaded automatically. */
|
||||
abstract val file: Property<File>
|
||||
}
|
||||
|
||||
class IlluaminatePlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
val extension = project.extensions.create("illuaminate", IlluaminateExtension::class.java)
|
||||
extension.file.convention(setupDependency(project, extension.version))
|
||||
|
||||
project.tasks.register(SetupIlluaminate.NAME, SetupIlluaminate::class.java) {
|
||||
file.set(extension.file.map { it.absolutePath })
|
||||
}
|
||||
}
|
||||
|
||||
/** Set up a repository for illuaminate and download our binary from it. */
|
||||
private fun setupDependency(project: Project, version: Provider<String>): Provider<File> {
|
||||
project.repositories.ivy {
|
||||
name = "Illuaminate"
|
||||
setUrl("https://squiddev.cc/illuaminate/bin/")
|
||||
patternLayout {
|
||||
artifact("[revision]/[artifact]-[ext]")
|
||||
}
|
||||
metadataSources {
|
||||
artifact()
|
||||
}
|
||||
content {
|
||||
includeModule("cc.squiddev", "illuaminate")
|
||||
}
|
||||
}
|
||||
|
||||
return version.map {
|
||||
val dep = illuaminateArtifact(project, it)
|
||||
val configuration = project.configurations.detachedConfiguration(dep)
|
||||
configuration.isTransitive = false
|
||||
configuration.resolve().single()
|
||||
}
|
||||
}
|
||||
|
||||
/** Define a dependency for illuaminate from a version number and the current operating system. */
|
||||
private fun illuaminateArtifact(project: Project, version: String): Dependency {
|
||||
val osName = System.getProperty("os.name").toLowerCase()
|
||||
val (os, suffix) = when {
|
||||
osName.contains("windows") -> Pair("windows", ".exe")
|
||||
osName.contains("mac os") || osName.contains("darwin") -> Pair("macos", "")
|
||||
osName.contains("linux") -> Pair("linux", "")
|
||||
else -> error("Unsupported OS $osName for illuaminate")
|
||||
}
|
||||
|
||||
val osArch = System.getProperty("os.arch").toLowerCase()
|
||||
val arch = when {
|
||||
osArch == "arm" || osArch.startsWith("aarch") -> error("Unsupported architecture '$osArch' for illuaminate")
|
||||
osArch.contains("64") -> "x86_64"
|
||||
else -> error("Unsupported architecture $osArch for illuaminate")
|
||||
}
|
||||
|
||||
return project.dependencies.create(
|
||||
mapOf(
|
||||
"group" to "cc.squiddev",
|
||||
"name" to "illuaminate",
|
||||
"version" to version,
|
||||
"ext" to "$os-$arch$suffix",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val Task.illuaminatePath: String? // "?" needed to avoid overload ambiguity in setExecutable below.
|
||||
get() = project.extensions.getByType(IlluaminateExtension::class.java).file.get().absolutePath
|
||||
|
||||
/** Prepares illuaminate for being run. This simply requests the dependency and then marks it as executable. */
|
||||
abstract class SetupIlluaminate : DefaultTask() {
|
||||
@get:Input
|
||||
abstract val file: Property<String>
|
||||
|
||||
@TaskAction
|
||||
fun setExecutable() {
|
||||
val file = File(this.file.get())
|
||||
if (file.canExecute()) {
|
||||
didWork = false
|
||||
return
|
||||
}
|
||||
|
||||
file.setExecutable(true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME: String = "setupIlluaminate"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class IlluaminateExec : AbstractExecTask<IlluaminateExec>(IlluaminateExec::class.java) {
|
||||
init {
|
||||
dependsOn(SetupIlluaminate.NAME)
|
||||
executable = illuaminatePath
|
||||
}
|
||||
}
|
||||
|
||||
abstract class IlluaminateExecToDir : ExecToDir() {
|
||||
init {
|
||||
dependsOn(SetupIlluaminate.NAME)
|
||||
executable = illuaminatePath
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
set -e
|
||||
|
||||
test -d bin || mkdir bin
|
||||
test -f bin/illuaminate || curl -s -obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate
|
||||
chmod +x bin/illuaminate
|
||||
|
||||
if [ -n ${GITHUB_ACTIONS+x} ]; then
|
||||
# Register a problem matcher (see https://github.com/actions/toolkit/blob/master/docs/problem-matchers.md)
|
||||
# for illuaminate.
|
||||
echo "::add-matcher::.github/matchers/illuaminate.json"
|
||||
trap 'echo "::remove-matcher owner=illuaminate::"' EXIT
|
||||
fi
|
||||
|
||||
./gradlew luaJavadoc
|
||||
bin/illuaminate lint
|
@ -185,7 +185,7 @@ end
|
||||
|
||||
:::note Confused?
|
||||
Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
|
||||
cover. That said, don't be afraid to ask on [Discord] or [IRC] either!
|
||||
cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
|
||||
:::
|
||||
|
||||
It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
|
||||
@ -200,6 +200,5 @@ This is, I'm afraid, left as an exercise to the reader.
|
||||
[PCM]: https://en.wikipedia.org/wiki/Pulse-code_modulation "Pulse-code Modulation - Wikipedia"
|
||||
[Ring Buffer]: https://en.wikipedia.org/wiki/Circular_buffer "Circular buffer - Wikipedia"
|
||||
[Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia"
|
||||
|
||||
[Discord]: https://discord.computercraft.cc "The Minecraft Computer Mods Discord"
|
||||
[IRC]: http://webchat.esper.net/?channels=computercraft "IRC webchat on EsperNet"
|
||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||
|
@ -37,8 +37,7 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
|
||||
Once you're a little more familiar with the mod, the sidebar and links below provide more detailed documentation on the
|
||||
various APIs and peripherals provided by the mod.
|
||||
|
||||
If you get stuck, do pop in to the [Minecraft Computer Mod Discord guild][discord] or ComputerCraft's
|
||||
[IRC channel][irc].
|
||||
If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
|
||||
|
||||
## Get Involved
|
||||
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
|
||||
@ -51,5 +50,5 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
|
||||
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||
[ccrestitched]: https://www.curseforge.com/minecraft/mc-mods/cc-restitched "Download CC: Restitched from CurseForge"
|
||||
[lua]: https://www.lua.org/ "Lua's main website"
|
||||
[discord]: https://discord.computercraft.cc "The Minecraft Computer Mods Discord"
|
||||
[irc]: http://webchat.esper.net/?channels=computercraft "IRC webchat on EsperNet"
|
||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||
|
@ -14,7 +14,7 @@ thread, not the whole program.
|
||||
|
||||
:::tip
|
||||
Because sleep internally uses timers, it is a function that yields. This means
|
||||
that you can use it to prevent "Too long without yielding" errors, however, as
|
||||
that you can use it to prevent "Too long without yielding" errors. However, as
|
||||
the minimum sleep time is 0.05 seconds, it will slow your program down.
|
||||
:::
|
||||
|
||||
|
@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx3G
|
||||
kotlin.stdlib.default.dependency=false
|
||||
|
||||
# Mod properties
|
||||
mod_version=1.100.9
|
||||
mod_version=1.100.10
|
||||
|
||||
# Minecraft properties (update mods.toml when changing)
|
||||
mc_version=1.19.2
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@ -24,7 +24,7 @@ final class LuaDateTime
|
||||
{
|
||||
}
|
||||
|
||||
static void format( DateTimeFormatterBuilder formatter, String format, ZoneOffset offset ) throws LuaException
|
||||
static void format( DateTimeFormatterBuilder formatter, String format ) throws LuaException
|
||||
{
|
||||
for( int i = 0; i < format.length(); )
|
||||
{
|
||||
@ -61,7 +61,7 @@ final class LuaDateTime
|
||||
formatter.appendText( ChronoField.MONTH_OF_YEAR, TextStyle.FULL );
|
||||
break;
|
||||
case 'c':
|
||||
format( formatter, "%a %b %e %H:%M:%S %Y", offset );
|
||||
format( formatter, "%a %b %e %H:%M:%S %Y" );
|
||||
break;
|
||||
case 'C':
|
||||
formatter.appendValueReduced( CENTURY, 2, 2, 0 );
|
||||
@ -71,13 +71,13 @@ final class LuaDateTime
|
||||
break;
|
||||
case 'D':
|
||||
case 'x':
|
||||
format( formatter, "%m/%d/%y", offset );
|
||||
format( formatter, "%m/%d/%y" );
|
||||
break;
|
||||
case 'e':
|
||||
formatter.padNext( 2 ).appendValue( ChronoField.DAY_OF_MONTH );
|
||||
break;
|
||||
case 'F':
|
||||
format( formatter, "%Y-%m-%d", offset );
|
||||
format( formatter, "%Y-%m-%d" );
|
||||
break;
|
||||
case 'g':
|
||||
formatter.appendValueReduced( IsoFields.WEEK_BASED_YEAR, 2, 2, 0 );
|
||||
@ -107,10 +107,10 @@ final class LuaDateTime
|
||||
formatter.appendText( ChronoField.AMPM_OF_DAY );
|
||||
break;
|
||||
case 'r':
|
||||
format( formatter, "%I:%M:%S %p", offset );
|
||||
format( formatter, "%I:%M:%S %p" );
|
||||
break;
|
||||
case 'R':
|
||||
format( formatter, "%H:%M", offset );
|
||||
format( formatter, "%H:%M" );
|
||||
break;
|
||||
case 'S':
|
||||
formatter.appendValue( ChronoField.SECOND_OF_MINUTE, 2 );
|
||||
@ -120,7 +120,7 @@ final class LuaDateTime
|
||||
break;
|
||||
case 'T':
|
||||
case 'X':
|
||||
format( formatter, "%H:%M:%S", offset );
|
||||
format( formatter, "%H:%M:%S" );
|
||||
break;
|
||||
case 'u':
|
||||
formatter.appendValue( ChronoField.DAY_OF_WEEK );
|
||||
@ -212,15 +212,13 @@ final class LuaDateTime
|
||||
throw new LuaException( "field \"" + field + "\" missing in date table" );
|
||||
}
|
||||
|
||||
private static final TemporalField CENTURY = map( ChronoField.YEAR, ValueRange.of( 0, 6 ), x -> (x / 100) % 100 );
|
||||
private static final TemporalField CENTURY = map( ChronoField.YEAR, ValueRange.of( 0, 99 ), x -> (x / 100) % 100 );
|
||||
private static final TemporalField ZERO_WEEK = map( WeekFields.SUNDAY_START.dayOfWeek(), ValueRange.of( 0, 6 ), x -> x - 1 );
|
||||
|
||||
private static TemporalField map( TemporalField field, ValueRange range, LongUnaryOperator convert )
|
||||
{
|
||||
return new TemporalField()
|
||||
{
|
||||
private final ValueRange range = ValueRange.of( 0, 99 );
|
||||
|
||||
@Override
|
||||
public TemporalUnit getBaseUnit()
|
||||
{
|
||||
|
@ -493,7 +493,7 @@ public class OSAPI implements ILuaAPI
|
||||
if( format.equals( "*t" ) ) return LuaDateTime.toTable( date, offset, instant );
|
||||
|
||||
DateTimeFormatterBuilder formatter = new DateTimeFormatterBuilder();
|
||||
LuaDateTime.format( formatter, format, offset );
|
||||
LuaDateTime.format( formatter, format );
|
||||
return formatter.toFormatter( Locale.ROOT ).format( date );
|
||||
}
|
||||
|
||||
|
@ -323,8 +323,9 @@ public class TurtleBrain implements ITurtleAccess
|
||||
|
||||
try
|
||||
{
|
||||
// Create a new turtle
|
||||
if( world.setBlock( pos, newState, 0 ) )
|
||||
// We use Block.UPDATE_CLIENTS here to ensure that neighbour updates caused in Block.updateNeighbourShapes
|
||||
// are sent to the client. We want to avoid doing a full block update until the turtle state is copied over.
|
||||
if( world.setBlock( pos, newState, 2 ) )
|
||||
{
|
||||
Block block = world.getBlockState( pos ).getBlock();
|
||||
if( block == oldBlock.getBlock() )
|
||||
|
@ -259,7 +259,7 @@ input should the whole output not fit on the display.
|
||||
local rows = {}
|
||||
for i = 1, 30 do rows[i] = {("Row #%d"):format(i), math.random(1, 400)} end
|
||||
|
||||
textutils.tabulate(colors.orange, {"Column", "Value"}, colors.lightBlue, table.unpack(rows))
|
||||
textutils.pagedTabulate(colors.orange, {"Column", "Value"}, colors.lightBlue, table.unpack(rows))
|
||||
]]
|
||||
function pagedTabulate(...)
|
||||
return tabulateCommon(true, ...)
|
||||
@ -749,9 +749,9 @@ suitable for pretty printing.
|
||||
@usage Demonstrates some of the other options
|
||||
|
||||
local tbl = { 1, 2, 3 }
|
||||
print(textutils.serialize({ tbl, tbl }, { allow_repetitions = true }))
|
||||
print(textutils.serialise({ tbl, tbl }, { allow_repetitions = true }))
|
||||
|
||||
print(textutils.serialize(tbl, { compact = true }))
|
||||
print(textutils.serialise(tbl, { compact = true }))
|
||||
]]
|
||||
function serialize(t, opts)
|
||||
local tTracking = {}
|
||||
@ -770,7 +770,7 @@ serialise = serialize -- GB version
|
||||
|
||||
--- Converts a serialised string back into a reassembled Lua object.
|
||||
--
|
||||
-- This is mainly used together with @{textutils.serialize}.
|
||||
-- This is mainly used together with @{textutils.serialise}.
|
||||
--
|
||||
-- @tparam string s The serialised string to deserialise.
|
||||
-- @return[1] The deserialised object
|
||||
@ -807,10 +807,10 @@ unserialise = unserialize -- GB version
|
||||
-- @throws If the object contains a value which cannot be
|
||||
-- serialised. This includes functions and tables which appear multiple
|
||||
-- times.
|
||||
-- @usage textutils.serializeJSON({ values = { 1, "2", true } })
|
||||
-- @usage textutils.serialiseJSON({ values = { 1, "2", true } })
|
||||
-- @since 1.7
|
||||
-- @see textutils.json_null Use to serialize a JSON `null` value.
|
||||
-- @see textutils.empty_json_array Use to serialize a JSON empty array.
|
||||
-- @see textutils.json_null Use to serialise a JSON `null` value.
|
||||
-- @see textutils.empty_json_array Use to serialise a JSON empty array.
|
||||
function serializeJSON(t, bNBTStyle)
|
||||
expect(1, t, "table", "string", "number", "boolean")
|
||||
expect(2, bNBTStyle, "boolean", "nil")
|
||||
|
@ -1,3 +1,14 @@
|
||||
# New features in CC: Tweaked 1.100.10
|
||||
|
||||
* Mention WAV support in speaker help (MCJack123).
|
||||
* Add http programs to the path, even when http is not enabled.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix example in textutils.pagedTabulate docs (IvoLeal72).
|
||||
* Fix help program treating the terminal one line longer than it was.
|
||||
* Send block updates to client when turtle moves (roland-a).
|
||||
* Resolve several monitor issues when running Occulus shaders.
|
||||
|
||||
# New features in CC: Tweaked 1.100.9
|
||||
|
||||
* Add documentation for setting up GPS (Lupus590).
|
||||
|
@ -1,5 +1,9 @@
|
||||
The speaker program plays audio files using speakers attached to this computer.
|
||||
|
||||
It supports audio files in a limited number of formats:
|
||||
* DFPWM: You can convert music to DFPWM with external tools like https://music.madefor.cc.
|
||||
* WAV: WAV files must be 8-bit PCM or DFPWM, with exactly one channel and a sample rate of 48kHz.
|
||||
|
||||
## Examples:
|
||||
- `speaker play example.dfpwm left` plays the "example.dfpwm" audio file using the speaker on the left of the computer.
|
||||
- `speaker stop` stops any currently playing audio.
|
||||
* `speaker play example.dfpwm left` plays the "example.dfpwm" audio file using the speaker on the left of the computer.
|
||||
* `speaker stop` stops any currently playing audio.
|
||||
|
@ -1,15 +1,12 @@
|
||||
New features in CC: Tweaked 1.100.9
|
||||
New features in CC: Tweaked 1.100.10
|
||||
|
||||
* Add documentation for setting up GPS (Lupus590).
|
||||
* Add WAV support to the `speaker` program (MCJack123).
|
||||
* Expose item groups in `getItemDetail` (itisluiz).
|
||||
* Other fixes to documentation (Erb3, JohnnyIrvin).
|
||||
* Add Norwegian translation (Erb3).
|
||||
* Mention WAV support in speaker help (MCJack123).
|
||||
* Add http programs to the path, even when http is not enabled.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix z-fighting on bold printout borders (toad-dev).
|
||||
* Fix `term.blit` failing on certain strings.
|
||||
* Fix `getItemLimit()` using the wrong slot (heap-underflow).
|
||||
* Increase size of monitor depth blocker.
|
||||
* Fix example in textutils.pagedTabulate docs (IvoLeal72).
|
||||
* Fix help program treating the terminal one line longer than it was.
|
||||
* Send block updates to client when turtle moves (roland-a).
|
||||
* Resolve several monitor issues when running Occulus shaders.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
@ -146,14 +146,17 @@ end
|
||||
|
||||
local contents = file:read("*a")
|
||||
file:close()
|
||||
-- Trim trailing newlines from the file to avoid displaying a blank line.
|
||||
if contents:sub(-1) == "\n" then contents:sub(1, -2) end
|
||||
|
||||
local word_wrap = sFile:sub(-3) == ".md" and word_wrap_markdown or word_wrap_basic
|
||||
local width, height = term.getSize()
|
||||
local content_height = height - 1 -- Height of the content box.
|
||||
local lines, fg, bg, sections = word_wrap(contents, width)
|
||||
local print_height = #lines
|
||||
|
||||
-- If we fit within the screen, just display without pagination.
|
||||
if print_height <= height then
|
||||
if print_height <= content_height then
|
||||
local _, y = term.getCursorPos()
|
||||
for i = 1, print_height do
|
||||
if y + i - 1 > height then
|
||||
@ -201,7 +204,7 @@ end
|
||||
|
||||
|
||||
local function draw()
|
||||
for y = 1, height - 1 do
|
||||
for y = 1, content_height do
|
||||
term.setCursorPos(1, y)
|
||||
if y + offset > print_height then
|
||||
-- Should only happen if we resize the terminal to a larger one
|
||||
@ -228,14 +231,14 @@ while true do
|
||||
if param == keys.up and offset > 0 then
|
||||
offset = offset - 1
|
||||
draw()
|
||||
elseif param == keys.down and offset < print_height - height then
|
||||
elseif param == keys.down and offset < print_height - content_height then
|
||||
offset = offset + 1
|
||||
draw()
|
||||
elseif param == keys.pageUp and offset > 0 then
|
||||
offset = math.max(offset - height + 2, 0)
|
||||
offset = math.max(offset - content_height + 1, 0)
|
||||
draw()
|
||||
elseif param == keys.pageDown and offset < print_height - height then
|
||||
offset = math.min(offset + height - 2, print_height - height)
|
||||
elseif param == keys.pageDown and offset < print_height - content_height then
|
||||
offset = math.min(offset + content_height - 1, print_height - content_height)
|
||||
draw()
|
||||
elseif param == keys.home then
|
||||
offset = 0
|
||||
@ -247,7 +250,7 @@ while true do
|
||||
offset = sections[current_section + 1].offset
|
||||
draw()
|
||||
elseif param == keys["end"] then
|
||||
offset = print_height - height
|
||||
offset = print_height - content_height
|
||||
draw()
|
||||
elseif param == keys.q then
|
||||
sleep(0) -- Super janky, but consumes stray "char" events.
|
||||
@ -257,7 +260,7 @@ while true do
|
||||
if param < 0 and offset > 0 then
|
||||
offset = offset - 1
|
||||
draw()
|
||||
elseif param > 0 and offset < print_height - height then
|
||||
elseif param > 0 and offset <= print_height - content_height then
|
||||
offset = offset + 1
|
||||
draw()
|
||||
end
|
||||
@ -270,7 +273,8 @@ while true do
|
||||
end
|
||||
|
||||
width, height = new_width, new_height
|
||||
offset = math.max(math.min(offset, print_height - height), 0)
|
||||
content_height = height - 1
|
||||
offset = math.max(math.min(offset, print_height - content_height), 0)
|
||||
draw()
|
||||
draw_menu()
|
||||
elseif event == "terminate" then
|
||||
|
@ -13,8 +13,8 @@ if #tArgs < 2 then
|
||||
end
|
||||
|
||||
if not http then
|
||||
printError("Pastebin requires the http API")
|
||||
printError("Set http.enabled to true in CC: Tweaked's config")
|
||||
printError("Pastebin requires the http API, but it is not enabled")
|
||||
printError("Set http.enabled to true in CC: Tweaked's server config")
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -21,8 +21,8 @@ end
|
||||
local url = table.remove(tArgs, 1)
|
||||
|
||||
if not http then
|
||||
printError("wget requires the http API")
|
||||
printError("Set http.enabled to true in CC: Tweaked's config")
|
||||
printError("wget requires the http API, but it is not enabled")
|
||||
printError("Set http.enabled to true in CC: Tweaked's server config")
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
local completion = require "cc.shell.completion"
|
||||
|
||||
-- Setup paths
|
||||
local sPath = ".:/rom/programs"
|
||||
local sPath = ".:/rom/programs:/rom/programs/http"
|
||||
if term.isColor() then
|
||||
sPath = sPath .. ":/rom/programs/advanced"
|
||||
end
|
||||
@ -19,9 +19,6 @@ end
|
||||
if commands then
|
||||
sPath = sPath .. ":/rom/programs/command"
|
||||
end
|
||||
if http then
|
||||
sPath = sPath .. ":/rom/programs/http"
|
||||
end
|
||||
shell.setPath(sPath)
|
||||
help.setPath("/rom/help")
|
||||
|
||||
|
@ -1,8 +1,46 @@
|
||||
local capture = require "test_helpers".capture_program
|
||||
local with_window_lines = require "test_helpers".with_window_lines
|
||||
|
||||
describe("The help program", function()
|
||||
local function stub_help(content)
|
||||
local name = "/help_file.txt"
|
||||
io.open(name, "wb"):write(content):close()
|
||||
stub(help, "lookup", function() return name end)
|
||||
end
|
||||
|
||||
local function capture_help(width, height, content)
|
||||
stub_help(content)
|
||||
|
||||
local co = coroutine.create(shell.run)
|
||||
local window = with_window_lines(width, height, function()
|
||||
local ok, err = coroutine.resume(co, "help topic")
|
||||
if not ok then error(err, 0) end
|
||||
end)
|
||||
return coroutine.status(co) == "dead", window
|
||||
end
|
||||
|
||||
it("errors when there is no such help file", function()
|
||||
expect(capture(stub, "help nothing"))
|
||||
:matches { ok = true, error = "No help available\n", output = "" }
|
||||
end)
|
||||
|
||||
it("prints a short file directly", function()
|
||||
local dead, output = capture_help(10, 3, "a short\nfile")
|
||||
expect(dead):eq(true)
|
||||
expect(output):same {
|
||||
"a short ",
|
||||
"file ",
|
||||
" ",
|
||||
}
|
||||
end)
|
||||
|
||||
it("launches the viewer for a longer file", function()
|
||||
local dead, output = capture_help(10, 3, "a longer\nfile\nwith content")
|
||||
expect(dead):eq(false)
|
||||
expect(output):same {
|
||||
"a longer ",
|
||||
"file ",
|
||||
"Help: topi",
|
||||
}
|
||||
end)
|
||||
end)
|
||||
|
@ -56,7 +56,22 @@ local function with_window(width, height, fn)
|
||||
return redirect
|
||||
end
|
||||
|
||||
--- Run a function redirecting to a new window with the given dimensions,
|
||||
-- returning the content of the window.
|
||||
--
|
||||
-- @tparam number width The window's width
|
||||
-- @tparam number height The window's height
|
||||
-- @tparam function() fn The action to run
|
||||
-- @treturn {string...} The content of the window.
|
||||
local function with_window_lines(width, height, fn)
|
||||
local window = with_window(width, height, fn)
|
||||
local out = {}
|
||||
for i = 1, height do out[i] = window.getLine(i) end
|
||||
return out
|
||||
end
|
||||
|
||||
return {
|
||||
capture_program = capture_program,
|
||||
with_window = with_window,
|
||||
with_window_lines = with_window_lines,
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
:root {
|
||||
--nav-width: 250px;
|
||||
}
|
||||
/* Some misc styles */
|
||||
|
||||
.big-image {
|
||||
|
Loading…
Reference in New Issue
Block a user