mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-04-12 05:43:20 +00:00
Merge branch 'mc-1.20.x' into mc-1.21.x
Oh, I'm sure I missed something here. This was a nasty merge, has the docs have changed so much in each version.
This commit is contained in:
commit
7337b91692
@ -58,6 +58,7 @@ repos:
|
||||
exclude: |
|
||||
(?x)^(
|
||||
projects/[a-z]+/src/generated|
|
||||
projects/[a-z]+/src/examples/generatedResources|
|
||||
projects/core/src/test/resources/test-rom/data/json-parsing/|
|
||||
.*\.dfpwm
|
||||
)
|
||||
|
@ -22,8 +22,7 @@ If you have a bug, suggestion, or other feedback, the best thing to do is [file
|
||||
use the issue templates - they provide a useful hint on what information to provide.
|
||||
|
||||
## Translations
|
||||
Translations are managed through [CrowdIn], an online interface for managing language strings. Translations may either
|
||||
be contributed there, or directly via a pull request.
|
||||
Translations are managed through [CrowdIn], an online interface for managing language strings.
|
||||
|
||||
## Setting up a development environment
|
||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||
@ -49,9 +48,12 @@ If you want to run CC:T in a normal Minecraft instance, run `./gradlew assemble`
|
||||
`projects/forge/build/libs` (for Forge) or `projects/fabric/build/libs` (for Fabric).
|
||||
|
||||
## Developing CC: Tweaked
|
||||
Before making any major changes to CC: Tweaked, I'd recommend you have a read of the [the architecture
|
||||
document][architecture] first. While it's not a comprehensive document, it gives a good hint of where you should start
|
||||
looking to make your changes. As always, if you're not sure, [do ask the community][community]!
|
||||
Before making any major changes to CC: Tweaked, I'd recommend starting opening an issue or starting a discussion on
|
||||
GitHub first. It's often helpful to discuss features before spending time developing them!
|
||||
|
||||
Once you're ready to start programming, have a read of the [the architecture document][architecture] first. While it's
|
||||
not a comprehensive document, it gives a good hint of where you should start looking to make your changes. As always, if
|
||||
you're not sure, [do ask the community][community]!
|
||||
|
||||
### Testing
|
||||
When making larger changes, it may be useful to write a test to make sure your code works as expected.
|
||||
|
13
README.md
13
README.md
@ -60,19 +60,6 @@ dependencies {
|
||||
}
|
||||
```
|
||||
|
||||
When using ForgeGradle, you may also need to add the following:
|
||||
|
||||
```groovy
|
||||
minecraft {
|
||||
runs {
|
||||
configureEach {
|
||||
property 'mixin.env.remapRefMap', 'true'
|
||||
property 'mixin.env.refMapRemappingFile', "${buildDir}/createSrgToMcp/output.srg"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
||||
subject to change at any point. If you depend on functionality outside the API (or need to mixin to CC:T), please file
|
||||
an issue to let me know!
|
||||
|
18
REUSE.toml
18
REUSE.toml
@ -8,10 +8,10 @@ SPDX-PackageSupplier = "Jonathan Coates <git@squiddev.cc>"
|
||||
SPDX-PackageDownloadLocation = "https://github.com/cc-tweaked/cc-tweaked"
|
||||
|
||||
[[annotations]]
|
||||
# Generated/data files are CC0.
|
||||
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
path = [
|
||||
# Generated/data files are CC0.
|
||||
"gradle/gradle-daemon-jvm.properties",
|
||||
"projects/common/src/main/resources/assets/computercraft/sounds.json",
|
||||
"projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg",
|
||||
@ -20,6 +20,11 @@ path = [
|
||||
"projects/**/src/generated/**",
|
||||
"projects/web/src/htmlTransform/export/index.json",
|
||||
"projects/web/src/htmlTransform/export/items/minecraft/**",
|
||||
# GitHub build scripts are CC0. While we could add a header to each file,
|
||||
# it's unclear if it will break actions or issue templates in some way.
|
||||
".github/**",
|
||||
# Example mod is CC0.
|
||||
"projects/**/src/examples/**"
|
||||
]
|
||||
|
||||
[[annotations]]
|
||||
@ -74,7 +79,7 @@ path = [
|
||||
]
|
||||
|
||||
[[annotations]]
|
||||
# Community-contributed license files
|
||||
# Community-contributed language files
|
||||
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
|
||||
SPDX-License-Identifier = "LicenseRef-CCPL"
|
||||
path = [
|
||||
@ -88,18 +93,11 @@ path = [
|
||||
]
|
||||
|
||||
[[annotations]]
|
||||
# Community-contributed license files
|
||||
# Community-contributed language files
|
||||
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
|
||||
SPDX-License-Identifier = "MPL-2.0"
|
||||
path = "projects/common/src/main/resources/assets/computercraft/lang/**"
|
||||
|
||||
[[annotations]]
|
||||
# GitHub build scripts are CC0. While we could add a header to each file,
|
||||
# it's unclear if it will break actions or issue templates in some way.
|
||||
SPDX-FileCopyrightText = "Jonathan Coates <git@squiddev.cc>"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
path = ".github/**"
|
||||
|
||||
[[annotations]]
|
||||
path = ["gradle/wrapper/**"]
|
||||
SPDX-FileCopyrightText = "Gradle Inc"
|
||||
|
@ -14,14 +14,10 @@ repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
|
||||
maven("https://maven.neoforged.net/releases") {
|
||||
maven("https://maven.neoforged.net") {
|
||||
name = "NeoForge"
|
||||
content {
|
||||
includeGroup("net.minecraftforge")
|
||||
includeGroup("net.neoforged")
|
||||
includeGroup("net.neoforged.gradle")
|
||||
includeModule("codechicken", "DiffPatch")
|
||||
includeModule("net.covers1624", "Quack")
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +44,7 @@ dependencies {
|
||||
implementation(libs.fabric.loom)
|
||||
implementation(libs.ideaExt)
|
||||
implementation(libs.minotaur)
|
||||
implementation(libs.neoGradle.userdev)
|
||||
implementation(libs.modDevGradle)
|
||||
implementation(libs.vanillaExtract)
|
||||
}
|
||||
|
||||
|
@ -11,20 +11,18 @@ import cc.tweaked.gradle.MinecraftConfigurations
|
||||
|
||||
plugins {
|
||||
id("cc-tweaked.java-convention")
|
||||
id("net.neoforged.gradle.userdev")
|
||||
id("net.neoforged.moddev")
|
||||
}
|
||||
|
||||
plugins.apply(CCTweakedPlugin::class.java)
|
||||
|
||||
val mcVersion: String by extra
|
||||
|
||||
minecraft {
|
||||
modIdentifier("computercraft")
|
||||
}
|
||||
neoForge {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
version = libs.findVersion("neoForge").get().toString()
|
||||
|
||||
subsystems {
|
||||
parchment {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
minecraftVersion = libs.findVersion("parchmentMc").get().toString()
|
||||
mappingsVersion = libs.findVersion("parchment").get().toString()
|
||||
}
|
||||
|
@ -99,6 +99,7 @@ sourceSets.all {
|
||||
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
||||
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
|
||||
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
||||
check("InvalidInlineTag", CheckSeverity.OFF) // Triggered by @snippet. Can be removed on Java 21.
|
||||
|
||||
check("NullAway", CheckSeverity.ERROR)
|
||||
option(
|
||||
|
@ -2,15 +2,16 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import cc.tweaked.gradle.clientClasses
|
||||
import cc.tweaked.gradle.commonClasses
|
||||
|
||||
/**
|
||||
* Sets up the configurations for writing game tests.
|
||||
*
|
||||
* See notes in [cc.tweaked.gradle.MinecraftConfigurations] for the general design behind these cursed ideas.
|
||||
*/
|
||||
|
||||
import cc.tweaked.gradle.MinecraftConfigurations
|
||||
import cc.tweaked.gradle.clientClasses
|
||||
import cc.tweaked.gradle.commonClasses
|
||||
|
||||
plugins {
|
||||
id("cc-tweaked.kotlin-convention")
|
||||
id("cc-tweaked.java-convention")
|
||||
@ -19,33 +20,16 @@ plugins {
|
||||
val main = sourceSets["main"]
|
||||
val client = sourceSets["client"]
|
||||
|
||||
// datagen and testMod inherit from the main and client classpath, just so we have access to Minecraft classes.
|
||||
val datagen by sourceSets.creating {
|
||||
compileClasspath += main.compileClasspath + client.compileClasspath
|
||||
runtimeClasspath += main.runtimeClasspath + client.runtimeClasspath
|
||||
}
|
||||
MinecraftConfigurations.createDerivedConfiguration(project, MinecraftConfigurations.DATAGEN)
|
||||
MinecraftConfigurations.createDerivedConfiguration(project, MinecraftConfigurations.EXAMPLES)
|
||||
MinecraftConfigurations.createDerivedConfiguration(project, MinecraftConfigurations.TEST_MOD)
|
||||
|
||||
val testMod by sourceSets.creating {
|
||||
compileClasspath += main.compileClasspath + client.compileClasspath
|
||||
runtimeClasspath += main.runtimeClasspath + client.runtimeClasspath
|
||||
}
|
||||
// Set up generated resources
|
||||
sourceSets.main { resources.srcDir("src/generated/resources") }
|
||||
sourceSets.named("examples") { resources.srcDir("src/examples/generatedResources") }
|
||||
|
||||
val extraConfigurations = listOf(datagen, testMod)
|
||||
|
||||
configurations {
|
||||
for (config in extraConfigurations) {
|
||||
named(config.compileClasspathConfigurationName) { shouldResolveConsistentlyWith(compileClasspath.get()) }
|
||||
named(config.runtimeClasspathConfigurationName) { shouldResolveConsistentlyWith(runtimeClasspath.get()) }
|
||||
}
|
||||
}
|
||||
|
||||
// Like the main test configurations, we're safe to depend on source set outputs.
|
||||
dependencies {
|
||||
for (config in extraConfigurations) {
|
||||
add(config.implementationConfigurationName, main.output)
|
||||
add(config.implementationConfigurationName, client.output)
|
||||
}
|
||||
}
|
||||
// Make sure our examples compile.
|
||||
tasks.check { dependsOn(tasks.named("compileExamplesJava")) }
|
||||
|
||||
// Similar to java-test-fixtures, but tries to avoid putting the obfuscated jar on the classpath.
|
||||
|
||||
|
@ -113,7 +113,7 @@ abstract class CCTweakedExtension(
|
||||
// Pull in sources from the other project.
|
||||
extendSourceSet(otherProject, main)
|
||||
extendSourceSet(otherProject, client)
|
||||
for (sourceSet in listOf("datagen", "testMod", "testFixtures")) {
|
||||
for (sourceSet in listOf(MinecraftConfigurations.DATAGEN, MinecraftConfigurations.EXAMPLES, MinecraftConfigurations.TEST_MOD, "testFixtures")) {
|
||||
otherJava.sourceSets.findByName(sourceSet)?.let { extendSourceSet(otherProject, it) }
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,6 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
||||
private val java = project.extensions.getByType(JavaPluginExtension::class.java)
|
||||
private val sourceSets = java.sourceSets
|
||||
private val configurations = project.configurations
|
||||
private val objects = project.objects
|
||||
|
||||
private val main = sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]
|
||||
private val test = sourceSets[SourceSet.TEST_SOURCE_SET_NAME]
|
||||
@ -37,13 +36,7 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
||||
val client = sourceSets.maybeCreate("client")
|
||||
|
||||
// Ensure the client classpaths behave the same as the main ones.
|
||||
configurations.named(client.compileClasspathConfigurationName) {
|
||||
shouldResolveConsistentlyWith(configurations[main.compileClasspathConfigurationName])
|
||||
}
|
||||
|
||||
configurations.named(client.runtimeClasspathConfigurationName) {
|
||||
shouldResolveConsistentlyWith(configurations[main.runtimeClasspathConfigurationName])
|
||||
}
|
||||
consistentWithMain(client)
|
||||
|
||||
// Set up an API configuration for clients (to ensure it's consistent with the main source set).
|
||||
val clientApi = configurations.maybeCreate(client.apiConfigurationName).apply {
|
||||
@ -85,6 +78,16 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
||||
setupBasic()
|
||||
}
|
||||
|
||||
private fun consistentWithMain(sourceSet: SourceSet) {
|
||||
configurations.named(sourceSet.compileClasspathConfigurationName) {
|
||||
shouldResolveConsistentlyWith(configurations[main.compileClasspathConfigurationName])
|
||||
}
|
||||
|
||||
configurations.named(sourceSet.runtimeClasspathConfigurationName) {
|
||||
shouldResolveConsistentlyWith(configurations[main.runtimeClasspathConfigurationName])
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupBasic() {
|
||||
val client = sourceSets["client"]
|
||||
|
||||
@ -102,7 +105,24 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
||||
project.tasks.named("check") { dependsOn(checkDependencyConsistency) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new configuration that pulls in the main and client classes from the mod.
|
||||
*/
|
||||
private fun createDerivedConfiguration(name: String) {
|
||||
val client = sourceSets["client"]
|
||||
val sourceSet = sourceSets.create(name)
|
||||
sourceSet.compileClasspath += main.compileClasspath + client.compileClasspath
|
||||
sourceSet.runtimeClasspath += main.runtimeClasspath + client.runtimeClasspath
|
||||
consistentWithMain(sourceSet)
|
||||
project.dependencies.add(sourceSet.implementationConfigurationName, main.output)
|
||||
project.dependencies.add(sourceSet.implementationConfigurationName, client.output)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DATAGEN = "datagen"
|
||||
const val EXAMPLES = "examples"
|
||||
const val TEST_MOD = "testMod"
|
||||
|
||||
fun setupBasic(project: Project) {
|
||||
MinecraftConfigurations(project).setupBasic()
|
||||
}
|
||||
@ -110,6 +130,10 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
||||
fun setup(project: Project) {
|
||||
MinecraftConfigurations(project).setup()
|
||||
}
|
||||
|
||||
fun createDerivedConfiguration(project: Project, name: String) {
|
||||
MinecraftConfigurations(project).createDerivedConfiguration(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import net.neoforged.moddevgradle.internal.RunGameTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.invocation.Gradle
|
||||
@ -65,6 +66,22 @@ abstract class ClientJavaExec : JavaExec() {
|
||||
setTestProperties()
|
||||
}
|
||||
|
||||
fun copyFromForge(path: String) = copyFromForge(project.tasks.getByName(path, RunGameTask::class))
|
||||
|
||||
/**
|
||||
* Set this task to run a given [RunGameTask].
|
||||
*/
|
||||
fun copyFromForge(task: RunGameTask) {
|
||||
copyFrom(task)
|
||||
|
||||
// Eagerly evaluate the behaviour of RunGameTask.exec
|
||||
environment.putAll(task.environmentProperty.get())
|
||||
classpath(task.classpathProvider)
|
||||
workingDir = task.gameDirectory.get().asFile
|
||||
|
||||
setTestProperties() // setRunConfig may clobber some properties, ensure everything is set.
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy configuration from a task with the given name.
|
||||
*/
|
||||
|
@ -133,7 +133,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</module>
|
||||
<module name="MethodTypeParameterName" />
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^(dan200\.computercraft|cc\.tweaked)(\.[a-z][a-z0-9]*)*" />
|
||||
<property name="format" value="^(dan200\.computercraft|cc\.tweaked|com\.example\.examplemod)(\.[a-z][a-z0-9]*)*" />
|
||||
</module>
|
||||
<module name="ParameterName" />
|
||||
<module name="StaticVariableName">
|
||||
|
@ -68,7 +68,7 @@ ideaExt = "1.1.7"
|
||||
illuaminate = "0.1.0-74-gf1551d5"
|
||||
lwjgl = "3.3.3"
|
||||
minotaur = "2.8.7"
|
||||
neoGradle = "7.0.170"
|
||||
modDevGradle = "2.0.74"
|
||||
nullAway = "0.10.25"
|
||||
shadow = "8.3.1"
|
||||
spotless = "6.23.3"
|
||||
@ -154,8 +154,8 @@ fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom"
|
||||
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
|
||||
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
|
||||
neoGradle-userdev = { module = "net.neoforged.gradle:userdev", version.ref = "neoGradle" }
|
||||
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
||||
modDevGradle = { module = "net.neoforged:moddev-gradle", version.ref = "modDevGradle" }
|
||||
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" }
|
||||
|
@ -18,13 +18,28 @@ dependencies {
|
||||
api(project(":core-api"))
|
||||
}
|
||||
|
||||
val javadocOverview by tasks.registering(Copy::class) {
|
||||
from("src/overview.html")
|
||||
into(layout.buildDirectory.dir(name))
|
||||
|
||||
expand(
|
||||
mapOf(
|
||||
"mcVersion" to mcVersion,
|
||||
"modVersion" to version,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
tasks.javadoc {
|
||||
title = "CC: Tweaked $version Minecraft $mcVersion"
|
||||
title = "CC: Tweaked $version for Minecraft $mcVersion"
|
||||
include("dan200/computercraft/api/**/*.java")
|
||||
|
||||
options {
|
||||
(this as StandardJavadocDocletOptions)
|
||||
|
||||
inputs.files(javadocOverview)
|
||||
overview(javadocOverview.get().destinationDir.resolve("overview.html").absolutePath)
|
||||
|
||||
groups = mapOf(
|
||||
"Common" to listOf(
|
||||
"dan200.computercraft.api",
|
||||
@ -47,6 +62,12 @@ tasks.javadoc {
|
||||
<link href=" https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css " rel="stylesheet">
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
val snippetSources = listOf(":common", ":fabric", ":forge").flatMap {
|
||||
project(it).sourceSets["examples"].allSource.sourceDirectories
|
||||
}
|
||||
inputs.files(snippetSources)
|
||||
addPathOption("-snippet-path").value = snippetSources
|
||||
}
|
||||
|
||||
// Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
|
||||
|
@ -21,7 +21,14 @@ import java.util.stream.Stream;
|
||||
* <p>
|
||||
* Use {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} to register a
|
||||
* modeller on Fabric and {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} to register one
|
||||
* on Forge
|
||||
* on Forge.
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
* <h3>Fabric</h3>
|
||||
* {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
|
||||
*
|
||||
* <h3>Forge</h3>
|
||||
* {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
|
||||
*
|
||||
* @param <T> The type of turtle upgrade this modeller applies to.
|
||||
* @see RegisterTurtleUpgradeModeller For multi-loader registration support.
|
||||
|
@ -171,16 +171,9 @@ public final class ComputerCraftAPI {
|
||||
* using {@link ILuaAPI#getModuleName()} to expose this library as a module instead of as a global.
|
||||
* <p>
|
||||
* This may be used with {@link IComputerSystem#getComponent(ComputerComponent)} to only attach APIs to specific
|
||||
* computers. For example, one can add an additional API just to turtles with the following code:
|
||||
* computers. For example, one can add a new API just to turtles with the following code:
|
||||
*
|
||||
* {@snippet lang="java":
|
||||
* ComputerCraftAPI.registerAPIFactory(computer -> {
|
||||
* // Read the turtle component.
|
||||
* var turtle = computer.getComponent(ComputerComponents.TURTLE);
|
||||
* // If present then add our API.
|
||||
* return turtle == null ? null : new MyCustomTurtleApi(turtle);
|
||||
* });
|
||||
* }
|
||||
* {@snippet class=com.example.examplemod.ExampleAPI region=register}
|
||||
*
|
||||
* @param factory The factory for your API subclass.
|
||||
* @see ILuaAPIFactory
|
||||
|
@ -20,32 +20,10 @@ import javax.annotation.Nullable;
|
||||
* A peripheral which can be equipped to the back side of a pocket computer.
|
||||
* <p>
|
||||
* Pocket upgrades are defined in two stages. First, one creates a {@link IPocketUpgrade} subclass and corresponding
|
||||
* {@link UpgradeType} instance, which are then registered in a registry.
|
||||
* {@link UpgradeType} instance, which are then registered in a Minecraft registry.
|
||||
* <p>
|
||||
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
||||
* the upgrade automatically registered. It is recommended this is done via
|
||||
* <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
* {@snippet lang="java" :
|
||||
* // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
|
||||
* static final DeferredRegister<UpgradeType<? extends IPocketUpgrade>> POCKET_UPGRADES = DeferredRegister.create(IPocketUpgrade.typeRegistry(), "my_mod");
|
||||
*
|
||||
* // Register a new upgrade upgrade type called "my_upgrade".
|
||||
* public static final RegistryObject<UpgradeType<MyUpgrade>> MY_UPGRADE =
|
||||
* POCKET_UPGRADES.register("my_upgrade", () -> UpgradeType.simple(new MyUpgrade()));
|
||||
*
|
||||
* // Then in your constructor
|
||||
* POCKET_UPGRADES.register(bus);
|
||||
* }
|
||||
* <p>
|
||||
* We can then define a new upgrade using JSON by placing the following in
|
||||
* {@code data/<my_mod>/computercraft/pocket_upgrade/<my_upgrade_id>.json}.
|
||||
* {@snippet lang="json" :
|
||||
* {
|
||||
* "type": "my_mod:my_upgrade"
|
||||
* }
|
||||
* }
|
||||
* the upgrade registered internally.
|
||||
*/
|
||||
public interface IPocketUpgrade extends UpgradeBase {
|
||||
ResourceKey<Registry<IPocketUpgrade>> REGISTRY = ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_upgrade"));
|
||||
|
@ -11,47 +11,79 @@ import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistrySetBuilder.PatchedRegistries;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Items;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* The primary interface for defining an update for Turtles. A turtle update can either be a new tool, or a new
|
||||
* peripheral.
|
||||
* <p>
|
||||
* Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding
|
||||
* {@link UpgradeType} instance, which are then registered in a registry.
|
||||
* {@link UpgradeType} instance, which are then registered in a Minecraft registry.
|
||||
* <p>
|
||||
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
||||
* the upgrade automatically registered. It is recommended this is done via
|
||||
* <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
|
||||
* the upgrade automatically registered.
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
* {@snippet lang="java" :
|
||||
* // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
|
||||
* static final DeferredRegister<UpgradeType<? extends ITurtleUpgrade>> TURTLE_UPGRADES = DeferredRegister.create(ITurtleUpgrade.typeRegistry(), "my_mod");
|
||||
* <h3>Registering the upgrade type</h3>
|
||||
* First, let's create a new class that implements {@link ITurtleUpgrade}. It is recommended to subclass
|
||||
* {@link AbstractTurtleUpgrade}, as that provides a default implementation of most methods.
|
||||
*
|
||||
* // Register a new upgrade type called "my_upgrade".
|
||||
* public static final RegistryObject<UpgradeType<MyUpgrade>> MY_UPGRADE =
|
||||
* TURTLE_UPGRADES.register("my_upgrade", () -> UpgradeType.simple(MyUpgrade::new));
|
||||
* {@snippet class=com.example.examplemod.ExampleTurtleUpgrade region=body}
|
||||
*
|
||||
* // Then in your constructor
|
||||
* TURTLE_UPGRADES.register(bus);
|
||||
* }
|
||||
* Now we must construct a new upgrade type. In most cases, you can use one of the helper methods (e.g.
|
||||
* {@link UpgradeType#simpleWithCustomItem(Function)}), rather than defining your own implementation.
|
||||
*
|
||||
* {@snippet class=com.example.examplemod.ExampleMod region=turtle_upgrades}
|
||||
*
|
||||
* We now must register this upgrade type. This is done the same way as you'd register blocks, items, or other
|
||||
* Minecraft objects. The approach to do this will depend on mod-loader.
|
||||
*
|
||||
* <h4>Fabric</h4>
|
||||
* {@snippet class=com.example.examplemod.FabricExampleMod region=turtle_upgrades}
|
||||
*
|
||||
* <h4>Forge</h4>
|
||||
* {@snippet class=com.example.examplemod.ForgeExampleMod region=turtle_upgrades}
|
||||
*
|
||||
* <h3>Rendering the upgrade</h3>
|
||||
* Next, we need to register a model for our upgrade. This is done by registering a
|
||||
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for your upgrade type.
|
||||
*
|
||||
* <h4>Fabric</h4>
|
||||
* {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
|
||||
*
|
||||
* <h4>Forge</h4>
|
||||
* {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
|
||||
*
|
||||
* <h3 id="datagen">Registering the upgrade itself</h3>
|
||||
* Upgrades themselves are loaded from datapacks when a level is loaded. In order to register our new upgrade, we must
|
||||
* create a new JSON file at {@code data/<my_mod>/computercraft/turtle_upgrade/<my_upgrade_id>.json}.
|
||||
*
|
||||
* {@snippet file=data/examplemod/computercraft/turtle_upgrade/example_turtle_upgrade.json}
|
||||
*
|
||||
* The {@code "type"} field points to the ID of the upgrade type we've just registered, while the other fields are read
|
||||
* by the type itself. As our upgrade was defined with {@link UpgradeType#simpleWithCustomItem(Function)}, the
|
||||
* {@code "item"} field will construct our upgrade with {@link Items#COMPASS}.
|
||||
* <p>
|
||||
* We can then define a new upgrade using JSON by placing the following in
|
||||
* {@code data/<my_mod>/computercraft/turtle_upgrade/<my_upgrade_id>.json}.
|
||||
* <p>
|
||||
* {@snippet lang="json" :
|
||||
* {
|
||||
* "type": "my_mod:my_upgrade"
|
||||
* }
|
||||
* }
|
||||
* <p>
|
||||
* Finally, we need to register a model for our upgrade, see
|
||||
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
|
||||
* Rather than manually creating the file, it is recommended to use data-generators to generate this file. First, we
|
||||
* register our new upgrades into a {@linkplain PatchedRegistries patched registry}.
|
||||
*
|
||||
* {@snippet class=com.example.examplemod.data.TurtleUpgradeProvider region=body}
|
||||
*
|
||||
* Next, we must write these upgrades to disk. Vanilla does not have complete support for this yet, so this must be done
|
||||
* with mod-loader-specific APIs.
|
||||
*
|
||||
* <h4>Fabric</h4>
|
||||
* {@snippet class=com.example.examplemod.FabricExampleModDataGenerator region=turtle_upgrades}
|
||||
*
|
||||
* <h4>Forge</h4>
|
||||
* {@snippet class=com.example.examplemod.ForgeExampleModDataGenerator region=turtle_upgrades}
|
||||
*/
|
||||
public interface ITurtleUpgrade extends UpgradeBase {
|
||||
/**
|
||||
|
@ -25,19 +25,11 @@ import java.util.Optional;
|
||||
/**
|
||||
* A builder for custom turtle tool upgrades.
|
||||
* <p>
|
||||
* This can be used from your <a href="../upgrades/UpgradeType.html#datagen">data generator</a> code in order to
|
||||
* This can be used from your <a href="./ITurtleUpgrade.html#datagen">data generator</a> code in order to
|
||||
* register turtle tools for your mod's tools.
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* {@snippet lang = "java":
|
||||
* import net.minecraft.data.worldgen.BootstrapContext;
|
||||
* import net.minecraft.resources.ResourceLocation;
|
||||
* import net.minecraft.world.item.Items;
|
||||
*
|
||||
* public void registerTool(BootstrapContext<ITurtleUpgrade> upgrades) {
|
||||
* TurtleToolBuilder.tool(ResourceLocation.fromNamespaceAndPath("my_mod", "wooden_pickaxe"), Items.WOODEN_PICKAXE).register(upgrades);
|
||||
* }
|
||||
*}
|
||||
* <h2>Example</h2>
|
||||
* {@snippet class=com.example.examplemod.data.TurtleToolProvider region=body}
|
||||
*/
|
||||
public final class TurtleToolBuilder {
|
||||
private final ResourceKey<ITurtleUpgrade> id;
|
||||
|
@ -7,9 +7,7 @@ package dan200.computercraft.api.upgrades;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.impl.upgrades.UpgradeTypeImpl;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.data.registries.RegistryPatchGenerator;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.level.storage.loot.functions.LootItemFunction;
|
||||
@ -23,13 +21,10 @@ import java.util.function.Function;
|
||||
* follow a similar design to other dynamic content, such as {@linkplain Recipe recipes} or {@link LootItemFunction
|
||||
* loot functions}.
|
||||
* <p>
|
||||
* First, one adds a new class implementing {@link ITurtleUpgrade} or {@link IPocketUpgrade}). This is responsible for
|
||||
* handling all the logic of your upgrade.
|
||||
* <p>
|
||||
* However, the upgrades are not registered directly. Instead, each upgrade class should have a corresponding
|
||||
* {@link UpgradeType}, which is responsible for loading the upgrade from a datapack. The upgrade type should then be
|
||||
* registered in its appropriate registry ({@link ITurtleUpgrade#typeRegistry()},
|
||||
* {@link IPocketUpgrade#typeRegistry()}).
|
||||
* While the {@link ITurtleUpgrade}/{@link IPocketUpgrade} class should contain the core logic of the upgrade, they are
|
||||
* not registered directly. Instead, each upgrade class has a corresponding {@link UpgradeType}, which is responsible
|
||||
* for loading the upgrade from a datapack. The upgrade type should then be registered in its appropriate registry
|
||||
* ({@link ITurtleUpgrade#typeRegistry()}, {@link IPocketUpgrade#typeRegistry()}).
|
||||
* <p>
|
||||
* In order to register the actual upgrade, a JSON file referencing your upgrade type should be added to a datapack. It
|
||||
* is recommended to do this via the data generators.
|
||||
@ -38,29 +33,7 @@ import java.util.function.Function;
|
||||
* As turtle and pocket upgrades are just loaded using vanilla's dynamic loaders, one may use the same data generation
|
||||
* tools as you would for any other dynamic registry.
|
||||
* <p>
|
||||
* This can typically be done by extending vanilla's built-in registries using {@link RegistryPatchGenerator}, and then
|
||||
* writing out the new registries using {@code net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider}
|
||||
* on Fabric or {@code net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider} on Forge.
|
||||
* <p>
|
||||
* {@snippet lang="java" :
|
||||
* import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
* import net.minecraft.Util;
|
||||
* import net.minecraft.core.HolderLookup;
|
||||
* import net.minecraft.core.RegistrySetBuilder;
|
||||
* import net.minecraft.data.DataGenerator;
|
||||
* import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider;
|
||||
*
|
||||
* import java.util.concurrent.CompletableFuture;
|
||||
*
|
||||
* public void generate(DataGenerator.PackGenerator output, CompletableFuture<HolderLookup.Provider> registries) {
|
||||
* var newRegistries = RegistryPatchGenerator.createLookup(registries, Util.make(new RegistrySetBuilder(), builder -> {
|
||||
* builder.add(ITurtleUpgrade.REGISTRY, upgrades -> {
|
||||
* upgrades.register(ITurtleUpgrade.createKey(ResourceLocation.fromNamespaceAndPath("my_mod", "my_upgrade")), new MyUpgrade());
|
||||
* });
|
||||
* }));
|
||||
* output.addProvider(o -> new DatapackBuiltinEntriesProvider(o, newRegistries, Set.of("my_mod")));
|
||||
* }
|
||||
* }
|
||||
* See <a href="../turtle/ITurtleUpgrade.html#datagen">the turtle upgrade docs</a> for a concrete example.
|
||||
*
|
||||
* @param <T> The upgrade subclass that this upgrade type represents.
|
||||
* @see ITurtleUpgrade
|
||||
|
@ -2,12 +2,9 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.impl.upgrades;
|
||||
package dan200.computercraft.api.upgrades;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link UpgradeType}.
|
||||
@ -15,6 +12,5 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
* @param codec The codec to read/write upgrades with.
|
||||
* @param <T> The upgrade subclass that this upgrade type represents.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public record UpgradeTypeImpl<T extends UpgradeBase>(MapCodec<T> codec) implements UpgradeType<T> {
|
||||
record UpgradeTypeImpl<T extends UpgradeBase>(MapCodec<T> codec) implements UpgradeType<T> {
|
||||
}
|
68
projects/common-api/src/overview.html
Normal file
68
projects/common-api/src/overview.html
Normal file
@ -0,0 +1,68 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<p>
|
||||
This is the documentation for CC: Tweaked $modVersion for Minecraft $mcVersion. Documentation for other versions of
|
||||
Minecraft are available on the CC: Tweaked website:
|
||||
|
||||
<ul>
|
||||
<li><a href="/mc-1.20.x/javadoc/">Minecraft 1.20.1</a>
|
||||
<li><a href="/mc-1.21.x/javadoc/">Minecraft 1.21.1</a>
|
||||
</ul>
|
||||
|
||||
<h1>Quick links</h1>
|
||||
<p>
|
||||
You probably want to start in the following places:
|
||||
|
||||
<ul>
|
||||
<li>{@linkplain dan200.computercraft.api.peripheral Registering new peripherals}</li>
|
||||
<li>
|
||||
{@link dan200.computercraft.api.lua.LuaFunction} and {@link dan200.computercraft.api.lua.IArguments} for
|
||||
adding methods to your peripheral or Lua objects.
|
||||
</li>
|
||||
<li>{@linkplain dan200.computercraft.api.turtle.ITurtleUpgrade Turtle upgrades}</li>
|
||||
<li>{@linkplain dan200.computercraft.api.pocket.IPocketUpgrade Pocket upgrades}</li>
|
||||
</ul>
|
||||
|
||||
<h1>Using</h1>
|
||||
<p>
|
||||
CC: Tweaked is hosted on my maven repo, and so is relatively simple to depend on. You may wish to add a soft (or
|
||||
hard) dependency in your <code>mods.toml</code> file, with the appropriate version bounds, to ensure that API
|
||||
functionality you depend on is present.
|
||||
|
||||
<pre class="language language-groovy"><code>repositories {
|
||||
maven {
|
||||
url "https://maven.squiddev.cc"
|
||||
content { includeGroup("cc.tweaked") }
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Vanilla (i.e. for multi-loader systems)
|
||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$modVersion")
|
||||
|
||||
// Forge Gradle
|
||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$modVersion")
|
||||
compileOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$modVersion"))
|
||||
runtimeOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge:$modVersion"))
|
||||
|
||||
// Fabric Loom
|
||||
modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$modVersion")
|
||||
modRuntimeOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric:$modVersion")
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<p>
|
||||
You should also be careful to only use classes within the <code>dan200.computercraft.api</code> package. Non-API
|
||||
classes are subject to change at any point. If you depend on functionality outside the API (or need to mixin to
|
||||
CC:T), please <a href="https://github.com/cc-tweaked/CC-Tweaked/discussions/new/choose">start a discussion</a> to
|
||||
let me know!
|
||||
|
||||
</body>
|
||||
</html>
|
@ -11,12 +11,6 @@ plugins {
|
||||
id("cc-tweaked.publishing")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources.srcDir("src/generated/resources")
|
||||
}
|
||||
}
|
||||
|
||||
minecraft {
|
||||
accessWideners(
|
||||
"src/main/resources/computercraft.accesswidener",
|
||||
@ -115,20 +109,28 @@ val lintLua by tasks.registering(IlluaminateExec::class) {
|
||||
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")
|
||||
fun MergeTrees.configureForDatagen(source: SourceSet, outputFolder: String) {
|
||||
output = layout.projectDirectory.dir(outputFolder)
|
||||
|
||||
for (loader in listOf("forge", "fabric")) {
|
||||
mustRunAfter(":$loader:runData")
|
||||
mustRunAfter(":$loader:$name")
|
||||
source {
|
||||
input {
|
||||
from(project(":$loader").layout.buildDirectory.dir("generatedResources"))
|
||||
from(project(":$loader").layout.buildDirectory.dir(source.getTaskName("generateResources", null)))
|
||||
exclude(".cache")
|
||||
}
|
||||
|
||||
output = project(":$loader").layout.projectDirectory.dir("src/generated/resources")
|
||||
output = project(":$loader").layout.projectDirectory.dir(outputFolder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val runData by tasks.registering(MergeTrees::class) {
|
||||
configureForDatagen(sourceSets.main.get(), "src/generated/resources")
|
||||
}
|
||||
|
||||
val runExampleData by tasks.registering(MergeTrees::class) {
|
||||
configureForDatagen(sourceSets.examples.get(), "src/examples/generatedResources")
|
||||
}
|
||||
|
||||
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }
|
||||
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
"type": "examplemod:example_turtle_upgrade",
|
||||
"item": "minecraft:compass"
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.example.examplemod;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.component.ComputerComponents;
|
||||
import dan200.computercraft.api.lua.Coerced;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* An example API that will be available on every turtle. This demonstrates both registering an API, and how to write
|
||||
* Lua-facing functions.
|
||||
* <p>
|
||||
* This API is not available as a global (as {@link #getNames() returns nothing}), but is instead accessible via
|
||||
* {@code require} (see {@link #getModuleName()}).
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
* <pre class="language language-lua">{@code
|
||||
* local my_api = require("example.my_api")
|
||||
* print("Turtle is facing " .. my_api.getDirection())
|
||||
* }</pre>
|
||||
*/
|
||||
public class ExampleAPI implements ILuaAPI {
|
||||
private final ITurtleAccess turtle;
|
||||
|
||||
public ExampleAPI(ITurtleAccess turtle) {
|
||||
this.turtle = turtle;
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
// @start region=register
|
||||
ComputerCraftAPI.registerAPIFactory(computer -> {
|
||||
// Read the turtle component.
|
||||
var turtle = computer.getComponent(ComputerComponents.TURTLE);
|
||||
// If present then add our API.
|
||||
return turtle == null ? null : new ExampleAPI(turtle);
|
||||
});
|
||||
// @end region=register
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNames() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getModuleName() {
|
||||
return "example.my_api";
|
||||
}
|
||||
|
||||
/**
|
||||
* A Lua-facing function function that returns the direction the turtle is facing.
|
||||
*
|
||||
* @return The turtle's direction.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String getDirection() {
|
||||
return turtle.getDirection().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* A Lua-facing function using {@link Coerced}. Unlike a {@link LuaFunction} taking a raw {@link String}, this will
|
||||
* accept any value, and convert it to a string.
|
||||
*
|
||||
* @param myString The value to write.
|
||||
*/
|
||||
// @start region=coerced
|
||||
@LuaFunction
|
||||
public final void writeString(Coerced<String> myString) {
|
||||
String contents = myString.value();
|
||||
System.out.println("Got " + contents);
|
||||
}
|
||||
// @end region=coerced
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.example.examplemod;
|
||||
|
||||
import com.example.examplemod.data.TurtleUpgradeProvider;
|
||||
import com.example.examplemod.peripheral.FurnacePeripheral;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
|
||||
/**
|
||||
* Our example mod, containing the various things we register.
|
||||
* <p>
|
||||
* This isn't an especially good template to follow! It's convenient for our example mod (as we need to be multi-loader
|
||||
* compatible), but there's a good chance there's a better pattern to follow. For example, on Forge you'd use
|
||||
* {@code DeferredRegister} to register things), and multi-loader mods probably have their own abstractions.
|
||||
* <p>
|
||||
* See {@code FabricExampleMod} and {@code ForgeExampleMod} for the actual mod entrypoints.
|
||||
*/
|
||||
public final class ExampleMod {
|
||||
public static final String MOD_ID = "examplemod";
|
||||
|
||||
/**
|
||||
* The upgrade type for our example turtle upgrade. See the documentation for {@link UpgradeType} or
|
||||
* {@code FabricExampleMod}/{@code ForgeExampleMod} for how this is registered.
|
||||
* <p>
|
||||
* This only defines the upgrade type. See {@link TurtleUpgradeProvider} for defining the actual upgrade.
|
||||
*/
|
||||
// @start region=turtle_upgrades
|
||||
public static final UpgradeType<ExampleTurtleUpgrade> EXAMPLE_TURTLE_UPGRADE = UpgradeType.simpleWithCustomItem(
|
||||
ExampleTurtleUpgrade::new
|
||||
);
|
||||
// @end region=turtle_upgrades
|
||||
|
||||
public static void registerComputerCraft() {
|
||||
// @start region=generic_source
|
||||
ComputerCraftAPI.registerGenericSource(new FurnacePeripheral());
|
||||
// @end region=generic_source
|
||||
|
||||
ExampleAPI.register();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.example.examplemod;
|
||||
|
||||
import dan200.computercraft.api.turtle.AbstractTurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeType;
|
||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* An example turtle upgrade.
|
||||
*/
|
||||
// @start region=body
|
||||
public class ExampleTurtleUpgrade extends AbstractTurtleUpgrade {
|
||||
public ExampleTurtleUpgrade(ItemStack stack) {
|
||||
super(TurtleUpgradeType.PERIPHERAL, "example", stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpgradeType<ExampleTurtleUpgrade> getType() {
|
||||
return ExampleMod.EXAMPLE_TURTLE_UPGRADE;
|
||||
}
|
||||
}
|
||||
// @end region=body
|
@ -0,0 +1,23 @@
|
||||
package com.example.examplemod.data;
|
||||
|
||||
import com.example.examplemod.ExampleMod;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleToolBuilder;
|
||||
import net.minecraft.data.worldgen.BootstrapContext;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Items;
|
||||
|
||||
/**
|
||||
* Extends the bootstrap registries with a new turtle tool.
|
||||
* <p>
|
||||
* See {@link TurtleUpgradeProvider} and {@link ITurtleUpgrade} for how this would be used in datagen.
|
||||
*/
|
||||
public class TurtleToolProvider {
|
||||
// @start region=body
|
||||
public static void addUpgrades(BootstrapContext<ITurtleUpgrade> upgrades) {
|
||||
TurtleToolBuilder
|
||||
.tool(ResourceLocation.fromNamespaceAndPath(ExampleMod.MOD_ID, "wooden_pickaxe"), Items.WOODEN_PICKAXE)
|
||||
.register(upgrades);
|
||||
}
|
||||
// @end region=body
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.example.examplemod.data;
|
||||
|
||||
import com.example.examplemod.ExampleMod;
|
||||
import com.example.examplemod.ExampleTurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.RegistrySetBuilder;
|
||||
import net.minecraft.data.registries.RegistryPatchGenerator;
|
||||
import net.minecraft.data.worldgen.BootstrapContext;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Extends the bootstrap registries with our {@linkplain ExampleTurtleUpgrade example turtle upgrade}.
|
||||
*/
|
||||
// @start region=body
|
||||
public class TurtleUpgradeProvider {
|
||||
// Register our turtle upgrades.
|
||||
public static void addUpgrades(BootstrapContext<ITurtleUpgrade> upgrades) {
|
||||
upgrades.register(
|
||||
ITurtleUpgrade.createKey(ResourceLocation.fromNamespaceAndPath(ExampleMod.MOD_ID, "example_turtle_upgrade")),
|
||||
new ExampleTurtleUpgrade(new ItemStack(Items.COMPASS))
|
||||
);
|
||||
}
|
||||
|
||||
// Set up the dynamic registries to contain our turtle upgrades.
|
||||
public static CompletableFuture<RegistrySetBuilder.PatchedRegistries> makeUpgradeRegistry(CompletableFuture<HolderLookup.Provider> registries) {
|
||||
return RegistryPatchGenerator.createLookup(registries, Util.make(new RegistrySetBuilder(), builder -> {
|
||||
builder.add(ITurtleUpgrade.REGISTRY, TurtleUpgradeProvider::addUpgrades);
|
||||
}));
|
||||
}
|
||||
}
|
||||
// @end region=body
|
@ -0,0 +1,12 @@
|
||||
@ApiStatus.Internal
|
||||
@DefaultQualifier(value = NonNull.class, locations = {
|
||||
TypeUseLocation.RETURN,
|
||||
TypeUseLocation.PARAMETER,
|
||||
TypeUseLocation.FIELD,
|
||||
})
|
||||
package com.example.examplemod;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
import org.checkerframework.framework.qual.TypeUseLocation;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
@ -0,0 +1,39 @@
|
||||
package com.example.examplemod.peripheral;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A peripheral that adds a {@code getFuel()} method to brewing stands. This demonstrates the usage of
|
||||
* {@link IPeripheral}.
|
||||
*
|
||||
* @see dan200.computercraft.api.peripheral
|
||||
* @see FurnacePeripheral Using {@code GenericPeripheral}.
|
||||
*/
|
||||
// @start region=body
|
||||
public class BrewingStandPeripheral implements IPeripheral {
|
||||
private final BrewingStandBlockEntity brewingStand;
|
||||
|
||||
public BrewingStandPeripheral(BrewingStandBlockEntity brewingStand) {
|
||||
this.brewingStand = brewingStand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "brewing_stand";
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
public final int getFuel() {
|
||||
// Don't do it this way! Use an access widener/transformer to access the "fuel" field instead.
|
||||
return brewingStand.saveWithoutMetadata(brewingStand.getLevel().registryAccess()).getInt("Fuel");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable IPeripheral other) {
|
||||
return other instanceof BrewingStandPeripheral o && brewingStand == o.brewingStand;
|
||||
}
|
||||
}
|
||||
// @end region=body
|
@ -0,0 +1,44 @@
|
||||
package com.example.examplemod.peripheral;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.AttachedComputerSet;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A peripheral that tracks what computers it is attached to.
|
||||
*
|
||||
* @see AttachedComputerSet
|
||||
*/
|
||||
// @start region=body
|
||||
public class ComputerTrackingPeripheral implements IPeripheral {
|
||||
private final AttachedComputerSet computers = new AttachedComputerSet();
|
||||
|
||||
@Override
|
||||
public void attach(IComputerAccess computer) {
|
||||
computers.add(computer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach(IComputerAccess computer) {
|
||||
computers.remove(computer);
|
||||
}
|
||||
|
||||
@LuaFunction
|
||||
public final void sayHello() {
|
||||
// Queue a "hello" event on each computer.
|
||||
computers.forEach(x -> x.queueEvent("hello", x.getAttachmentName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "my_peripheral";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable IPeripheral other) {
|
||||
return this == other;
|
||||
}
|
||||
}
|
||||
// @end region=body
|
@ -0,0 +1,29 @@
|
||||
package com.example.examplemod.peripheral;
|
||||
|
||||
import com.example.examplemod.ExampleMod;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.GenericPeripheral;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
|
||||
|
||||
/**
|
||||
* A peripheral that adds a {@code getBurnTime} method to furnaces. This is used to demonstrate the usage of
|
||||
* {@link GenericPeripheral}.
|
||||
*
|
||||
* @see dan200.computercraft.api.peripheral
|
||||
* @see BrewingStandPeripheral Using {@code IPeripheral}.
|
||||
*/
|
||||
// @start region=body
|
||||
public class FurnacePeripheral implements GenericPeripheral {
|
||||
@Override
|
||||
public String id() {
|
||||
return ResourceLocation.fromNamespaceAndPath(ExampleMod.MOD_ID, "furnace").toString();
|
||||
}
|
||||
|
||||
@LuaFunction(mainThread = true)
|
||||
public int getBurnTime(AbstractFurnaceBlockEntity furnace) {
|
||||
// Don't do it this way! Use an access widener/transformer to access the "litTime" field instead.
|
||||
return furnace.saveWithoutMetadata(furnace.getLevel().registryAccess()).getInt("BurnTime");
|
||||
}
|
||||
}
|
||||
// @end region=body
|
@ -747,6 +747,7 @@ public class TurtleAPI implements ILuaAPI {
|
||||
* @throws LuaException If the slot is out of range.
|
||||
* @cc.treturn nil|table Information about the given slot, or {@code nil} if it is empty.
|
||||
* @cc.since 1.64
|
||||
* @cc.changed 1.90.0 Added detailed parameter.
|
||||
* @cc.usage Print the current slot, assuming it contains 13 dirt.
|
||||
*
|
||||
* <pre>{@code
|
||||
|
@ -8,10 +8,6 @@ plugins {
|
||||
id("cc-tweaked")
|
||||
}
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
// Due to the slightly circular nature of our API, add the main API jars to the javadoc classpath.
|
||||
val docApi by configurations.registering {
|
||||
isTransitive = false
|
||||
|
@ -10,13 +10,8 @@ package dan200.computercraft.api.lua;
|
||||
* This is designed to be used with {@link LuaFunction} annotated functions, to mark an argument as being coerced to
|
||||
* the given type, rather than requiring an exact type.
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* {@snippet lang="java" :
|
||||
* @LuaFunction
|
||||
* public final void doSomething(Coerced<String> myString) {
|
||||
* var value = myString.value();
|
||||
* }
|
||||
* }
|
||||
* <h2>Example</h2>
|
||||
* {@snippet class=com.example.examplemod.ExampleAPI region=coerced}
|
||||
*
|
||||
* @param value The argument value.
|
||||
* @param <T> The type of the underlying value.
|
||||
|
@ -18,22 +18,11 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
* by capabilities/the block lookup API take priority. Block entities which use this system are given a peripheral name
|
||||
* determined by their id, rather than any peripheral provider, though additional types may be provided by overriding
|
||||
* {@link GenericPeripheral#getType()}.
|
||||
* <p>
|
||||
* For example, the main CC: Tweaked mod defines a generic source for inventories, which works on {@code IItemHandler}s:
|
||||
*
|
||||
* {@snippet lang="java" :
|
||||
* public class InventoryMethods implements GenericSource {
|
||||
* @LuaFunction(mainThread = true)
|
||||
* public int size(IItemHandler inventory) {
|
||||
* return inventory.getSlots();
|
||||
* }
|
||||
*
|
||||
* // ...
|
||||
* }
|
||||
* }
|
||||
* <h2>Example</h2>
|
||||
* {@snippet class=com.example.examplemod.peripheral.FurnacePeripheral region=body}
|
||||
* <p>
|
||||
* New capabilities or block lookups (those not built into Forge/Fabric) must be explicitly registered using the
|
||||
* loader-specific API.
|
||||
* New capabilities (those not built into Forge) must be explicitly registered using the loader-specific API.
|
||||
*
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerGenericSource(GenericSource)
|
||||
*/
|
||||
|
@ -4,17 +4,6 @@
|
||||
|
||||
/**
|
||||
* ComputerCraft's public API.
|
||||
* <p>
|
||||
* You probably want to start in the following places:
|
||||
* <ul>
|
||||
* <li>{@link dan200.computercraft.api.peripheral} for registering new peripherals.</li>
|
||||
* <li>
|
||||
* {@link dan200.computercraft.api.lua.LuaFunction} and {@link dan200.computercraft.api.lua.IArguments} for
|
||||
* adding methods to your peripheral or Lua objects.
|
||||
* </li>
|
||||
* <li>{@link dan200.computercraft.api.turtle.ITurtleUpgrade} for turtle upgrades.</li>
|
||||
* <li>{@link dan200.computercraft.api.pocket.IPocketUpgrade} for pocket upgrades.</li>
|
||||
* </ul>
|
||||
*/
|
||||
@DefaultQualifier(value = NonNull.class, locations = {
|
||||
TypeUseLocation.RETURN,
|
||||
|
@ -27,21 +27,7 @@ import java.util.function.Consumer;
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
*
|
||||
* {@snippet lang="java" :
|
||||
* public class MyPeripheral implements IPeripheral {
|
||||
* private final AttachedComputerSet computers = new ComputerCollection();
|
||||
*
|
||||
* @Override
|
||||
* public void attach(IComputerAccess computer) {
|
||||
* computers.add(computer);
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void detach(IComputerAccess computer) {
|
||||
* computers.remove(computer);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* {@snippet class=com.example.examplemod.peripheral.ComputerTrackingPeripheral region=body}
|
||||
*
|
||||
* @see IComputerAccess
|
||||
* @see IPeripheral#attach(IComputerAccess)
|
||||
|
@ -47,36 +47,12 @@
|
||||
* Then, we can start adding methods to your block entity. Each method should take its target type as the first
|
||||
* argument, which in this case is a {@code AbstractFurnaceBlockEntity}. We then annotate this method with
|
||||
* {@link dan200.computercraft.api.lua.LuaFunction} to expose it to computers.
|
||||
* <p>
|
||||
* {@snippet lang="java" :
|
||||
* import dan200.computercraft.api.lua.LuaFunction;
|
||||
* import dan200.computercraft.api.peripheral.GenericPeripheral;
|
||||
* import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
|
||||
*
|
||||
* public final class FurnacePeripheral implements GenericPeripheral {
|
||||
* @Override
|
||||
* public String id() {
|
||||
* return "mymod:furnace";
|
||||
* }
|
||||
* {@snippet class=com.example.examplemod.peripheral.FurnacePeripheral region=body}
|
||||
*
|
||||
* @LuaFunction(mainThread = true)
|
||||
* public int getBurnTime(AbstractFurnaceBlockEntity furnace) {
|
||||
* return furnace.litTime;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* <p>
|
||||
* Finally, we need to register our peripheral, so that ComputerCraft is aware of it:
|
||||
* <p>
|
||||
* {@snippet lang="java" :
|
||||
* import dan200.computercraft.api.ComputerCraftAPI;
|
||||
*
|
||||
* public class ComputerCraftCompat {
|
||||
* public static void register() {
|
||||
* ComputerCraftAPI.registerGenericSource(new FurnacePeripheral());
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* {@snippet class=com.example.examplemod.ExampleMod region=generic_source}
|
||||
*
|
||||
* <h3>Creating a {@code IPeripheral}</h3>
|
||||
* First, we'll need to create a new class that implements {@link dan200.computercraft.api.peripheral.IPeripheral}. This
|
||||
@ -84,72 +60,20 @@
|
||||
* <p>
|
||||
* We can then start adding peripheral methods to our class. Each method should be {@code final}, and annotated with
|
||||
* {@link dan200.computercraft.api.lua.LuaFunction}.
|
||||
* <p>
|
||||
* {@snippet lang="java" :
|
||||
* import dan200.computercraft.api.lua.LuaFunction;
|
||||
* import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
* import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
|
||||
* import org.jetbrains.annotations.Nullable;
|
||||
*
|
||||
* public class FurnacePeripheral implements IPeripheral {
|
||||
* private final AbstractFurnaceBlockEntity furnace;
|
||||
* {@snippet class=com.example.examplemod.peripheral.BrewingStandPeripheral region=body}
|
||||
*
|
||||
* public FurnacePeripheral(AbstractFurnaceBlockEntity furnace) {
|
||||
* this.furnace = furnace;
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public String getType() {
|
||||
* return "furnace";
|
||||
* }
|
||||
*
|
||||
* @LuaFunction(mainThread = true)
|
||||
* public final int getBurnTime() {
|
||||
* return furnace.litTime;
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public boolean equals(@Nullable IPeripheral other) {
|
||||
* return this == other || other instanceof FurnacePeripheral p && furnace == p.furnace;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* <p>
|
||||
* Finally, we'll need to register our peripheral. This is done with capabilities on Forge, or the block lookup API on
|
||||
* Fabric.
|
||||
*
|
||||
* <h4>Registering {@code IPeripheral} on Forge</h4>
|
||||
* Registering a peripheral on Forge can be done by using the capability API, via {@code PeripheralCapability}.
|
||||
*
|
||||
* {@snippet lang="java" :
|
||||
* import dan200.computercraft.api.peripheral.PeripheralCapability;
|
||||
* import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
* import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
|
||||
*
|
||||
* public class ComputerCraftCompat {
|
||||
* public static void register(RegisterCapabilitiesEvent event) {
|
||||
* event.registerBlockEntity(PeripheralCapability.get(), BlockEntityType.FURNACE, (f, s) -> new FurnacePeripheral(f));
|
||||
* event.registerBlockEntity(PeripheralCapability.get(), BlockEntityType.BLAST_FURNACE, (f, s) -> new FurnacePeripheral(f));
|
||||
* event.registerBlockEntity(PeripheralCapability.get(), BlockEntityType.SMOKER, (f, s) -> new FurnacePeripheral(f));
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* {@snippet class=com.example.examplemod.ForgeExampleMod region=peripherals}
|
||||
*
|
||||
* <h4>Registering {@code IPeripheral} on Fabric</h4>
|
||||
* Registering a peripheral on Fabric can be done using the block lookup API, via {@code PeripheralLookup}.
|
||||
*
|
||||
* {@snippet lang="java" :
|
||||
* import dan200.computercraft.api.peripheral.PeripheralLookup;
|
||||
* import dan200.computercraft.example.FurnacePeripheral;
|
||||
* import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
*
|
||||
* public class ComputerCraftCompat {
|
||||
* public static void register() {
|
||||
* PeripheralLookup.get().registerForBlockEntity((f, s) -> new FurnacePeripheral(f), BlockEntityType.FURNACE);
|
||||
* PeripheralLookup.get().registerForBlockEntity((f, s) -> new FurnacePeripheral(f), BlockEntityType.BLAST_FURNACE);
|
||||
* PeripheralLookup.get().registerForBlockEntity((f, s) -> new FurnacePeripheral(f), BlockEntityType.SMOKER);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* {@snippet class=com.example.examplemod.FabricExampleMod region=peripherals}
|
||||
*/
|
||||
package dan200.computercraft.api.peripheral;
|
||||
|
@ -13,7 +13,7 @@ import java.util.Set;
|
||||
* This is intended for logging errors where the message content is supplied from untrusted sources. This isn't a
|
||||
* perfect escaping mechanism, but ensures basic "unsafe" strings (i.e. ANSI escape sequences, long lines) are escaped.
|
||||
*
|
||||
* <h2>Example:</h2>
|
||||
* <h2>Example</h2>
|
||||
* <pre>{@code
|
||||
* LOG.error("Some error occurred: {}", new TruncatedError(error));
|
||||
* }</pre>
|
||||
|
@ -223,16 +223,21 @@ end
|
||||
--- Returns true if a path is mounted to the parent filesystem.
|
||||
--
|
||||
-- The root filesystem "/" is considered a mount, along with disk folders and
|
||||
-- the rom folder. Other programs (such as network shares) can exstend this to
|
||||
-- make other mount types by correctly assigning their return value for getDrive.
|
||||
-- the rom folder.
|
||||
--
|
||||
-- @tparam string path The path to check.
|
||||
-- @treturn boolean If the path is mounted, rather than a normal file/folder.
|
||||
-- @throws If the path does not exist.
|
||||
-- @see getDrive
|
||||
-- @since 1.87.0
|
||||
function fs.isDriveRoot(sPath)
|
||||
expect(1, sPath, "string")
|
||||
function fs.isDriveRoot(path)
|
||||
expect(1, path, "string")
|
||||
|
||||
local parent = fs.getDir(path)
|
||||
|
||||
-- Force the root directory to be a mount.
|
||||
return fs.getDir(sPath) == ".." or fs.getDrive(sPath) ~= fs.getDrive(fs.getDir(sPath))
|
||||
if parent == ".." then return true end
|
||||
|
||||
local drive = fs.getDrive(path)
|
||||
return drive ~= nil and drive ~= fs.getDrive(parent)
|
||||
end
|
||||
|
@ -147,7 +147,7 @@ handleMetatable = {
|
||||
|
||||
if format == "l" then
|
||||
if handle.readLine then res = handle.readLine() end
|
||||
elseif format == "L" and handle.readLine then
|
||||
elseif format == "L" then
|
||||
if handle.readLine then res = handle.readLine(true) end
|
||||
elseif format == "a" then
|
||||
if handle.readAll then res = handle.readAll() or "" end
|
||||
|
@ -35,7 +35,7 @@ local function getFilename(sUrl)
|
||||
return sUrl:match("/([^/]+)$")
|
||||
end
|
||||
|
||||
local function get(sUrl)
|
||||
local function get(url)
|
||||
-- Check if the URL is valid
|
||||
local ok, err = http.checkURL(url)
|
||||
if not ok then
|
||||
@ -43,12 +43,12 @@ local function get(sUrl)
|
||||
return
|
||||
end
|
||||
|
||||
write("Connecting to " .. sUrl .. "... ")
|
||||
write("Connecting to " .. url .. "... ")
|
||||
|
||||
local response = http.get(sUrl)
|
||||
local response, err = http.get(url)
|
||||
if not response then
|
||||
print("Failed.")
|
||||
return nil
|
||||
printError(err)
|
||||
return
|
||||
end
|
||||
|
||||
print("Success.")
|
||||
|
@ -104,7 +104,7 @@ while running do
|
||||
end
|
||||
else
|
||||
printError(results[2])
|
||||
require "cc.internal.exception".report(results[2], results[3], chunk_map)
|
||||
exception.report(results[2], results[3], chunk_map)
|
||||
end
|
||||
else
|
||||
local parser = require "cc.internal.syntax"
|
||||
|
@ -83,6 +83,10 @@ describe("The fs library", function()
|
||||
expect(fs.isDriveRoot("/rom/startup.lua")):eq(false)
|
||||
expect(fs.isDriveRoot("/rom/programs/delete.lua")):eq(false)
|
||||
end)
|
||||
|
||||
it("returns false for missing files", function()
|
||||
expect(fs.isDriveRoot("does_not_exist")):eq(false)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fs.list", function()
|
||||
@ -555,6 +559,22 @@ describe("The fs library", function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fs.getDrive", function()
|
||||
it("returns the drive for the mount roots", function()
|
||||
expect(fs.getDrive("")):eq("hdd")
|
||||
expect(fs.getDrive("rom")):eq("rom")
|
||||
end)
|
||||
|
||||
it("returns the drive for subdirectories", function()
|
||||
expect(fs.getDrive("rom/startup.lua")):eq("rom")
|
||||
end)
|
||||
|
||||
it("returns nothing for missing files", function()
|
||||
-- Peculiar, but we return no values, rather than nil!
|
||||
expect(table.pack(fs.getDrive("no_such_file"))):same { n = 0 }
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("fs.attributes", function()
|
||||
it("errors on non-existent files", function()
|
||||
expect.error(fs.attributes, "xuxu_nao_existe"):eq("/xuxu_nao_existe: No such file")
|
||||
|
@ -7,10 +7,6 @@ plugins {
|
||||
id("cc-tweaked.publishing")
|
||||
}
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
cct.inlineProject(":common-api")
|
||||
|
||||
dependencies {
|
||||
|
@ -106,8 +106,6 @@ dependencies {
|
||||
testFixturesImplementation(testFixtures(project(":core")))
|
||||
}
|
||||
|
||||
sourceSets.main { resources.srcDir("src/generated/resources") }
|
||||
|
||||
loom {
|
||||
accessWidenerPath.set(project(":common").file("src/main/resources/computercraft.accesswidener"))
|
||||
mixin.useLegacyMixinAp = false
|
||||
@ -126,6 +124,10 @@ loom {
|
||||
sourceSet(sourceSets.testMod.get())
|
||||
sourceSet(project(":common").sourceSets.testMod.get())
|
||||
}
|
||||
|
||||
register("examplemod") {
|
||||
sourceSet(sourceSets.examples.get())
|
||||
}
|
||||
}
|
||||
|
||||
runs {
|
||||
@ -143,19 +145,24 @@ loom {
|
||||
runDir("run/server")
|
||||
}
|
||||
|
||||
register("data") {
|
||||
configName = "Datagen"
|
||||
fun RunConfigSettings.configureForData(sourceSet: SourceSet) {
|
||||
client()
|
||||
|
||||
source(sourceSets.datagen.get())
|
||||
|
||||
runDir("run/dataGen")
|
||||
runDir("run/run${name.capitalise()}")
|
||||
property("fabric-api.datagen")
|
||||
property("fabric-api.datagen.output-dir", layout.buildDirectory.dir("generatedResources").getAbsolutePath())
|
||||
property(
|
||||
"fabric-api.datagen.output-dir",
|
||||
layout.buildDirectory.dir(sourceSet.getTaskName("generateResources", null)).getAbsolutePath(),
|
||||
)
|
||||
property("fabric-api.datagen.strict-validation")
|
||||
}
|
||||
|
||||
fun configureForGameTest(config: RunConfigSettings) = config.run {
|
||||
register("data") {
|
||||
configName = "Datagen"
|
||||
configureForData(sourceSets.main.get())
|
||||
source(sourceSets.datagen.get())
|
||||
}
|
||||
|
||||
fun RunConfigSettings.configureForGameTest() {
|
||||
source(sourceSets.testMod.get())
|
||||
|
||||
val testSources = project(":common").file("src/testMod/resources/data/cctest").absolutePath
|
||||
@ -170,7 +177,7 @@ loom {
|
||||
val testClient by registering {
|
||||
configName = "Test Client"
|
||||
client()
|
||||
configureForGameTest(this)
|
||||
configureForGameTest()
|
||||
|
||||
runDir("run/testClient")
|
||||
property("cctest.tags", "client,common")
|
||||
@ -179,16 +186,27 @@ loom {
|
||||
register("gametest") {
|
||||
configName = "Game Test"
|
||||
server()
|
||||
configureForGameTest(this)
|
||||
configureForGameTest()
|
||||
|
||||
property("fabric-api.gametest")
|
||||
property(
|
||||
"fabric-api.gametest.report-file",
|
||||
layout.buildDirectory.dir("test-results/runGametest.xml")
|
||||
.getAbsolutePath(),
|
||||
layout.buildDirectory.dir("test-results/runGametest.xml").getAbsolutePath(),
|
||||
)
|
||||
runDir("run/gametest")
|
||||
}
|
||||
|
||||
register("exampleClient") {
|
||||
client()
|
||||
configName = "Example Mod Client"
|
||||
source(sourceSets.examples.get())
|
||||
}
|
||||
|
||||
register("exampleData") {
|
||||
configName = "Example Mod Datagen"
|
||||
configureForData(sourceSets.examples.get())
|
||||
source(sourceSets.examples.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
package com.example.examplemod;
|
||||
|
||||
import com.example.examplemod.peripheral.BrewingStandPeripheral;
|
||||
import dan200.computercraft.api.peripheral.PeripheralLookup;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
|
||||
/**
|
||||
* The main entry point for our example mod.
|
||||
*/
|
||||
public class FabricExampleMod implements ModInitializer {
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
// @start region=turtle_upgrades
|
||||
@SuppressWarnings("unchecked")
|
||||
var turtleUpgradeSerialisers = (Registry<UpgradeType<? extends ITurtleUpgrade>>) BuiltInRegistries.REGISTRY.get(ITurtleUpgrade.typeRegistry().location());
|
||||
Registry.register(turtleUpgradeSerialisers, ResourceLocation.fromNamespaceAndPath(ExampleMod.MOD_ID, "example_turtle_upgrade"), ExampleMod.EXAMPLE_TURTLE_UPGRADE);
|
||||
// @end region=turtle_upgrades
|
||||
|
||||
ExampleMod.registerComputerCraft();
|
||||
|
||||
// @start region=peripherals
|
||||
PeripheralLookup.get().registerForBlockEntity((f, s) -> new BrewingStandPeripheral(f), BlockEntityType.BREWING_STAND);
|
||||
// @end region=peripherals
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.example.examplemod;
|
||||
|
||||
import dan200.computercraft.api.client.FabricComputerCraftAPIClient;
|
||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
|
||||
public class FabricExampleModClient implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
// @start region=turtle_modellers
|
||||
FabricComputerCraftAPIClient.registerTurtleUpgradeModeller(ExampleMod.EXAMPLE_TURTLE_UPGRADE, TurtleUpgradeModeller.flatItem());
|
||||
// @end region=turtle_modellers
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.example.examplemod;
|
||||
|
||||
import com.example.examplemod.data.TurtleUpgradeProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider;
|
||||
import net.fabricmc.fabric.api.event.registry.DynamicRegistries;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.RegistrySetBuilder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Data generators for our Fabric example mod.
|
||||
*/
|
||||
public class FabricExampleModDataGenerator implements DataGeneratorEntrypoint {
|
||||
@Override
|
||||
public void onInitializeDataGenerator(FabricDataGenerator generator) {
|
||||
var pack = generator.createPack();
|
||||
addTurtleUpgrades(pack, generator.getRegistries());
|
||||
}
|
||||
|
||||
// @start region=turtle_upgrades
|
||||
private static void addTurtleUpgrades(FabricDataGenerator.Pack pack, CompletableFuture<HolderLookup.Provider> registries) {
|
||||
var fullRegistryPatch = TurtleUpgradeProvider.makeUpgradeRegistry(registries);
|
||||
pack.addProvider((FabricDataOutput output) -> new AutomaticDynamicRegistryProvider(output, fullRegistryPatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* A subclass of {@link FabricDynamicRegistryProvider} that writes all new entries.
|
||||
*/
|
||||
private static class AutomaticDynamicRegistryProvider extends FabricDynamicRegistryProvider {
|
||||
AutomaticDynamicRegistryProvider(FabricDataOutput output, CompletableFuture<RegistrySetBuilder.PatchedRegistries> registries) {
|
||||
super(output, registries.thenApply(RegistrySetBuilder.PatchedRegistries::patches));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HolderLookup.Provider registries, Entries entries) {
|
||||
for (var r : DynamicRegistries.getDynamicRegistries()) entries.addAll(registries.lookupOrThrow(r.key()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Registries";
|
||||
}
|
||||
}
|
||||
// @end region=turtle_upgrades
|
||||
}
|
16
projects/fabric/src/examples/resources/fabric.mod.json
Normal file
16
projects/fabric/src/examples/resources/fabric.mod.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "examplemod",
|
||||
"version": "1.0.0",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"com.example.examplemod.FabricExampleMod"
|
||||
],
|
||||
"fabric-datagen": [
|
||||
"com.example.examplemod.FabricExampleModDataGenerator"
|
||||
]
|
||||
},
|
||||
"depends": {
|
||||
"computercraft": "*"
|
||||
}
|
||||
}
|
@ -7,10 +7,6 @@ plugins {
|
||||
id("cc-tweaked.publishing")
|
||||
}
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
cct.inlineProject(":common-api")
|
||||
|
||||
dependencies {
|
||||
|
@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import cc.tweaked.gradle.*
|
||||
import net.neoforged.gradle.dsl.common.runs.run.Run
|
||||
import net.neoforged.moddevgradle.dsl.RunModel
|
||||
|
||||
plugins {
|
||||
id("cc-tweaked.forge")
|
||||
@ -19,106 +19,122 @@ cct {
|
||||
allProjects.forEach { externalSources(it) }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources.srcDir("src/generated/resources")
|
||||
}
|
||||
|
||||
testMod { runs { modIdentifier = "cctest" } }
|
||||
testFixtures { runs { modIdentifier = "cctest" } }
|
||||
}
|
||||
|
||||
minecraft {
|
||||
accessTransformers {
|
||||
file("src/main/resources/META-INF/accesstransformer.cfg")
|
||||
}
|
||||
}
|
||||
|
||||
runs {
|
||||
configureEach {
|
||||
systemProperty("forge.logging.markers", "REGISTRIES")
|
||||
systemProperty("forge.logging.console.level", "debug")
|
||||
|
||||
neoForge {
|
||||
val computercraft by mods.registering {
|
||||
cct.sourceDirectories.get().forEach {
|
||||
if (it.classes) modSources.add("computercraft", it.sourceSet)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
runtime(configurations["minecraftLibrary"])
|
||||
if (it.classes) sourceSet(it.sourceSet)
|
||||
}
|
||||
}
|
||||
|
||||
val client by registering {
|
||||
workingDirectory(file("run"))
|
||||
}
|
||||
|
||||
val server by registering {
|
||||
workingDirectory(file("run/server"))
|
||||
argument("--nogui")
|
||||
}
|
||||
|
||||
val data by registering {
|
||||
workingDirectory(file("run"))
|
||||
arguments.addAll(
|
||||
"--mod", "computercraft", "--all",
|
||||
"--output", layout.buildDirectory.dir("generatedResources").getAbsolutePath(),
|
||||
"--existing", project(":common").file("src/main/resources/").absolutePath,
|
||||
"--existing", file("src/main/resources/").absolutePath,
|
||||
)
|
||||
|
||||
modSources.add("computercraft", sourceSets.datagen.get())
|
||||
}
|
||||
|
||||
fun Run.configureForGameTest() {
|
||||
gameTest()
|
||||
|
||||
systemProperty("cctest.sources", project(":common").file("src/testMod/resources/data/cctest").absolutePath)
|
||||
|
||||
modSource(sourceSets.testMod.get())
|
||||
modSource(sourceSets.testFixtures.get())
|
||||
modSources.add("cctest", project(":core").sourceSets.testFixtures.get())
|
||||
|
||||
jvmArgument("-ea")
|
||||
|
||||
dependencies {
|
||||
runtime(configurations["testMinecraftLibrary"])
|
||||
val computercraftDatagen by mods.registering {
|
||||
cct.sourceDirectories.get().forEach {
|
||||
if (it.classes) sourceSet(it.sourceSet)
|
||||
}
|
||||
sourceSet(sourceSets.datagen.get())
|
||||
}
|
||||
|
||||
val gameTestServer by registering {
|
||||
workingDirectory(file("run/testServer"))
|
||||
configureForGameTest()
|
||||
val testMod by mods.registering {
|
||||
sourceSet(sourceSets.testMod.get())
|
||||
sourceSet(sourceSets.testFixtures.get())
|
||||
sourceSet(project(":core").sourceSets["testFixtures"])
|
||||
}
|
||||
|
||||
val gameTestClient by registering {
|
||||
configure(runTypes.named("client"))
|
||||
val exampleMod by mods.registering {
|
||||
sourceSet(sourceSets.examples.get())
|
||||
}
|
||||
|
||||
workingDirectory(file("run/testClient"))
|
||||
configureForGameTest()
|
||||
runs {
|
||||
configureEach {
|
||||
ideName = "Forge - ${name.capitalise()}"
|
||||
systemProperty("forge.logging.markers", "REGISTRIES")
|
||||
systemProperty("forge.logging.console.level", "debug")
|
||||
loadedMods.add(computercraft)
|
||||
}
|
||||
|
||||
systemProperties("cctest.tags", "client,common")
|
||||
register("client") {
|
||||
client()
|
||||
}
|
||||
|
||||
register("server") {
|
||||
server()
|
||||
gameDirectory = file("run/server")
|
||||
programArgument("--nogui")
|
||||
}
|
||||
|
||||
fun RunModel.configureForData(mod: String, sourceSet: SourceSet) {
|
||||
data()
|
||||
gameDirectory = file("run/run${name.capitalise()}")
|
||||
programArguments.addAll(
|
||||
"--mod", mod, "--all",
|
||||
"--output",
|
||||
layout.buildDirectory.dir(sourceSet.getTaskName("generateResources", null))
|
||||
.getAbsolutePath(),
|
||||
"--existing", project.project(":common").file("src/${sourceSet.name}/resources/").absolutePath,
|
||||
"--existing", project.file("src/${sourceSet.name}/resources/").absolutePath,
|
||||
)
|
||||
}
|
||||
|
||||
register("data") {
|
||||
configureForData("computercraft", sourceSets.main.get())
|
||||
loadedMods = listOf(computercraftDatagen.get())
|
||||
}
|
||||
|
||||
fun RunModel.configureForGameTest() {
|
||||
systemProperty(
|
||||
"cctest.sources",
|
||||
project.project(":common").file("src/testMod/resources/data/cctest").absolutePath,
|
||||
)
|
||||
|
||||
programArgument("--mixin.config=computercraft-gametest.mixins.json")
|
||||
loadedMods.add(testMod)
|
||||
|
||||
jvmArgument("-ea")
|
||||
}
|
||||
|
||||
register("testClient") {
|
||||
client()
|
||||
gameDirectory = file("run/testClient")
|
||||
configureForGameTest()
|
||||
|
||||
systemProperty("cctest.tags", "client,common")
|
||||
}
|
||||
|
||||
register("gametest") {
|
||||
type = "gameTestServer"
|
||||
configureForGameTest()
|
||||
|
||||
systemProperty("forge.logging.console.level", "info")
|
||||
systemProperty(
|
||||
"cctest.gametest-report",
|
||||
layout.buildDirectory.dir("test-results/runGametest.xml").getAbsolutePath(),
|
||||
)
|
||||
gameDirectory = file("run/gametest")
|
||||
}
|
||||
|
||||
register("exampleClient") {
|
||||
client()
|
||||
loadedMods.add(exampleMod.get())
|
||||
}
|
||||
|
||||
register("exampleData") {
|
||||
configureForData("examplemod", sourceSets.examples.get())
|
||||
loadedMods.add(exampleMod.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
val minecraftEmbed by registering {
|
||||
isCanBeResolved = false
|
||||
isCanBeConsumed = false
|
||||
}
|
||||
named("jarJar") { extendsFrom(minecraftEmbed.get()) }
|
||||
additionalRuntimeClasspath { extendsFrom(jarJar.get()) }
|
||||
|
||||
val minecraftLibrary by registering {
|
||||
isCanBeResolved = true
|
||||
isCanBeConsumed = false
|
||||
extendsFrom(minecraftEmbed.get())
|
||||
}
|
||||
runtimeOnly { extendsFrom(minecraftLibrary.get()) }
|
||||
|
||||
val testMinecraftLibrary by registering {
|
||||
val testAdditionalRuntimeClasspath by registering {
|
||||
isCanBeResolved = true
|
||||
isCanBeConsumed = false
|
||||
// Prevent ending up with multiple versions of libraries on the classpath.
|
||||
shouldResolveConsistentlyWith(minecraftLibrary.get())
|
||||
shouldResolveConsistentlyWith(additionalRuntimeClasspath.get())
|
||||
}
|
||||
|
||||
for (testConfig in listOf("testClientAdditionalRuntimeClasspath", "gametestAdditionalRuntimeClasspath")) {
|
||||
named(testConfig) { extendsFrom(testAdditionalRuntimeClasspath.get()) }
|
||||
}
|
||||
|
||||
register("testWithIris") {
|
||||
@ -136,21 +152,18 @@ dependencies {
|
||||
runtimeOnly(libs.bundles.externalMods.forge.runtime) { cct.exclude(this) }
|
||||
compileOnly(variantOf(libs.create.forge) { classifier("slim") }) { isTransitive = false }
|
||||
|
||||
implementation("net.neoforged:neoforge:${libs.versions.neoForge.get()}")
|
||||
|
||||
// Depend on our other projects.
|
||||
api(commonClasses(project(":forge-api"))) { cct.exclude(this) }
|
||||
clientApi(clientClasses(project(":forge-api"))) { cct.exclude(this) }
|
||||
implementation(project(":core")) { cct.exclude(this) }
|
||||
|
||||
"minecraftEmbed"(libs.cobalt)
|
||||
"minecraftEmbed"(libs.jzlib)
|
||||
|
||||
jarJar(libs.cobalt)
|
||||
jarJar(libs.jzlib)
|
||||
// We don't jar-in-jar our additional netty dependencies (see the tasks.jarJar configuration), but still want them
|
||||
// on the legacy classpath.
|
||||
"minecraftLibrary"(libs.netty.http)
|
||||
"minecraftLibrary"(libs.netty.socks)
|
||||
"minecraftLibrary"(libs.netty.proxy)
|
||||
additionalRuntimeClasspath(libs.netty.http) { isTransitive = false }
|
||||
additionalRuntimeClasspath(libs.netty.socks) { isTransitive = false }
|
||||
additionalRuntimeClasspath(libs.netty.proxy) { isTransitive = false }
|
||||
|
||||
testFixturesApi(libs.bundles.test)
|
||||
testFixturesApi(libs.bundles.kotlin)
|
||||
@ -163,8 +176,8 @@ dependencies {
|
||||
testModImplementation(testFixtures(project(":forge")))
|
||||
|
||||
// Ensure our test fixture dependencies are on the classpath
|
||||
"testMinecraftLibrary"(libs.bundles.kotlin)
|
||||
"testMinecraftLibrary"(libs.bundles.test)
|
||||
"testAdditionalRuntimeClasspath"(libs.bundles.kotlin)
|
||||
"testAdditionalRuntimeClasspath"(libs.bundles.test)
|
||||
|
||||
testFixturesImplementation(testFixtures(project(":core")))
|
||||
|
||||
@ -178,15 +191,12 @@ tasks.processResources {
|
||||
inputs.property("modVersion", modVersion)
|
||||
inputs.property("neoVersion", libs.versions.neoForge.get())
|
||||
|
||||
filesMatching("META-INF/neoforge.mods.toml") {
|
||||
filesMatching("META-INF/mods.toml") {
|
||||
expand(mapOf("neoVersion" to libs.versions.neoForge.get(), "file" to mapOf("jarVersion" to modVersion)))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
archiveClassifier.set("slim")
|
||||
duplicatesStrategy = DuplicatesStrategy.FAIL
|
||||
|
||||
// Include all classes from other projects except core.
|
||||
val coreSources = project(":core").sourceSets["main"]
|
||||
for (source in cct.sourceDirectories.get()) {
|
||||
@ -203,42 +213,28 @@ tasks.sourcesJar {
|
||||
for (source in cct.sourceDirectories.get()) from(source.sourceSet.allSource)
|
||||
}
|
||||
|
||||
tasks.jarJar {
|
||||
archiveClassifier.set("")
|
||||
}
|
||||
|
||||
tasks.assemble { dependsOn("jarJar") }
|
||||
|
||||
// Check tasks
|
||||
|
||||
tasks.test {
|
||||
systemProperty("cct.test-files", layout.buildDirectory.dir("tmp/testFiles").getAbsolutePath())
|
||||
}
|
||||
|
||||
val runGametest by tasks.registering(JavaExec::class) {
|
||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
description = "Runs tests on a temporary Minecraft instance."
|
||||
dependsOn("cleanRunGametest")
|
||||
val runGametest = tasks.named<JavaExec>("runGametest") {
|
||||
usesService(MinecraftRunnerService.get(gradle))
|
||||
|
||||
copyFromTask("runGameTestServer")
|
||||
|
||||
systemProperty("forge.logging.console.level", "info")
|
||||
systemProperty("cctest.gametest-report", layout.buildDirectory.dir("test-results/$name.xml").getAbsolutePath())
|
||||
}
|
||||
cct.jacoco(runGametest)
|
||||
tasks.check { dependsOn(runGametest) }
|
||||
|
||||
val runGametestClient by tasks.registering(ClientJavaExec::class) {
|
||||
description = "Runs client-side gametests with no mods"
|
||||
copyFrom("runGameTestClient")
|
||||
copyFromForge("runTestClient")
|
||||
tags("client")
|
||||
}
|
||||
cct.jacoco(runGametestClient)
|
||||
|
||||
val runGametestClientWithIris by tasks.registering(ClientJavaExec::class) {
|
||||
description = "Runs client-side gametests with Iris"
|
||||
copyFrom("runGameTestClient")
|
||||
copyFromForge("runGameTestClient")
|
||||
|
||||
tags("iris")
|
||||
classpath += configurations["testWithIris"]
|
||||
@ -256,20 +252,15 @@ tasks.register("checkClient") {
|
||||
// Upload tasks
|
||||
|
||||
modPublishing {
|
||||
output.set(tasks.jarJar)
|
||||
output.set(tasks.jar)
|
||||
}
|
||||
|
||||
// Don't publish the slim jar
|
||||
for (cfg in listOf(configurations.apiElements, configurations.runtimeElements)) {
|
||||
cfg.configure { artifacts.removeIf { it.classifier == "slim" } }
|
||||
}
|
||||
|
||||
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }
|
||||
publishing {
|
||||
publications {
|
||||
named("maven", MavenPublication::class) {
|
||||
mavenDependencies {
|
||||
cct.configureExcludes(this)
|
||||
exclude(libs.jei.forge.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
package com.example.examplemod;
|
||||
|
||||
import com.example.examplemod.peripheral.BrewingStandPeripheral;
|
||||
import dan200.computercraft.api.peripheral.PeripheralCapability;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
|
||||
import net.neoforged.neoforge.registries.RegisterEvent;
|
||||
|
||||
/**
|
||||
* The main entry point for the Forge version of our example mod.
|
||||
*/
|
||||
@Mod(ExampleMod.MOD_ID)
|
||||
public class ForgeExampleMod {
|
||||
public ForgeExampleMod(IEventBus modBus) {
|
||||
// Register our turtle upgrade. If writing a Forge-only mod, you'd normally use DeferredRegister instead.
|
||||
// However, this is an easy way to implement this in a multi-loader-compatible manner.
|
||||
|
||||
// @start region=turtle_upgrades
|
||||
modBus.addListener((RegisterEvent event) -> {
|
||||
event.register(
|
||||
ITurtleUpgrade.typeRegistry(),
|
||||
ResourceLocation.fromNamespaceAndPath(ExampleMod.MOD_ID, "example_turtle_upgrade"),
|
||||
() -> ExampleMod.EXAMPLE_TURTLE_UPGRADE
|
||||
);
|
||||
});
|
||||
// @end region=turtle_upgrades
|
||||
|
||||
modBus.addListener((FMLCommonSetupEvent event) -> ExampleMod.registerComputerCraft());
|
||||
|
||||
// @start region=peripherals
|
||||
modBus.addListener((RegisterCapabilitiesEvent event) -> {
|
||||
event.registerBlockEntity(PeripheralCapability.get(), BlockEntityType.BREWING_STAND, (b, d) -> new BrewingStandPeripheral(b));
|
||||
});
|
||||
// @end region=peripherals
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.example.examplemod;
|
||||
|
||||
import dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent;
|
||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
|
||||
/**
|
||||
* The client-side entry point for the Forge version of our example mod.
|
||||
*/
|
||||
@EventBusSubscriber(modid = ExampleMod.MOD_ID, value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD)
|
||||
public class ForgeExampleModClient {
|
||||
// @start region=turtle_modellers
|
||||
@SubscribeEvent
|
||||
public static void onRegisterTurtleModellers(RegisterTurtleModellersEvent event) {
|
||||
event.register(ExampleMod.EXAMPLE_TURTLE_UPGRADE, TurtleUpgradeModeller.flatItem());
|
||||
}
|
||||
// @end region=turtle_modellers
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.example.examplemod;
|
||||
|
||||
import com.example.examplemod.data.TurtleUpgradeProvider;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider;
|
||||
import net.neoforged.neoforge.data.event.GatherDataEvent;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Data generators for the Forge version of our example mod.
|
||||
*/
|
||||
@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD)
|
||||
public class ForgeExampleModDataGenerator {
|
||||
@SubscribeEvent
|
||||
public static void gather(GatherDataEvent event) {
|
||||
var pack = event.getGenerator().getVanillaPack(true);
|
||||
addTurtleUpgrades(pack, event.getLookupProvider());
|
||||
}
|
||||
|
||||
// @start region=turtle_upgrades
|
||||
private static void addTurtleUpgrades(DataGenerator.PackGenerator pack, CompletableFuture<HolderLookup.Provider> registries) {
|
||||
var fullRegistryPatch = TurtleUpgradeProvider.makeUpgradeRegistry(registries);
|
||||
pack.addProvider(o -> new DatapackBuiltinEntriesProvider(o, fullRegistryPatch, Set.of(ExampleMod.MOD_ID)));
|
||||
}
|
||||
// @end region=turtle_upgrades
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
modLoader="javafml"
|
||||
loaderVersion="[1,)"
|
||||
license="CC0-1.0"
|
||||
|
||||
[[mods]]
|
||||
modId="examplemod"
|
||||
version="1.0.0"
|
||||
|
||||
[[dependencies.examplemod]]
|
||||
modId="computercraft"
|
||||
mandatory=true
|
||||
versionRange="[1.0,)"
|
||||
ordering="AFTER"
|
||||
side="BOTH"
|
6
projects/forge/src/examples/resources/pack.mcmeta
Normal file
6
projects/forge/src/examples/resources/pack.mcmeta
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"pack": {
|
||||
"pack_format": 15,
|
||||
"description": "Example Mod"
|
||||
}
|
||||
}
|
@ -474,4 +474,4 @@
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,14 +8,10 @@ pluginManagement {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
|
||||
maven("https://maven.neoforged.net/releases") {
|
||||
maven("https://maven.neoforged.net") {
|
||||
name = "NeoForge"
|
||||
content {
|
||||
includeGroup("net.minecraftforge")
|
||||
includeGroup("net.neoforged")
|
||||
includeGroup("net.neoforged.gradle")
|
||||
includeModule("codechicken", "DiffPatch")
|
||||
includeModule("net.covers1624", "Quack")
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user