mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-16 14:37:39 +00:00
Compare commits
88 Commits
v1.20.4-1.
...
v1.20.1-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
209b1ddbf9 | ||
![]() |
0c9f9a8652 | ||
![]() |
862d92785e | ||
![]() |
d48b85d50c | ||
![]() |
4d619de357 | ||
![]() |
57c289f173 | ||
![]() |
f63f85921f | ||
![]() |
c7e49d1929 | ||
![]() |
1e214f329e | ||
![]() |
de930c8d09 | ||
![]() |
735e7ce09b | ||
![]() |
6e9799316a | ||
![]() |
4e90240922 | ||
![]() |
1a87d1bf45 | ||
![]() |
00e2e2bd2d | ||
![]() |
7c1f40031b | ||
![]() |
929debd382 | ||
![]() |
4980b7355d | ||
![]() |
925092add3 | ||
![]() |
550296edc5 | ||
![]() |
0771c4891b | ||
![]() |
776fa00b94 | ||
![]() |
03bb279206 | ||
![]() |
fabd77132d | ||
![]() |
95be0a25bf | ||
![]() |
ad49325376 | ||
![]() |
825d45eb26 | ||
![]() |
8b2516abb5 | ||
![]() |
bce099ef32 | ||
![]() |
6d14ce625f | ||
![]() |
c8eadf4011 | ||
![]() |
0c1ab780bb | ||
![]() |
0f623c2cca | ||
![]() |
b9ba2534a4 | ||
![]() |
c764981a40 | ||
![]() |
6363164f2b | ||
![]() |
63580b4acb | ||
![]() |
9af1aa1ecf | ||
![]() |
ad0f551204 | ||
![]() |
0d3e00cc41 | ||
![]() |
836d6b939e | ||
![]() |
0e5248e5e6 | ||
![]() |
e154b0db2a | ||
![]() |
ae767eb5be | ||
![]() |
777aa34bb0 | ||
![]() |
286f969f94 | ||
![]() |
57c72711bb | ||
![]() |
cbafbca86b | ||
![]() |
c9caffb10f | ||
![]() |
4675583e1c | ||
![]() |
afe16cc593 | ||
![]() |
0abd107348 | ||
![]() |
cef4b4906b | ||
![]() |
04900dc82f | ||
![]() |
9b63cc81b1 | ||
![]() |
9eead7a0ec | ||
![]() |
ad97b2922b | ||
![]() |
52986f8d73 | ||
![]() |
ab00580389 | ||
![]() |
128ac2f109 | ||
![]() |
5d8c46c7e6 | ||
![]() |
1a5dc92bd4 | ||
![]() |
98b2d3f310 | ||
![]() |
e92c2d02f8 | ||
![]() |
f8ef40d378 | ||
![]() |
61f9b1d0c6 | ||
![]() |
ffb62dfa02 | ||
![]() |
6fb291112d | ||
![]() |
7ee821e9c9 | ||
![]() |
b7df91349a | ||
![]() |
cb8e06af2a | ||
![]() |
6478fca7a2 | ||
![]() |
3493159a05 | ||
![]() |
eead67e314 | ||
![]() |
3b8813cf8f | ||
![]() |
a9191a4d4e | ||
![]() |
451a2593ce | ||
![]() |
d38b1da974 | ||
![]() |
6e374579a4 | ||
![]() |
4daa2a2b6a | ||
![]() |
84b6edab82 | ||
![]() |
31aaf46d09 | ||
![]() |
2d11b51c62 | ||
![]() |
a0f759527d | ||
![]() |
385e4210fa | ||
![]() |
d2896473f2 | ||
![]() |
f14cb2a3d1 | ||
![]() |
8db5c6bc3a |
30
.github/workflows/main-ci.yml
vendored
30
.github/workflows/main-ci.yml
vendored
@@ -9,16 +9,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 📥 Clone repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 📥 Set up Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: 📥 Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
|
||||
@@ -58,13 +58,13 @@ jobs:
|
||||
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
|
||||
|
||||
- name: 📤 Upload Jar
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CC-Tweaked
|
||||
path: ./jars
|
||||
|
||||
- name: 📤 Upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
|
||||
build-core:
|
||||
strategy:
|
||||
@@ -81,24 +81,28 @@ jobs:
|
||||
runs-on: ${{ matrix.uses }}
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v3
|
||||
- name: 📥 Clone repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v3
|
||||
- name: 📥 Set up Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: 📥 Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
|
||||
- name: Run tests
|
||||
- name: ⚒️ Build
|
||||
run: |
|
||||
./gradlew --configure-on-demand :core:assemble
|
||||
|
||||
- name: 🧪 Run tests
|
||||
run: |
|
||||
./gradlew --configure-on-demand :core:test
|
||||
|
||||
- name: Parse test reports
|
||||
- name: 🧪 Parse test reports
|
||||
run: python3 ./tools/parse-reports.py
|
||||
if: ${{ failure() }}
|
||||
|
9
.github/workflows/make-doc.yml
vendored
9
.github/workflows/make-doc.yml
vendored
@@ -3,8 +3,7 @@ name: Build documentation
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- mc-1.19.x
|
||||
- mc-1.20.x
|
||||
- mc-*
|
||||
|
||||
jobs:
|
||||
make_doc:
|
||||
@@ -13,16 +12,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,6 +9,7 @@
|
||||
/projects/*/logs
|
||||
/projects/fabric/fabricloader.log
|
||||
/projects/*/build
|
||||
/projects/*/src/test/generated_tests/
|
||||
/buildSrc/build
|
||||
/out
|
||||
/buildSrc/out
|
||||
|
26
.gitpod.yml
26
.gitpod.yml
@@ -1,26 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
image:
|
||||
file: config/gitpod/Dockerfile
|
||||
|
||||
ports:
|
||||
- port: 25565
|
||||
onOpen: notify
|
||||
|
||||
vscode:
|
||||
extensions:
|
||||
- eamodio.gitlens
|
||||
- github.vscode-pull-request-github
|
||||
- ms-azuretools.vscode-docker
|
||||
- redhat.java
|
||||
- richardwillis.vscode-gradle
|
||||
- vscjava.vscode-java-debug
|
||||
- vscode.github
|
||||
|
||||
tasks:
|
||||
- name: Setup pre-commit hool
|
||||
init: pre-commit install --allow-missing-config
|
||||
- name: Install npm packages
|
||||
init: npm ci
|
@@ -8,8 +8,7 @@ Files:
|
||||
projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg
|
||||
projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/*
|
||||
projects/common/src/testMod/resources/data/cctest/structures/*
|
||||
projects/fabric/src/generated/*
|
||||
projects/forge/src/generated/*
|
||||
projects/*/src/generated/*
|
||||
projects/web/src/htmlTransform/export/index.json
|
||||
projects/web/src/htmlTransform/export/items/minecraft/*
|
||||
Comment: Generated/data files are CC0.
|
||||
|
@@ -29,9 +29,9 @@ automatically with GitHub, so please don't submit PRs adding/changing translatio
|
||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||
|
||||
- Make sure you've got the following software installed:
|
||||
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
|
||||
- Java Development Kit (JDK). This can be downloaded from [Adoptium].
|
||||
- [Git](https://git-scm.com/).
|
||||
- If you want to work on documentation, [NodeJS][node].
|
||||
- [NodeJS][node].
|
||||
|
||||
- Download CC: Tweaked's source code:
|
||||
```
|
||||
|
@@ -5,9 +5,8 @@
|
||||
import cc.tweaked.gradle.JUnitExt
|
||||
import net.fabricmc.loom.api.LoomGradleExtensionAPI
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper
|
||||
import org.jetbrains.gradle.ext.compiler
|
||||
import org.jetbrains.gradle.ext.runConfigurations
|
||||
import org.jetbrains.gradle.ext.settings
|
||||
import org.jetbrains.gradle.ext.*
|
||||
import org.jetbrains.gradle.ext.Application
|
||||
|
||||
plugins {
|
||||
publishing
|
||||
@@ -86,6 +85,19 @@ idea.project.settings.runConfigurations {
|
||||
moduleName = "${idea.project.name}.forge.test"
|
||||
packageName = ""
|
||||
}
|
||||
|
||||
register<Application>("Standalone") {
|
||||
moduleName = "${idea.project.name}.standalone.main"
|
||||
mainClass = "cc.tweaked.standalone.Main"
|
||||
programParameters = "--resources=projects/core/src/main/resources --term=80x30 --allow-local-domains"
|
||||
}
|
||||
}
|
||||
|
||||
// Build with the IntelliJ, rather than through Gradle. This may require setting the "Compiler Output" option in
|
||||
// "Project Structure".
|
||||
idea.project.settings.delegateActions {
|
||||
delegateBuildRunToGradle = false
|
||||
testRunner = ActionDelegationConfig.TestRunner.PLATFORM
|
||||
}
|
||||
|
||||
idea.project.settings.compiler.javac {
|
||||
|
@@ -40,10 +40,6 @@ repositories {
|
||||
|
||||
val mainMaven = maven("https://squiddev.cc/maven") {
|
||||
name = "SquidDev"
|
||||
content {
|
||||
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
||||
includeModule("org.spongepowered", "mixin")
|
||||
}
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
@@ -98,9 +94,8 @@ sourceSets.all {
|
||||
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
|
||||
// Too many false positives right now. Maybe we need an indirection for it later on.
|
||||
check("ReferenceEquality", CheckSeverity.OFF)
|
||||
check("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
|
||||
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
|
||||
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
||||
check("AlreadyChecked", CheckSeverity.OFF) // Seems to be broken?
|
||||
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
|
||||
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
||||
|
||||
@@ -113,6 +108,8 @@ sourceSets.all {
|
||||
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||
option("NullAway:CheckOptionalEmptiness")
|
||||
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
||||
|
||||
excludedPaths = ".*/jmh_generated/.*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ val publishCurseForge by tasks.registering(TaskPublishCurseForge::class) {
|
||||
apiToken = findProperty("curseForgeApiKey") ?: ""
|
||||
enabled = apiToken != ""
|
||||
|
||||
val mainFile = upload("282001", modPublishing.output.get().archiveFile)
|
||||
val mainFile = upload("282001", modPublishing.output)
|
||||
mainFile.changelog =
|
||||
"Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
||||
mainFile.changelogType = "markdown"
|
||||
|
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.util.regex.Pattern
|
||||
|
||||
abstract class CCTweakedExtension(
|
||||
@@ -226,12 +225,12 @@ abstract class CCTweakedExtension(
|
||||
* where possible.
|
||||
*/
|
||||
fun downloadFile(label: String, url: String): File {
|
||||
val url = URL(url)
|
||||
val path = File(url.path)
|
||||
val uri = URI(url)
|
||||
val path = File(uri.path)
|
||||
|
||||
project.repositories.ivy {
|
||||
name = label
|
||||
setUrl(URI(url.protocol, url.userInfo, url.host, url.port, path.parent, null, null))
|
||||
setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null))
|
||||
patternLayout {
|
||||
artifact("[artifact].[ext]")
|
||||
}
|
||||
|
120
buildSrc/src/main/kotlin/cc/tweaked/gradle/MergeTrees.kt
Normal file
120
buildSrc/src/main/kotlin/cc/tweaked/gradle/MergeTrees.kt
Normal file
@@ -0,0 +1,120 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import cc.tweaked.vanillaextract.core.util.MoreFiles
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.*
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Merge common files across multiple directories into one destination directory.
|
||||
*
|
||||
* This is intended for merging the generated resources from the Forge and Fabric projects. Files common between the two
|
||||
* are written to the global [output] directory, while distinct files are written to the per-source
|
||||
* [MergeTrees.Source.output] directory.
|
||||
*/
|
||||
abstract class MergeTrees : DefaultTask() {
|
||||
/**
|
||||
* A source directory to read from.
|
||||
*/
|
||||
interface Source {
|
||||
/**
|
||||
* The folder contianing all input files.
|
||||
*/
|
||||
@get:InputFiles
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
val input: ConfigurableFileTree
|
||||
|
||||
fun input(configure: Action<ConfigurableFileTree>) {
|
||||
configure.execute(input)
|
||||
}
|
||||
|
||||
/**
|
||||
* The folder to write files unique to this folder to.
|
||||
*/
|
||||
@get:OutputDirectory
|
||||
val output: DirectoryProperty
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of sources.
|
||||
*/
|
||||
@get:Nested
|
||||
abstract val sources: ListProperty<Source>
|
||||
|
||||
/**
|
||||
* Add and configure a new source.
|
||||
*/
|
||||
fun source(configure: Action<Source>) {
|
||||
val instance = objectFactory.newInstance(Source::class.java)
|
||||
configure.execute(instance)
|
||||
instance.output.disallowChanges()
|
||||
sources.add(instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* The directory to write common files to.
|
||||
*/
|
||||
@get:OutputDirectory
|
||||
abstract val output: DirectoryProperty
|
||||
|
||||
@get:Inject
|
||||
protected abstract val objectFactory: ObjectFactory
|
||||
|
||||
@get:Inject
|
||||
protected abstract val fsOperations: FileSystemOperations
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val sources = this.sources.get()
|
||||
if (sources.isEmpty()) throw GradleException("Cannot have an empty list of sources")
|
||||
|
||||
val files = mutableMapOf<String, SharedFile>()
|
||||
for (source in sources) {
|
||||
source.input.visit(
|
||||
object : FileVisitor {
|
||||
override fun visitDir(dirDetails: FileVisitDetails) = Unit
|
||||
override fun visitFile(fileDetails: FileVisitDetails) {
|
||||
val path = fileDetails.file.toRelativeString(source.input.dir)
|
||||
val hash = MoreFiles.computeSha1(fileDetails.file.toPath())
|
||||
|
||||
val existing = files[path]
|
||||
if (existing == null) {
|
||||
files[path] = SharedFile(hash, 1)
|
||||
} else if (existing.hash == hash) {
|
||||
existing.found++
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
|
||||
|
||||
// Copy shared files to the common directory
|
||||
fsOperations.sync {
|
||||
from(sources[0].input)
|
||||
into(output)
|
||||
include(sharedFiles)
|
||||
}
|
||||
|
||||
// And all other files to their per-source directory
|
||||
for (source in sources) {
|
||||
fsOperations.sync {
|
||||
from(source.input)
|
||||
into(source.output)
|
||||
exclude(sharedFiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SharedFile(val hash: String, var found: Int)
|
||||
}
|
@@ -46,7 +46,7 @@ abstract class NpmInstall : DefaultTask() {
|
||||
@TaskAction
|
||||
fun install() {
|
||||
project.exec {
|
||||
commandLine("npm", "ci")
|
||||
commandLine(ProcessHelpers.getExecutable("npm"), "ci")
|
||||
workingDir = projectRoot.get().asFile
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,6 @@ abstract class NpmInstall : DefaultTask() {
|
||||
abstract class NpxExecToDir : ExecToDir() {
|
||||
init {
|
||||
dependsOn(NpmInstall.TASK_NAME)
|
||||
executable = "npx"
|
||||
executable = ProcessHelpers.getExecutable("npx")
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import org.gradle.api.GradleException
|
||||
import java.io.BufferedReader
|
||||
import java.io.File
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
internal object ProcessHelpers {
|
||||
fun startProcess(vararg command: String): Process {
|
||||
@@ -34,7 +35,7 @@ internal object ProcessHelpers {
|
||||
val process = startProcess(*command)
|
||||
process.outputStream.close()
|
||||
|
||||
val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
|
||||
val out = BufferedReader(InputStreamReader(process.inputStream, StandardCharsets.UTF_8)).use { reader ->
|
||||
reader.lines().filter { it.isNotEmpty() }.toList()
|
||||
}
|
||||
ProcessGroovyMethods.closeStreams(process)
|
||||
@@ -46,6 +47,28 @@ internal object ProcessHelpers {
|
||||
val path = System.getenv("PATH") ?: return false
|
||||
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for an executable on the `PATH` if required.
|
||||
*
|
||||
* [Process]/[ProcessBuilder] does not handle all executable file extensions on Windows (such as `.com). When on
|
||||
* Windows, this function searches `PATH` and `PATHEXT` for an executable matching [name].
|
||||
*/
|
||||
fun getExecutable(name: String): String {
|
||||
if (!System.getProperty("os.name").lowercase().contains("windows")) return name
|
||||
|
||||
val path = (System.getenv("PATH") ?: return name).split(File.pathSeparator)
|
||||
val pathExt = (System.getenv("PATHEXT") ?: return name).split(File.pathSeparator)
|
||||
|
||||
for (pathEntry in path) {
|
||||
for (ext in pathExt) {
|
||||
val resolved = File(pathEntry, name + ext)
|
||||
if (resolved.exists()) return resolved.getAbsolutePath()
|
||||
}
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Process.waitForOrThrow(message: String) {
|
||||
|
@@ -13,8 +13,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<property name="tabWidth" value="4"/>
|
||||
<property name="charset" value="UTF-8" />
|
||||
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||
</module>
|
||||
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${config_loc}/suppressions.xml" />
|
||||
<property name="file" value="${config_loc}/suppressions.xml" />
|
||||
</module>
|
||||
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
|
@@ -21,5 +21,5 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<suppress checks="PackageName" files=".*[\\/]T[A-Za-z]+.java" />
|
||||
|
||||
<!-- Allow underscores in our test classes. -->
|
||||
<suppress checks="MethodName" files=".*Contract.java" />
|
||||
<suppress checks="MethodName" files=".*(Contract|Test).java" />
|
||||
</suppressions>
|
||||
|
@@ -1,12 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
FROM gitpod/workspace-base
|
||||
|
||||
USER gitpod
|
||||
|
||||
RUN sudo apt-get -q update \
|
||||
&& sudo apt-get install -yq openjdk-16-jdk python3-pip npm \
|
||||
&& sudo pip3 install pre-commit \
|
||||
&& sudo update-java-alternatives --set java-1.16.0-openjdk-amd64
|
@@ -19,7 +19,7 @@ In order to give the best results, a GPS constellation needs at least four compu
|
||||
constellation is redundant, but it does not cause problems.
|
||||
|
||||
## Building a GPS constellation
|
||||
<img alt="An example GPS constellation." src="/images/gps-constellation-example.png" class="big-image" />
|
||||
<img alt="An example GPS constellation." src="../images/gps-constellation-example.png" class="big-image" />
|
||||
|
||||
We are going to build our GPS constellation as shown in the image above. You will need 4 computers and either 4 wireless
|
||||
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
||||
|
@@ -131,7 +131,7 @@ different.
|
||||
First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
|
||||
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
|
||||
|
||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
|
||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPWM uses a single bit for each
|
||||
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
|
||||
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
|
||||
[`fs.ReadHandle.read`] if you prefer.
|
||||
|
BIN
doc/images/computercraft-dump.png
Normal file
BIN
doc/images/computercraft-dump.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 254 KiB |
BIN
doc/images/computercraft-track.png
Normal file
BIN
doc/images/computercraft-track.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 304 KiB |
140
doc/reference/command.md
Normal file
140
doc/reference/command.md
Normal file
@@ -0,0 +1,140 @@
|
||||
---
|
||||
module: [kind=reference] computercraft_command
|
||||
---
|
||||
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
# The `/computercraft` command
|
||||
CC: Tweaked provides a `/computercraft` command for server owners to manage running computers on a server.
|
||||
|
||||
## Permissions {#permissions}
|
||||
As the `/computercraft` command is mostly intended for debugging and administrative purposes, its sub-commands typically
|
||||
require you to have op (or similar).
|
||||
|
||||
- All players have access to the [`queue`] sub-command.
|
||||
- On a multi-player server, all other commands require op.
|
||||
- On a single-player world, the player can run the [`dump`], [`turn-on`]/[`shutdown`], and [`track`] sub-commands, even
|
||||
when cheats are not enabled. The [`tp`] and [`view`] commands require cheats.
|
||||
|
||||
If a permission mod such as [LuckPerms] is installed[^permission], you can configure access to the individual
|
||||
sub-commands. Each sub-command creates a `computercraft.command.NAME` permission node to control which players can
|
||||
execute it.
|
||||
|
||||
[LuckPerms]: https://github.com/LuckPerms/LuckPerms/ "A permissions plugin for Minecraft servers."
|
||||
[fabric-permission-api]: https://github.com/lucko/fabric-permissions-api "A simple permissions API for Fabric"
|
||||
|
||||
[^permission]: This supports any mod which uses Forge's permission API or [fabric-permission-api].
|
||||
|
||||
## Computer selectors {#computer-selectors}
|
||||
Some commands (such as [`tp`] or [`turn-on`]) target a specific computer, or a list of computers. To specify which
|
||||
computers to operate on, you must use "computer selectors".
|
||||
|
||||
Computer selectors are similar to Minecraft's [entity target selectors], but targeting computers instead. They allow
|
||||
you to select one or more computers, based on a set of predicates.
|
||||
|
||||
The following predicates are supported:
|
||||
- `id=<id>`: Select computer(s) with a specific id.
|
||||
- `instance=<id>`: Select the computer with the given instance id.
|
||||
- `family=<normal|advanced|command>`: Select computers based on their type.
|
||||
- `label=<label>`: Select computers with the given label.
|
||||
- `distance=<distance>`: Select computers within a specific distance of the player executing the command. This uses
|
||||
Minecraft's [float range] syntax.
|
||||
|
||||
`#<id>` may also be used as a shorthand for `@c[id=<id>]`, to select computer(s) with a specific id.
|
||||
|
||||
### Examples:
|
||||
- `/computercraft turn-on #12`: Turn on the computer(s) with an id of 12.
|
||||
- `/computercraft shutdown @c[distance=..100]`: Shut down all computers with 100 blocks of the player.
|
||||
|
||||
[entity target selectors]: https://minecraft.wiki/w/Target_selectors "Target Selectors on the Minecraft wiki"
|
||||
[Float range]: https://minecraft.wiki/w/Argument_types#minecraft:float_range
|
||||
|
||||
## Commands {#commands}
|
||||
### `/computercraft dump` {#dump}
|
||||
`/computercraft dump` prints a table of currently loaded computers, including their id, position, and whether they're
|
||||
running. It can also be run with a single computer argument to dump more detailed information about a computer.
|
||||
|
||||

|
||||
|
||||
Next to the computer id, there are several buttons to either [teleport][`tp`] to the computer, or [open its terminal
|
||||
][`view`].
|
||||
|
||||
Computers are sorted by distance to the player, so nearby computers will appear earlier.
|
||||
|
||||
### `/computercraft turn-on [computers...]` {#turn-on}
|
||||
Turn on one or more computers or, if no run with no arguments, all loaded computers.
|
||||
|
||||
#### Examples
|
||||
- `/computercraft turn-on #0 #2`: Turn on computers with id 0 and 2.
|
||||
- `/computercraft turn-on @c[family=command]`: Turn on all command computers.
|
||||
|
||||
### `/computercraft shutdown [computers...]` {#shutdown}
|
||||
Shutdown one or more computers or, if no run with no arguments, all loaded computers.
|
||||
|
||||
This is sometimes useful when dealing with lag, as a way to ensure that ComputerCraft is not causing problems.
|
||||
|
||||
#### Examples
|
||||
- `/computercraft shutdown`: Shut down all loaded computers.
|
||||
- `/computercraft shutdown @c[distance=..10]`: Shut down all computers in a block radius.
|
||||
|
||||
### `/computercraft tp [computer]` {#tp}
|
||||
Teleport to the given computer.
|
||||
|
||||
This is normally used from via the [`dump`] command interface rather than being invoked directly.
|
||||
|
||||
### `/computercraft view [computer]` {#view}
|
||||
Open a terminal for the specified computer. This allows remotely viewing computers without having to interact with the
|
||||
block.
|
||||
|
||||
This is normally used from via the [`dump`] command interface rather than being invoked directly.
|
||||
|
||||
### `/computercraft track` {#track}
|
||||
The `/computercraft track` command allows you to enable profiling of computers. When a computer runs code, or interacts
|
||||
with the Minecraft world, we time how long that takes. This timing information may then be queried, and used to find
|
||||
computers which may be causing lag.
|
||||
|
||||
To enable the profiler, run `/computercraft track start`. Computers will then start recording metrics. Once enough data
|
||||
has been gathered, run `/computercraft track stop` to stop profiling and display the recorded data.
|
||||
|
||||

|
||||
|
||||
The table by default shows the number of times each computer has run, and how long it ran for (in total, and on
|
||||
average). In the above screenshot, we can see one computer was particularly badly behaved, and ran for 7 seconds. The
|
||||
buttons may be used to [teleport][`tp`] to the computer, or [open its terminal ][`view`], and inspect it further.
|
||||
|
||||
`/computercraft track dump` can be used to display this table at any point (including while profiling is still running).
|
||||
|
||||
Computers also record other information, such as how much server-thread time they consume, or their HTTP bandwidth
|
||||
usage. The `dump` subcommand accepts a list of other fields to display, instead of the default timings.
|
||||
|
||||
#### Examples
|
||||
- `/computercraft track dump server_tasks_count server_tasks`: Print the number of server-thread tasks each computer
|
||||
executed, and how long they took in total.
|
||||
- `/computercraft track dump http_upload http_download`: Print the number of bytes uploaded and downloaded by each
|
||||
computer.
|
||||
|
||||
|
||||
### `/computercraft queue` {#queue}
|
||||
The queue subcommand allows non-operator players to queue a `computer_command` event on *command* computers.
|
||||
|
||||
This has a similar purpose to vanilla's [`/trigger`] command. Command computers may choose to listen to this event, and
|
||||
then perform some action.
|
||||
|
||||
[`/trigger`]: https://minecraft.wiki/w/Commands/trigger "/trigger on the Minecraft wiki"
|
||||
|
||||
|
||||
[`dump`]: #dump "/computercraft dump"
|
||||
[`queue`]: #queue "/computercraft queue"
|
||||
[`shutdown`]: #shutdown "/computercraft shutdown"
|
||||
[`tp`]: #tp "/computercraft tp"
|
||||
[`track`]: #track "/computercraft track"
|
||||
[`turn-on`]: #turn-on "/computercraft turn-on"
|
||||
[`view`]: #view "/computercraft view"
|
||||
[computer selectors]: #computer-selectors "Computer selectors"
|
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.jvmargs=-Xmx3G -Dfile.encoding=UTF-8
|
||||
org.gradle.parallel=true
|
||||
|
||||
kotlin.stdlib.default.dependency=false
|
||||
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
isUnstable=false
|
||||
modVersion=1.109.5
|
||||
modVersion=1.111.0
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.20.1
|
||||
|
@@ -26,7 +26,7 @@ slf4j = "2.0.1"
|
||||
asm = "9.6"
|
||||
autoService = "1.1.1"
|
||||
checkerFramework = "3.42.0"
|
||||
cobalt = "0.9.0"
|
||||
cobalt = "0.9.3"
|
||||
commonsCli = "1.6.0"
|
||||
jetbrainsAnnotations = "24.1.0"
|
||||
jsr305 = "3.0.2"
|
||||
@@ -51,28 +51,28 @@ sodium = "mc1.20-0.4.10"
|
||||
hamcrest = "2.2"
|
||||
jqwik = "1.8.2"
|
||||
junit = "5.10.1"
|
||||
jmh = "1.37"
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = "1.8.2"
|
||||
checkstyle = "10.12.6"
|
||||
checkstyle = "10.14.1"
|
||||
curseForgeGradle = "1.0.14"
|
||||
errorProne-core = "2.23.0"
|
||||
errorProne-core = "2.27.0"
|
||||
errorProne-plugin = "3.1.0"
|
||||
fabric-loom = "1.5.7"
|
||||
forgeGradle = "6.0.20"
|
||||
fabric-loom = "1.6.7"
|
||||
forgeGradle = "6.0.21"
|
||||
githubRelease = "2.5.2"
|
||||
gradleVersions = "0.50.0"
|
||||
ideaExt = "1.1.7"
|
||||
illuaminate = "0.1.0-44-g9ee0055"
|
||||
illuaminate = "0.1.0-71-g378d86e"
|
||||
librarian = "1.+"
|
||||
lwjgl = "3.3.3"
|
||||
minotaur = "2.+"
|
||||
mixinGradle = "0.7.38"
|
||||
nullAway = "0.9.9"
|
||||
nullAway = "0.10.25"
|
||||
spotless = "6.23.3"
|
||||
taskTree = "2.1.1"
|
||||
teavm = "0.10.0-SQUID.2"
|
||||
vanillaExtract = "0.1.1"
|
||||
teavm = "0.10.0-SQUID.4"
|
||||
vanillaExtract = "0.1.3"
|
||||
versionCatalogUpdate = "0.8.1"
|
||||
|
||||
[libraries]
|
||||
@@ -127,6 +127,8 @@ junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.re
|
||||
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
||||
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
||||
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||
jmh = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" }
|
||||
jmh-processor = { module = "org.openjdk.jmh:jmh-generator-annprocess", version.ref = "jmh" }
|
||||
|
||||
# LWJGL
|
||||
lwjgl-bom = { module = "org.lwjgl:lwjgl-bom", version.ref = "lwjgl" }
|
||||
@@ -152,6 +154,7 @@ minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur"
|
||||
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
||||
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
|
||||
teavm-core = { module = "org.teavm:teavm-core", version.ref = "teavm" }
|
||||
teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" }
|
||||
teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" }
|
||||
teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" }
|
||||
@@ -168,7 +171,6 @@ githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "g
|
||||
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
||||
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
@@ -105,6 +105,10 @@
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua)
|
||||
(linters -var:deprecated))
|
||||
|
||||
;; Suppress unused variable warnings in the parser.
|
||||
(at /projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/cc/internal/syntax/parser.lua
|
||||
(linters -var:unused))
|
||||
|
||||
(at /projects/core/src/test/resources/test-rom
|
||||
; We should still be able to test deprecated members.
|
||||
(linters -var:deprecated)
|
||||
|
@@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -22,6 +23,7 @@ import java.util.Map;
|
||||
*
|
||||
* @see WiredNode#getNetwork()
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface WiredNetwork {
|
||||
/**
|
||||
* Create a connection between two nodes.
|
||||
@@ -35,7 +37,9 @@ public interface WiredNetwork {
|
||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
||||
* @see WiredNode#connectTo(WiredNode)
|
||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
||||
* @deprecated Use {@link WiredNode#connectTo(WiredNode)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean connect(WiredNode left, WiredNode right);
|
||||
|
||||
/**
|
||||
@@ -50,7 +54,9 @@ public interface WiredNetwork {
|
||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
||||
* @see WiredNode#disconnectFrom(WiredNode)
|
||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
||||
* @deprecated Use {@link WiredNode#disconnectFrom(WiredNode)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean disconnect(WiredNode left, WiredNode right);
|
||||
|
||||
/**
|
||||
@@ -64,7 +70,9 @@ public interface WiredNetwork {
|
||||
* only element.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see WiredNode#remove()
|
||||
* @deprecated Use {@link WiredNode#remove()}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean remove(WiredNode node);
|
||||
|
||||
/**
|
||||
@@ -77,6 +85,8 @@ public interface WiredNetwork {
|
||||
* @param peripherals The new peripherals for this node.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see WiredNode#updatePeripherals(Map)
|
||||
* @deprecated Use {@link WiredNode#updatePeripherals(Map)}
|
||||
*/
|
||||
@Deprecated
|
||||
void updatePeripherals(WiredNode node, Map<String, IPeripheral> peripherals);
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.network.PacketNetwork;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -22,6 +23,7 @@ import java.util.Map;
|
||||
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever
|
||||
* be used on the main server thread.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface WiredNode extends PacketNetwork {
|
||||
/**
|
||||
* The associated element for this network node.
|
||||
@@ -37,7 +39,9 @@ public interface WiredNode extends PacketNetwork {
|
||||
* This should only be used on the server thread.
|
||||
*
|
||||
* @return This node's network.
|
||||
* @deprecated Use the connect/disconnect/remove methods on {@link WiredNode}.
|
||||
*/
|
||||
@Deprecated
|
||||
WiredNetwork getNetwork();
|
||||
|
||||
/**
|
||||
@@ -47,12 +51,9 @@ public interface WiredNode extends PacketNetwork {
|
||||
*
|
||||
* @param node The other node to connect to.
|
||||
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
||||
* @see WiredNode#disconnectFrom(WiredNode)
|
||||
*/
|
||||
default boolean connectTo(WiredNode node) {
|
||||
return getNetwork().connect(this, node);
|
||||
}
|
||||
boolean connectTo(WiredNode node);
|
||||
|
||||
/**
|
||||
* Destroy a connection between this node and another.
|
||||
@@ -61,13 +62,9 @@ public interface WiredNode extends PacketNetwork {
|
||||
*
|
||||
* @param node The other node to disconnect from.
|
||||
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
|
||||
* @throws IllegalArgumentException If {@code node} is not on the same network.
|
||||
* @see WiredNetwork#disconnect(WiredNode, WiredNode)
|
||||
* @see WiredNode#connectTo(WiredNode)
|
||||
*/
|
||||
default boolean disconnectFrom(WiredNode node) {
|
||||
return getNetwork().disconnect(this, node);
|
||||
}
|
||||
boolean disconnectFrom(WiredNode node);
|
||||
|
||||
/**
|
||||
* Sever all connections this node has, removing it from this network.
|
||||
@@ -78,11 +75,8 @@ public interface WiredNode extends PacketNetwork {
|
||||
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
||||
* only element.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see WiredNetwork#remove(WiredNode)
|
||||
*/
|
||||
default boolean remove() {
|
||||
return getNetwork().remove(this);
|
||||
}
|
||||
boolean remove();
|
||||
|
||||
/**
|
||||
* Mark this node's peripherals as having changed.
|
||||
@@ -91,9 +85,6 @@ public interface WiredNode extends PacketNetwork {
|
||||
* that your network element owns.
|
||||
*
|
||||
* @param peripherals The new peripherals for this node.
|
||||
* @see WiredNetwork#updatePeripherals(WiredNode, Map)
|
||||
*/
|
||||
default void updatePeripherals(Map<String, IPeripheral> peripherals) {
|
||||
getNetwork().updatePeripherals(this, peripherals);
|
||||
}
|
||||
void updatePeripherals(Map<String, IPeripheral> peripherals);
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ public final class Services {
|
||||
* @throws IllegalStateException When the service cannot be loaded.
|
||||
*/
|
||||
public static <T> T load(Class<T> klass) {
|
||||
var services = ServiceLoader.load(klass).stream().toList();
|
||||
var services = ServiceLoader.load(klass, klass.getClassLoader()).stream().toList();
|
||||
return switch (services.size()) {
|
||||
case 1 -> services.get(0).get();
|
||||
case 0 -> throw new IllegalStateException("Cannot find service for " + klass.getName());
|
||||
|
@@ -11,6 +11,12 @@ plugins {
|
||||
id("cc-tweaked.publishing")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources.srcDir("src/generated/resources")
|
||||
}
|
||||
}
|
||||
|
||||
minecraft {
|
||||
accessWideners(
|
||||
"src/main/resources/computercraft.accesswidener",
|
||||
@@ -46,6 +52,9 @@ dependencies {
|
||||
testImplementation(libs.bundles.test)
|
||||
testRuntimeOnly(libs.bundles.testRuntime)
|
||||
|
||||
testImplementation(libs.jmh)
|
||||
testAnnotationProcessor(libs.jmh.processor)
|
||||
|
||||
testModCompileOnly(libs.mixin)
|
||||
testModImplementation(testFixtures(project(":core")))
|
||||
testModImplementation(testFixtures(project(":common")))
|
||||
@@ -102,3 +111,21 @@ val lintLua by tasks.registering(IlluaminateExec::class) {
|
||||
doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
|
||||
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
|
||||
}
|
||||
|
||||
val runData by tasks.registering(MergeTrees::class) {
|
||||
output = layout.projectDirectory.dir("src/generated/resources")
|
||||
|
||||
for (loader in listOf("forge", "fabric")) {
|
||||
mustRunAfter(":$loader:runData")
|
||||
source {
|
||||
input {
|
||||
from(project(":$loader").layout.buildDirectory.dir("generatedResources"))
|
||||
exclude(".cache")
|
||||
}
|
||||
|
||||
output = project(":$loader").layout.projectDirectory.dir("src/generated/resources")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }
|
||||
|
@@ -22,6 +22,7 @@ import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||
@@ -77,8 +78,10 @@ public final class ClientRegistry {
|
||||
|
||||
/**
|
||||
* Register any client-side objects which must be done on the main thread.
|
||||
*
|
||||
* @param itemProperties Callback to register item properties.
|
||||
*/
|
||||
public static void registerMainThread() {
|
||||
public static void registerMainThread(RegisterItemProperty itemProperties) {
|
||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
|
||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
|
||||
MenuScreens.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
|
||||
@@ -90,11 +93,14 @@ public final class ClientRegistry {
|
||||
|
||||
MenuScreens.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
|
||||
|
||||
registerItemProperty("state",
|
||||
new UnclampedPropertyFunction((stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal()),
|
||||
registerItemProperty(itemProperties, "state",
|
||||
new UnclampedPropertyFunction((stack, world, player, random) -> {
|
||||
var computer = ClientPocketComputers.get(stack);
|
||||
return (computer == null ? ComputerState.OFF : computer.getState()).ordinal();
|
||||
}),
|
||||
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||
);
|
||||
registerItemProperty("coloured",
|
||||
registerItemProperty(itemProperties, "coloured",
|
||||
(stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
|
||||
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||
);
|
||||
@@ -115,9 +121,17 @@ public final class ClientRegistry {
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static void registerItemProperty(String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||
private static void registerItemProperty(RegisterItemProperty itemProperties, String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
||||
for (var item : items) ItemProperties.register(item.get(), id, getter);
|
||||
for (var item : items) itemProperties.register(item.get(), id, getter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an item property via {@link ItemProperties#register}. Forge and Fabric expose different methods, so we
|
||||
* supply this via mod-loader-specific code.
|
||||
*/
|
||||
public interface RegisterItemProperty {
|
||||
void register(Item item, ResourceLocation name, ClampedItemPropertyFunction property);
|
||||
}
|
||||
|
||||
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
|
||||
@@ -155,17 +169,14 @@ public final class ClientRegistry {
|
||||
}
|
||||
|
||||
private static int getPocketColour(ItemStack stack, int layer) {
|
||||
switch (layer) {
|
||||
case 0:
|
||||
default:
|
||||
return 0xFFFFFF;
|
||||
case 1: // Frame colour
|
||||
return IColouredItem.getColourBasic(stack);
|
||||
case 2: { // Light colour
|
||||
var light = ClientPocketComputers.get(stack).getLightState();
|
||||
return light == -1 ? Colour.BLACK.getHex() : light;
|
||||
return switch (layer) {
|
||||
default -> 0xFFFFFF;
|
||||
case 1 -> IColouredItem.getColourBasic(stack); // Frame colour
|
||||
case 2 -> { // Light colour
|
||||
var computer = ClientPocketComputers.get(stack);
|
||||
yield computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static int getTurtleColour(ItemStack stack, int layer) {
|
||||
|
@@ -9,6 +9,7 @@ import dan200.computercraft.client.gui.widgets.DynamicImageButton;
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
import dan200.computercraft.client.network.ClientNetworking;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
@@ -18,6 +19,7 @@ import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.network.server.UploadFileMessage;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
@@ -96,8 +98,8 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
getTerminal().update();
|
||||
|
||||
if (uploadNagDeadline != Long.MAX_VALUE && Util.getNanos() >= uploadNagDeadline) {
|
||||
new ItemToast(minecraft, displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
|
||||
.showOrReplace(minecraft.getToasts());
|
||||
new ItemToast(minecraft(), displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
|
||||
.showOrReplace(minecraft().getToasts());
|
||||
uploadNagDeadline = Long.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
@@ -207,7 +209,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
return;
|
||||
}
|
||||
|
||||
if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
|
||||
if (!toUpload.isEmpty()) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
|
||||
}
|
||||
|
||||
public void uploadResult(UploadResult result, @Nullable Component message) {
|
||||
@@ -223,9 +225,13 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
}
|
||||
|
||||
private void alert(Component title, Component message) {
|
||||
OptionScreen.show(minecraft, title, message,
|
||||
List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
|
||||
() -> minecraft.setScreen(this)
|
||||
OptionScreen.show(minecraft(), title, message,
|
||||
List.of(OptionScreen.newButton(OK, b -> minecraft().setScreen(this))),
|
||||
() -> minecraft().setScreen(this)
|
||||
);
|
||||
}
|
||||
|
||||
private Minecraft minecraft() {
|
||||
return Nullability.assertNonNull(minecraft);
|
||||
}
|
||||
}
|
||||
|
@@ -49,31 +49,31 @@ public final class ClientInputHandler implements InputHandler {
|
||||
|
||||
@Override
|
||||
public void keyDown(int key, boolean repeat) {
|
||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key));
|
||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.Action.REPEAT : KeyEventServerMessage.Action.DOWN, key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyUp(int key) {
|
||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key));
|
||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.Action.UP, key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClick(int button, int x, int y) {
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.CLICK, button, x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseUp(int button, int x, int y) {
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.UP, button, x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDrag(int button, int x, int y) {
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.DRAG, button, x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseScroll(int direction, int x, int y) {
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y));
|
||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.SCROLL, direction, x, y));
|
||||
}
|
||||
}
|
||||
|
@@ -6,8 +6,10 @@ package dan200.computercraft.client.gui;
|
||||
|
||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
||||
@@ -16,6 +18,7 @@ import net.minecraft.world.entity.player.Inventory;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||
|
||||
@@ -44,8 +47,8 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
||||
protected void init() {
|
||||
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
|
||||
// grabbing unsets.
|
||||
minecraft.mouseHandler.grabMouse();
|
||||
minecraft.screen = this;
|
||||
minecraft().mouseHandler.grabMouse();
|
||||
minecraft().screen = this;
|
||||
KeyMapping.releaseAll();
|
||||
|
||||
super.init();
|
||||
@@ -64,13 +67,13 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) {
|
||||
minecraft.player.getInventory().swapPaint(pDelta);
|
||||
Objects.requireNonNull(minecraft().player).getInventory().swapPaint(pDelta);
|
||||
return super.mouseScrolled(pMouseX, pMouseY, pDelta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
minecraft.player.closeContainer();
|
||||
Objects.requireNonNull(minecraft().player).closeContainer();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
@@ -93,12 +96,16 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||
|
||||
var font = minecraft.font;
|
||||
var font = minecraft().font;
|
||||
var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8));
|
||||
var y = 10;
|
||||
for (var line : lines) {
|
||||
graphics.drawString(font, line, (width / 2) - (minecraft.font.width(line) / 2), y, 0xFFFFFF, true);
|
||||
graphics.drawString(font, line, (width / 2) - (font.width(line) / 2), y, 0xFFFFFF, true);
|
||||
y += 9;
|
||||
}
|
||||
}
|
||||
|
||||
private Minecraft minecraft() {
|
||||
return Nullability.assertNonNull(minecraft);
|
||||
}
|
||||
}
|
||||
|
@@ -17,6 +17,7 @@ import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -27,7 +28,6 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -49,7 +49,7 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMonitorData(BlockPos pos, TerminalState terminal) {
|
||||
public void handleMonitorData(BlockPos pos, @Nullable TerminalState terminal) {
|
||||
var player = Minecraft.getInstance().player;
|
||||
if (player == null) return;
|
||||
|
||||
@@ -67,19 +67,17 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePocketComputerData(int instanceId, ComputerState state, int lightState, TerminalState terminal) {
|
||||
var computer = ClientPocketComputers.get(instanceId, terminal.colour);
|
||||
computer.setState(state, lightState);
|
||||
if (terminal.hasTerminal()) computer.setTerminal(terminal);
|
||||
public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, @Nullable TerminalState terminal) {
|
||||
ClientPocketComputers.setState(instanceId, state, lightState, terminal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePocketComputerDeleted(int instanceId) {
|
||||
public void handlePocketComputerDeleted(UUID instanceId) {
|
||||
ClientPocketComputers.remove(instanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, ByteBuffer buffer) {
|
||||
public void handleSpeakerAudio(UUID source, SpeakerPosition.Message position, float volume, EncodedAudio buffer) {
|
||||
SpeakerManager.getSound(source).playAudio(reifyPosition(position), volume, buffer);
|
||||
}
|
||||
|
||||
|
@@ -4,21 +4,25 @@
|
||||
|
||||
package dan200.computercraft.client.pocket;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Maps {@link ServerComputer#getInstanceID()} to locals {@link PocketComputerData}.
|
||||
* Maps {@link ServerComputer#getInstanceUUID()} to locals {@link PocketComputerData}.
|
||||
* <p>
|
||||
* This is populated by {@link PocketComputerDataMessage} and accessed when rendering pocket computers
|
||||
*/
|
||||
public final class ClientPocketComputers {
|
||||
private static final Int2ObjectMap<PocketComputerData> instances = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<UUID, PocketComputerData> instances = new HashMap<>();
|
||||
|
||||
private ClientPocketComputers() {
|
||||
}
|
||||
@@ -27,25 +31,29 @@ public final class ClientPocketComputers {
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
public static void remove(int id) {
|
||||
public static void remove(UUID id) {
|
||||
instances.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a pocket computer.
|
||||
* Set the state of a pocket computer.
|
||||
*
|
||||
* @param instanceId The instance ID of the pocket computer.
|
||||
* @param advanced Whether this computer has an advanced terminal.
|
||||
* @return The pocket computer data.
|
||||
* @param instanceId The instance ID of the pocket computer.
|
||||
* @param state The computer state of the pocket computer.
|
||||
* @param lightColour The current colour of the modem light.
|
||||
* @param terminalData The current terminal contents.
|
||||
*/
|
||||
public static PocketComputerData get(int instanceId, boolean advanced) {
|
||||
public static void setState(UUID instanceId, ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
|
||||
var computer = instances.get(instanceId);
|
||||
if (computer == null) instances.put(instanceId, computer = new PocketComputerData(advanced));
|
||||
return computer;
|
||||
if (computer == null) {
|
||||
instances.put(instanceId, new PocketComputerData(state, lightColour, terminalData));
|
||||
} else {
|
||||
computer.setState(state, lightColour, terminalData);
|
||||
}
|
||||
}
|
||||
|
||||
public static PocketComputerData get(ItemStack stack) {
|
||||
var family = stack.getItem() instanceof PocketComputerItem computer ? computer.getFamily() : ComputerFamily.NORMAL;
|
||||
return get(PocketComputerItem.getInstanceID(stack), family != ComputerFamily.NORMAL);
|
||||
public static @Nullable PocketComputerData get(ItemStack stack) {
|
||||
var id = PocketComputerItem.getInstanceID(stack);
|
||||
return id == null ? null : instances.get(id);
|
||||
}
|
||||
}
|
||||
|
@@ -4,13 +4,13 @@
|
||||
|
||||
package dan200.computercraft.client.pocket;
|
||||
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Clientside data about a pocket computer.
|
||||
* <p>
|
||||
@@ -21,20 +21,22 @@ import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
* @see ClientPocketComputers The registry which holds pocket computers.
|
||||
* @see PocketServerComputer The server-side pocket computer.
|
||||
*/
|
||||
public class PocketComputerData {
|
||||
private final NetworkedTerminal terminal;
|
||||
private ComputerState state = ComputerState.OFF;
|
||||
private int lightColour = -1;
|
||||
public final class PocketComputerData {
|
||||
private @Nullable NetworkedTerminal terminal;
|
||||
private ComputerState state;
|
||||
private int lightColour;
|
||||
|
||||
public PocketComputerData(boolean colour) {
|
||||
terminal = new NetworkedTerminal(Config.pocketTermWidth, Config.pocketTermHeight, colour);
|
||||
PocketComputerData(ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
|
||||
this.state = state;
|
||||
this.lightColour = lightColour;
|
||||
if (terminalData != null) terminal = terminalData.create();
|
||||
}
|
||||
|
||||
public int getLightState() {
|
||||
return state != ComputerState.OFF ? lightColour : -1;
|
||||
}
|
||||
|
||||
public Terminal getTerminal() {
|
||||
public @Nullable NetworkedTerminal getTerminal() {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
@@ -42,12 +44,16 @@ public class PocketComputerData {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(ComputerState state, int lightColour) {
|
||||
void setState(ComputerState state, int lightColour, @Nullable TerminalState terminalData) {
|
||||
this.state = state;
|
||||
this.lightColour = lightColour;
|
||||
}
|
||||
|
||||
public void setTerminal(TerminalState state) {
|
||||
state.apply(terminal);
|
||||
if (terminalData != null) {
|
||||
if (terminal == null) {
|
||||
terminal = terminalData.create();
|
||||
} else {
|
||||
terminalData.apply(terminal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,6 +17,8 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A base class for items which have map-like rendering when held in the hand.
|
||||
*
|
||||
@@ -35,7 +37,7 @@ public abstract class ItemMapLikeRenderer {
|
||||
protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light);
|
||||
|
||||
public void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
|
||||
Player player = Minecraft.getInstance().player;
|
||||
Player player = Objects.requireNonNull(Minecraft.getInstance().player);
|
||||
|
||||
transform.pushPose();
|
||||
if (hand == InteractionHand.MAIN_HAND && player.getOffhandItem().isEmpty()) {
|
||||
|
@@ -11,6 +11,7 @@ import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -32,10 +33,16 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
@Override
|
||||
protected void renderItem(PoseStack transform, MultiBufferSource bufferSource, ItemStack stack, int light) {
|
||||
var computer = ClientPocketComputers.get(stack);
|
||||
var terminal = computer.getTerminal();
|
||||
var terminal = computer == null ? null : computer.getTerminal();
|
||||
|
||||
var termWidth = terminal.getWidth();
|
||||
var termHeight = terminal.getHeight();
|
||||
int termWidth, termHeight;
|
||||
if (terminal == null) {
|
||||
termWidth = Config.pocketTermWidth;
|
||||
termHeight = Config.pocketTermHeight;
|
||||
} else {
|
||||
termWidth = terminal.getWidth();
|
||||
termHeight = terminal.getHeight();
|
||||
}
|
||||
|
||||
var width = termWidth * FONT_WIDTH + MARGIN * 2;
|
||||
var height = termHeight * FONT_HEIGHT + MARGIN * 2;
|
||||
@@ -60,14 +67,15 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||
renderFrame(matrix, bufferSource, family, frameColour, light, width, height);
|
||||
|
||||
// Render the light
|
||||
var lightColour = ClientPocketComputers.get(stack).getLightState();
|
||||
if (lightColour == -1) lightColour = Colour.BLACK.getHex();
|
||||
var lightColour = computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState();
|
||||
renderLight(transform, bufferSource, lightColour, width, height);
|
||||
|
||||
FixedWidthFontRenderer.drawTerminal(
|
||||
FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL)),
|
||||
MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN
|
||||
);
|
||||
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(transform, bufferSource.getBuffer(RenderTypes.TERMINAL));
|
||||
if (terminal == null) {
|
||||
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, width, height);
|
||||
} else {
|
||||
FixedWidthFontRenderer.drawTerminal(quadEmitter, MARGIN, MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
|
||||
}
|
||||
|
||||
transform.popPose();
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
// Render the label
|
||||
var label = turtle.getLabel();
|
||||
var hit = renderer.cameraHitResult;
|
||||
if (label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
|
||||
if (label != null && hit != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
|
||||
var mc = Minecraft.getInstance();
|
||||
var font = this.font;
|
||||
|
||||
|
@@ -58,9 +58,9 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
|
||||
@Override
|
||||
public void render(MonitorBlockEntity monitor, float partialTicks, PoseStack transform, MultiBufferSource bufferSource, int lightmapCoord, int overlayLight) {
|
||||
// Render from the origin monitor
|
||||
var originTerminal = monitor.getClientMonitor();
|
||||
|
||||
var originTerminal = monitor.getOriginClientMonitor();
|
||||
if (originTerminal == null) return;
|
||||
|
||||
var origin = originTerminal.getOrigin();
|
||||
var renderState = originTerminal.getRenderState(MonitorRenderState::new);
|
||||
var monitorPos = monitor.getBlockPos();
|
||||
|
@@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.client.sound;
|
||||
|
||||
import com.mojang.blaze3d.audio.Channel;
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.client.sounds.AudioStream;
|
||||
@@ -36,7 +37,7 @@ class DfpwmStream implements AudioStream {
|
||||
/**
|
||||
* The {@link Channel} which this sound is playing on.
|
||||
*
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
|
||||
*/
|
||||
@Nullable
|
||||
Channel channel;
|
||||
@@ -44,21 +45,23 @@ class DfpwmStream implements AudioStream {
|
||||
/**
|
||||
* The underlying {@link SoundEngine} executor.
|
||||
*
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, ByteBuffer)
|
||||
* @see SpeakerInstance#playAudio(SpeakerPosition, float, EncodedAudio)
|
||||
* @see SoundEngine#executor
|
||||
*/
|
||||
@Nullable
|
||||
Executor executor;
|
||||
|
||||
private int charge = 0; // q
|
||||
private int strength = 0; // s
|
||||
private int lowPassCharge;
|
||||
private boolean previousBit = false;
|
||||
|
||||
DfpwmStream() {
|
||||
}
|
||||
|
||||
void push(ByteBuffer input) {
|
||||
void push(EncodedAudio audio) {
|
||||
var charge = audio.charge();
|
||||
var strength = audio.strength();
|
||||
var previousBit = audio.previousBit();
|
||||
var input = audio.audio();
|
||||
|
||||
var readable = input.remaining();
|
||||
var output = ByteBuffer.allocate(readable * 8).order(ByteOrder.nativeOrder());
|
||||
|
||||
|
@@ -6,12 +6,12 @@ package dan200.computercraft.client.sound;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.peripheral.speaker.EncodedAudio;
|
||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* An instance of a speaker, which is either playing a {@link DfpwmStream} stream or a normal sound.
|
||||
@@ -25,7 +25,7 @@ public class SpeakerInstance {
|
||||
SpeakerInstance() {
|
||||
}
|
||||
|
||||
private void pushAudio(ByteBuffer buffer) {
|
||||
private void pushAudio(EncodedAudio buffer) {
|
||||
var sound = this.sound;
|
||||
|
||||
var stream = currentStream;
|
||||
@@ -43,7 +43,7 @@ public class SpeakerInstance {
|
||||
}
|
||||
}
|
||||
|
||||
public void playAudio(SpeakerPosition position, float volume, ByteBuffer buffer) {
|
||||
public void playAudio(SpeakerPosition position, float volume, EncodedAudio buffer) {
|
||||
pushAudio(buffer);
|
||||
|
||||
var soundManager = Minecraft.getInstance().getSoundManager();
|
||||
|
@@ -1,8 +1,14 @@
|
||||
{
|
||||
"argument.computercraft.argument_expected": "Argument expected",
|
||||
"argument.computercraft.computer.distance": "Distance to entity",
|
||||
"argument.computercraft.computer.family": "Computer family",
|
||||
"argument.computercraft.computer.id": "Computer ID",
|
||||
"argument.computercraft.computer.instance": "Unique instance ID",
|
||||
"argument.computercraft.computer.label": "Computer label",
|
||||
"argument.computercraft.computer.many_matching": "Multiple computers matching '%s' (instances %s)",
|
||||
"argument.computercraft.computer.no_matching": "No computers matching '%s'",
|
||||
"argument.computercraft.tracking_field.no_field": "Unknown field '%s'",
|
||||
"argument.computercraft.unknown_computer_family": "Unknown computer family '%s'",
|
||||
"block.computercraft.cable": "Networking Cable",
|
||||
"block.computercraft.computer_advanced": "Advanced Computer",
|
||||
"block.computercraft.computer_command": "Command Computer",
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user