1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-11-04 17:16:20 +00:00

Move our public API into separate modules

This adds two new modules: common-api and forge-api, which contain the
common and Forge-specific interfaces for CC's Minecraft-specific API.

We add a new PlatformHelper interface, which abstracts over some of the
loader-specific functionality, such as reading registries[^1] or calling
Forge-specific methods. This interface is then implemented in the main
mod, and loaded via ServiceLoaders.

Some other notes on this:

 - We now split shared and client-specific source code into separate
   modules. This is to make it harder to reference client code on the
   server, thus crashing the game.

   Eventually we'll split the main mod up too into separate source sets
   - this is, of course, a much bigger problem!

 - There's currently some nastiness here due to wanting to preserve
   binary compatibility of the API. We'll hopefully be able to remove
   this when 1.19.3 releases.

 - In order to build a separate Forge-specific API jar, we compile the
   common sources twice: once for the common jar and once for the Forge
   jar.

   Getting this to play nicely with IDEs is a little tricky and so we
   provide a cct.inlineProject(...) helper to handle everything.

[^1]: We /can/ do this with vanilla's APIs, but it gives a lot of
deprecation warnings. It just ends up being nicer to abstract over it.
This commit is contained in:
Jonathan Coates 2022-11-06 15:07:13 +00:00
parent d8e2161f15
commit 76710eec9d
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
78 changed files with 956 additions and 96 deletions

View File

@ -6,9 +6,9 @@ import org.jetbrains.gradle.ext.settings
plugins { plugins {
// Build // Build
alias(libs.plugins.forgeGradle) id("net.minecraftforge.gradle")
alias(libs.plugins.mixinGradle) alias(libs.plugins.mixinGradle)
alias(libs.plugins.librarian) id("org.parchmentmc.librarian.forgegradle")
alias(libs.plugins.shadow) alias(libs.plugins.shadow)
// Publishing // Publishing
alias(libs.plugins.curseForgeGradle) alias(libs.plugins.curseForgeGradle)
@ -16,7 +16,7 @@ plugins {
alias(libs.plugins.minotaur) alias(libs.plugins.minotaur)
// Utility // Utility
alias(libs.plugins.taskTree) alias(libs.plugins.taskTree)
alias(libs.plugins.ideaExt) id("org.jetbrains.gradle.plugin.idea-ext")
id("cc-tweaked.illuaminate") id("cc-tweaked.illuaminate")
id("cc-tweaked.gametest") id("cc-tweaked.gametest")
@ -28,17 +28,20 @@ val isStable = true
val modVersion: String by extra val modVersion: String by extra
val mcVersion: String by extra val mcVersion: String by extra
val allProjects = listOf(":core-api", ":core").map { evaluationDependsOn(it) } val allProjects = listOf(":core-api", ":core", ":forge-api").map { evaluationDependsOn(it) }
cct { cct {
allProjects.forEach { externalSources(it) } allProjects.forEach { externalSources(it) }
} }
java { java {
withJavadocJar()
registerFeature("extraMods") { usingSourceSet(sourceSets.main.get()) } registerFeature("extraMods") { usingSourceSet(sourceSets.main.get()) }
} }
sourceSets { sourceSets {
// ForgeGradle adds a dep on the clientClasses task, despite forge-api coming from a separate project. Register an
// empty one.
register("client")
main { main {
resources.srcDir("src/generated/resources") resources.srcDir("src/generated/resources")
} }
@ -160,6 +163,8 @@ dependencies {
"extraModsCompileOnly"(fg.deobf("maven.modrinth:oculus:1.2.5")) "extraModsCompileOnly"(fg.deobf("maven.modrinth:oculus:1.2.5"))
implementation(project(":core")) implementation(project(":core"))
implementation(commonClasses(project(":forge-api")))
implementation(clientClasses(project(":forge-api")))
"shade"(libs.cobalt) "shade"(libs.cobalt)
"shade"(libs.netty.http) "shade"(libs.netty.http)
@ -182,19 +187,6 @@ illuaminate {
// Compile tasks // Compile tasks
tasks.javadoc {
include("dan200/computercraft/api/**/*.java")
}
val apiJar by tasks.registering(Jar::class) {
archiveClassifier.set("api")
from(sourceSets.main.get().output) {
include("dan200/computercraft/api/**/*")
}
}
tasks.assemble { dependsOn(apiJar) }
val luaJavadoc by tasks.registering(Javadoc::class) { val luaJavadoc by tasks.registering(Javadoc::class) {
description = "Generates documentation for Java-side Lua functions." description = "Generates documentation for Java-side Lua functions."
group = JavaBasePlugin.DOCUMENTATION_GROUP group = JavaBasePlugin.DOCUMENTATION_GROUP
@ -344,7 +336,6 @@ tasks.publish { dependsOn(tasks.githubRelease) }
publishing { publishing {
publications { publications {
named("maven", MavenPublication::class) { named("maven", MavenPublication::class) {
artifact(apiJar)
fg.component(this) fg.component(this)
} }
} }

View File

@ -8,10 +8,42 @@ repositories {
gradlePluginPortal() gradlePluginPortal()
} }
// Duplicated in settings.gradle.kts
repositories {
mavenCentral()
gradlePluginPortal()
maven("https://maven.minecraftforge.net") {
name = "Forge"
content {
includeGroup("net.minecraftforge")
includeGroup("net.minecraftforge.gradle")
}
}
maven("https://maven.parchmentmc.org") {
name = "Librarian"
content {
includeGroupByRegex("^org\\.parchmentmc.*")
}
}
maven("https://repo.spongepowered.org/repository/maven-public/") {
name = "Sponge"
content {
includeGroup("org.spongepowered")
}
}
}
dependencies { dependencies {
implementation(libs.errorProne.plugin) implementation(libs.errorProne.plugin)
implementation(libs.kotlin.plugin) implementation(libs.kotlin.plugin)
implementation(libs.spotless) implementation(libs.spotless)
implementation(libs.vanillaGradle)
implementation(libs.forgeGradle)
implementation(libs.librarian)
} }
gradlePlugin { gradlePlugin {

View File

@ -0,0 +1,35 @@
/** Default configuration for Forge projects. */
import cc.tweaked.gradle.CCTweakedExtension
import cc.tweaked.gradle.CCTweakedPlugin
import cc.tweaked.gradle.IdeaRunConfigurations
import cc.tweaked.gradle.MinecraftConfigurations
plugins {
id("cc-tweaked.java-convention")
id("net.minecraftforge.gradle")
id("org.parchmentmc.librarian.forgegradle")
}
plugins.apply(CCTweakedPlugin::class.java)
val mcVersion: String by extra
minecraft {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
mappings("parchment", "${libs.findVersion("parchmentMc").get()}-${libs.findVersion("parchment").get()}-$mcVersion")
accessTransformer(rootProject.file("src/main/resources/META-INF/accesstransformer.cfg"))
}
MinecraftConfigurations.setup(project)
dependencies {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
"minecraft"("net.minecraftforge:forge:$mcVersion-${libs.findVersion("forge").get()}")
}
tasks.configureEach {
// genIntellijRuns isn't registered until much later, so we need this silly hijinks.
if (name == "genIntellijRuns") doLast { IdeaRunConfigurations(project).patch() }
}

View File

@ -0,0 +1,27 @@
/** Default configuration for non-modloader-specific Minecraft projects. */
import cc.tweaked.gradle.CCTweakedExtension
import cc.tweaked.gradle.CCTweakedPlugin
import cc.tweaked.gradle.MinecraftConfigurations
plugins {
id("cc-tweaked.java-convention")
id("org.spongepowered.gradle.vanilla")
}
plugins.apply(CCTweakedPlugin::class.java)
val mcVersion: String by extra
minecraft {
version(mcVersion)
}
dependencies {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
// Depend on error prone annotations to silence a lot of compile warnings.
compileOnlyApi(libs.findLibrary("errorProne.annotations").get())
}
MinecraftConfigurations.setup(project)

View File

@ -1,21 +1,31 @@
package cc.tweaked.gradle package cc.tweaked.gradle
import net.ltgt.gradle.errorprone.CheckSeverity
import net.ltgt.gradle.errorprone.errorprone
import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.NamedDomainObjectProvider
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.attributes.TestSuiteType import org.gradle.api.attributes.TestSuiteType
import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.FileSystemOperations
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.provider.SetProperty import org.gradle.api.provider.SetProperty
import org.gradle.api.reporting.ReportingExtension import org.gradle.api.reporting.ReportingExtension
import org.gradle.api.tasks.JavaExec import org.gradle.api.tasks.JavaExec
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSetContainer import org.gradle.api.tasks.SourceSetContainer
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.configurationcache.extensions.capitalized import org.gradle.configurationcache.extensions.capitalized
import org.gradle.kotlin.dsl.get import org.gradle.kotlin.dsl.get
import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources
import org.gradle.testing.jacoco.plugins.JacocoCoverageReport import org.gradle.testing.jacoco.plugins.JacocoCoverageReport
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
import org.gradle.testing.jacoco.tasks.JacocoReport import org.gradle.testing.jacoco.tasks.JacocoReport
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.BufferedWriter import java.io.BufferedWriter
import java.io.IOException import java.io.IOException
import java.io.OutputStreamWriter import java.io.OutputStreamWriter
@ -90,6 +100,61 @@ abstract class CCTweakedExtension(
} }
} }
/**
* Add a dependency on another project such that its sources and compiles are processed with this one.
*
* This is used when importing a common library into a loader-specific one, as we want to compile sources using
* the loader-specific sources.
*/
fun inlineProject(path: String) {
val otherProject = project.evaluationDependsOn(path)
val otherJava = otherProject.extensions.getByType(JavaPluginExtension::class.java)
val main = otherJava.sourceSets.getByName("main")
val client = otherJava.sourceSets.getByName("client")
val testMod = otherJava.sourceSets.findByName("testMod")
val testFixtures = otherJava.sourceSets.findByName("testFixtures")
// Pull in sources from the other project.
extendSourceSet(otherProject, main)
extendSourceSet(otherProject, client)
if (testMod != null) extendSourceSet(otherProject, testMod)
if (testFixtures != null) extendSourceSet(otherProject, testFixtures)
// The extra source-processing tasks should include these files too.
project.tasks.named(main.javadocTaskName, Javadoc::class.java) { source(main.allJava, client.allJava) }
project.tasks.named(main.sourcesJarTaskName, Jar::class.java) { from(main.allSource, client.allSource) }
sourceDirectories.addAll(SourceSetReference.inline(main), SourceSetReference.inline(client))
}
/**
* Extend a source set with files from another project.
*
* This actually extends the original compile tasks, as extending the source sets does not play well with IDEs.
*/
private fun extendSourceSet(otherProject: Project, sourceSet: SourceSet) {
project.tasks.named(sourceSet.compileJavaTaskName, JavaCompile::class.java) {
dependsOn(otherProject.tasks.named(sourceSet.compileJavaTaskName)) // Avoid duplicate compile errors
source(sourceSet.allJava)
}
project.tasks.named(sourceSet.processResourcesTaskName, ProcessResources::class.java) {
from(sourceSet.resources)
}
// Also try to depend on Kotlin if it exists
val kotlin = otherProject.extensions.findByType(KotlinProjectExtension::class.java)
if (kotlin != null) {
val compileKotlin = sourceSet.getCompileTaskName("kotlin")
project.tasks.named(compileKotlin, KotlinCompile::class.java) {
dependsOn(otherProject.tasks.named(compileKotlin))
source(kotlin.sourceSets.getByName(sourceSet.name).kotlin)
}
}
// If we're doing an IDE sync, add a fake dependency to ensure it's on the classpath.
if (isIdeSync) project.dependencies.add(sourceSet.apiConfigurationName, sourceSet.output)
}
fun jacoco(task: NamedDomainObjectProvider<JavaExec>) { fun jacoco(task: NamedDomainObjectProvider<JavaExec>) {
val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}") val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}")
val reportTaskName = "jacoco${task.name.capitalized()}Report" val reportTaskName = "jacoco${task.name.capitalized()}Report"
@ -146,5 +211,8 @@ abstract class CCTweakedExtension(
} }
} }
} }
internal val isIdeSync: Boolean
get() = java.lang.Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"))
} }
} }

View File

@ -18,3 +18,14 @@ fun JavaExec.copyToFull(spec: JavaExec) {
spec.javaLauncher.set(javaLauncher) spec.javaLauncher.set(javaLauncher)
spec.args = args spec.args = args
} }
/**
* An alternative to [Nothing] with a more descriptive name. Use to enforce calling a function with named arguments:
*
* ```kotlin
* fun f(vararg unused: UseNamedArgs, arg1: Int, arg2: Int) {
* // ...
* }
* ```
*/
class UseNamedArgs private constructor()

View File

@ -0,0 +1,170 @@
package cc.tweaked.gradle
import org.gradle.api.Project
import org.gradle.api.logging.Logging
import org.w3c.dom.Attr
import org.w3c.dom.Document
import org.w3c.dom.Node
import org.xml.sax.InputSource
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactory
/**
* Patches up run configurations from ForgeGradle and Loom.
*
* Would be good to PR some (or all) of these changes upstream at some point.
*
* @see net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask
* @see net.minecraftforge.gradle.common.util.runs.IntellijRunGenerator
*/
internal class IdeaRunConfigurations(project: Project) {
private val rootProject = project.rootProject
private val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
private val xpath = XPathFactory.newInstance().newXPath()
private val writer = TransformerFactory.newInstance().newTransformer()
private val ideaDir = rootProject.file(".idea/")
private val buildDir: Lazy<String?> = lazy {
val ideaMisc = ideaDir.resolve("misc.xml")
try {
val doc = Files.newBufferedReader(ideaMisc.toPath(), StandardCharsets.UTF_8).use {
documentBuilder.parse(InputSource(it))
}
val node =
xpath.evaluate("//component[@name=\"ProjectRootManager\"]/output", doc, XPathConstants.NODE) as Node
val attr = node.attributes.getNamedItem("url") as Attr
attr.value.removePrefix("file://")
} catch (e: Exception) {
LOGGER.error("Failed to find root directory", e)
null
}
}
fun patch() = synchronized(LOCK) {
val runConfigDir = ideaDir.resolve("runConfigurations")
if (!runConfigDir.isDirectory) return
Files.list(runConfigDir.toPath()).use {
for (configuration in it) {
val filename = configuration.fileName.toString();
when {
filename.endsWith("_fabric.xml") -> patchFabric(configuration)
filename.startsWith("forge_") && filename.endsWith(".xml") -> patchForge(configuration)
else -> {}
}
}
}
}
private fun patchFabric(path: Path) = withXml(path) {
setXml("//configuration", "folderName") { "Fabric" }
}
private fun patchForge(path: Path) = withXml(path) {
val configId = path.fileName.toString().removePrefix("forge_").removeSuffix(".xml")
val sourceSet = forgeConfigs[configId]
if (sourceSet == null) {
LOGGER.error("[{}] Cannot map run configuration to a known source set", path)
return@withXml
}
setXml("//configuration", "folderName") { "Forge" }
setXml("//configuration/module", "name") { "${rootProject.name}.forge.$sourceSet" }
if (buildDir.value == null) return@withXml
setXml("//configuration/envs/env[@name=\"MOD_CLASSES\"]", "value") { classpath ->
val classes = classpath!!.split(':')
val newClasses = mutableListOf<String>()
fun appendUnique(x: String) {
if (!newClasses.contains(x)) newClasses.add(x)
}
for (entry in classes) {
if (!entry.contains("/out/")) {
appendUnique(entry)
continue
}
val match = CLASSPATH_ENTRY.matchEntire(entry)
if (match != null) {
val modId = match.groups["modId"]!!.value
val proj = match.groups["proj"]!!.value
var component = match.groups["component"]!!.value
if (component == "production") component = "main"
appendUnique(forgeModEntry(modId, proj, component))
} else {
LOGGER.warn("[{}] Unknown classpath entry {}", path, entry)
appendUnique(entry)
}
}
// Ensure common code is on the classpath
for (proj in listOf("common", "common-api")) {
for (component in listOf("main", "client")) {
appendUnique(forgeModEntry("computercraft", proj, component))
}
}
if (newClasses.any { it.startsWith("cctest%%") }) {
appendUnique(forgeModEntry("cctest", "core", "testFixtures"))
appendUnique(forgeModEntry("cctest", "common", "testMod"))
}
newClasses.joinToString(":")
}
}
private fun forgeModEntry(mod: String, project: String, component: String) =
"$mod%%${buildDir.value}/production/${rootProject.name}.$project.$component"
private fun LocatedDocument.setXml(xpath: String, attribute: String, value: (String?) -> String) {
val node = this@IdeaRunConfigurations.xpath.evaluate(xpath, document, XPathConstants.NODE) as Node?
if (node == null) {
LOGGER.error("[{}] Cannot find {}", path.fileName, xpath)
return
}
val attr = node.attributes.getNamedItem(attribute) as Attr? ?: document.createAttribute(attribute)
val oldValue = attr.value
attr.value = value(attr.value)
node.attributes.setNamedItem(attr)
if (oldValue != attr.value) {
LOGGER.info("[{}] Setting {}@{}:\n Old: {}\n New: {}", path.fileName, xpath, attribute, oldValue, attr.value)
}
}
private fun withXml(path: Path, run: LocatedDocument.() -> Unit) {
val doc = Files.newBufferedReader(path).use { documentBuilder.parse(InputSource(it)) }
run(LocatedDocument(path, doc))
Files.newBufferedWriter(path).use { writer.transform(DOMSource(doc), StreamResult(it)) }
}
private class LocatedDocument(val path: Path, val document: Document)
companion object {
private val LOGGER = Logging.getLogger(IdeaRunConfigurations::class.java)
private val LOCK = Any()
private val CLASSPATH_ENTRY =
Regex("(?<modId>[a-z]+)%%\\\$PROJECT_DIR\\\$/projects/(?<proj>[a-z-]+)/out/(?<component>\\w+)/(?<type>[a-z]+)\$")
private val forgeConfigs = mapOf(
"runClient" to "client",
"runData" to "main",
"runGameTestServer" to "testMod",
"runServer" to "main",
"runTestClient" to "testMod",
)
}
}

View File

@ -0,0 +1,189 @@
package cc.tweaked.gradle
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.attributes.Bundling
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.attributes.Usage
import org.gradle.api.attributes.java.TargetJvmVersion
import org.gradle.api.capabilities.Capability
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.named
/**
* This sets up a separate client-only source set, and extends that and the main/common source set with additional
* metadata, to make it easier to consume jars downstream.
*/
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]
/**
* Performs the initial setup of our configurations.
*/
private fun setup() {
// Define a client source set.
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])
}
// Set up an API configuration for clients (to ensure it's consistent with the main source set).
val clientApi = configurations.maybeCreate(client.apiConfigurationName).apply {
isVisible = false
isCanBeConsumed = false
isCanBeResolved = false
}
configurations.named(client.implementationConfigurationName) { extendsFrom(clientApi) }
/*
Now add outgoing variants for the main and common source sets that we can consume downstream. This is possibly
the worst way to do things, but unfortunately the alternatives don't actually work very well:
- Just using source set outputs: This means dependencies don't propagate, which means when :fabric depends
on :fabric-api, we don't inherit the fake :common-api in IDEA.
- Having separate common/main jars: Nice in principle, but unfortunately Forge needs a separate deobf jar
task (as the original jar is obfuscated), and IDEA is not able to map its output back to a source set.
This works for now, but is incredibly brittle. It's part of the reason we can't use testFixtures inside our
MC projects, as that adds a project(self) -> test dependency, which would pull in the jar instead.
Note we register a fake client jar here. It's not actually needed, but is there to make sure IDEA has
a way to tell that client classes are needed at runtime.
I'm so sorry, deeply aware how cursed this is.
*/
setupOutgoing(main, "CommonOnly")
project.tasks.register(client.jarTaskName, Jar::class.java) {
description = "An empty jar standing in for the client classes."
group = BasePlugin.BUILD_GROUP
archiveClassifier.set("client")
}
setupOutgoing(client)
// Reset the client classpath (Loom configures it slightly differently to this) and add a main -> client
// dependency. Here we /can/ use source set outputs as we add transitive deps by patching the classpath. Nasty,
// but avoids accidentally pulling in Forge's obfuscated jar.
client.compileClasspath = client.compileClasspath + main.compileClasspath
client.runtimeClasspath = client.runtimeClasspath + main.runtimeClasspath
project.dependencies.add(client.apiConfigurationName, main.output)
// Also add client classes to the test classpath. We do the same nasty tricks as needed for main -> client.
test.compileClasspath += client.compileClasspath
test.runtimeClasspath += client.runtimeClasspath
project.dependencies.add(test.implementationConfigurationName, client.output)
// Configure some tasks to include our additional files.
project.tasks.named("javadoc", Javadoc::class.java) {
source += client.allJava
classpath = main.compileClasspath + main.output + client.compileClasspath + client.output
}
// This are already done by Fabric, but we need it for Forge and vanilla. It shouldn't conflict at all.
project.tasks.named("jar", Jar::class.java) { from(client.output) }
project.tasks.named("sourcesJar", Jar::class.java) { from(client.allSource) }
project.extensions.configure(CCTweakedExtension::class.java) {
sourceDirectories.add(SourceSetReference.internal(client))
}
}
private fun setupOutgoing(sourceSet: SourceSet, suffix: String = "") {
setupOutgoing("${sourceSet.apiElementsConfigurationName}$suffix", sourceSet, objects.named(Usage.JAVA_API)) {
description = "API elements for ${sourceSet.name}"
extendsFrom(configurations[sourceSet.apiConfigurationName])
}
setupOutgoing("${sourceSet.runtimeElementsConfigurationName}$suffix", sourceSet, objects.named(Usage.JAVA_RUNTIME)) {
description = "Runtime elements for ${sourceSet.name}"
extendsFrom(configurations[sourceSet.implementationConfigurationName], configurations[sourceSet.runtimeOnlyConfigurationName])
}
}
/**
* Set up an outgoing configuration for a specific source set. We set an additional "main" or "client" capability
* (depending on the source set name) which allows downstream projects to consume them separately (see
* [DependencyHandler.commonClasses] and [DependencyHandler.clientClasses]).
*/
private fun setupOutgoing(name: String, sourceSet: SourceSet, usage: Usage, configure: Configuration.() -> Unit) {
configurations.register(name) {
isVisible = false
isCanBeConsumed = true
isCanBeResolved = false
configure(this)
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
attribute(Usage.USAGE_ATTRIBUTE, usage)
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
attributeProvider(
TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE,
java.toolchain.languageVersion.map { it.asInt() },
)
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
}
outgoing {
capability(BasicOutgoingCapability(project, sourceSet.name))
// We have two outgoing variants here: the original jar and the classes.
artifact(project.tasks.named(sourceSet.jarTaskName))
variants.create("classes") {
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES))
sourceSet.output.classesDirs.forEach { artifact(it) { builtBy(sourceSet.output) } }
}
}
}
}
companion object {
fun setup(project: Project) {
MinecraftConfigurations(project).setup()
}
}
}
private class BasicIncomingCapability(private val module: ModuleDependency, private val name: String) : Capability {
override fun getGroup(): String = module.group!!
override fun getName(): String = "${module.name}-$name"
override fun getVersion(): String? = null
}
private class BasicOutgoingCapability(private val project: Project, private val name: String) : Capability {
override fun getGroup(): String = project.group.toString()
override fun getName(): String = "${project.name}-$name"
override fun getVersion(): String = project.version.toString()
}
fun DependencyHandler.clientClasses(notation: Any): ModuleDependency {
val dep = create(notation) as ModuleDependency
dep.capabilities { requireCapability(BasicIncomingCapability(dep, "client")) }
return dep
}
fun DependencyHandler.commonClasses(notation: Any): ModuleDependency {
val dep = create(notation) as ModuleDependency
dep.capabilities { requireCapability(BasicIncomingCapability(dep, "main")) }
return dep
}

View File

@ -43,6 +43,7 @@ nullAway = "0.9.9"
shadow = "7.1.2" shadow = "7.1.2"
spotless = "6.8.0" spotless = "6.8.0"
taskTree = "2.1.0" taskTree = "2.1.0"
vanillaGradle = "0.2.1-SNAPSHOT"
[libraries] [libraries]
asm = { module = "org.ow2.asm:asm", version.ref = "asm" } asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
@ -74,9 +75,12 @@ errorProne-api = { module = "com.google.errorprone:error_prone_check_api", versi
errorProne-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorProne-core" } errorProne-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorProne-core" }
errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" } errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" }
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" } errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" } nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
vanillaGradle = { module = "org.spongepowered:vanillagradle", version.ref = "vanillaGradle" }
[plugins] [plugins]
curseForgeGradle = { id = "net.darkhax.curseforgegradle", version.ref = "curseForgeGradle" } curseForgeGradle = { id = "net.darkhax.curseforgegradle", version.ref = "curseForgeGradle" }

View File

@ -0,0 +1,18 @@
plugins {
id("cc-tweaked.java-convention")
id("cc-tweaked.publishing")
id("cc-tweaked.vanilla")
}
java {
withJavadocJar()
}
dependencies {
api(project(":core-api"))
compileOnly(project(":forge-stubs"))
}
tasks.javadoc {
include("dan200/computercraft/api/**/*.java")
}

View File

@ -9,7 +9,6 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.impl.client.ComputerCraftAPIClientService; import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -20,7 +19,8 @@ public final class ComputerCraftAPIClient {
/** /**
* Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades. * Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
* <p> * <p>
* This may be called at any point after registry creation, though it is recommended to call it within {@link FMLClientSetupEvent}. * This may be called at any point after registry creation, though it is recommended to call it within your client
* setup step.
* *
* @param serialiser The turtle upgrade serialiser. * @param serialiser The turtle upgrade serialiser.
* @param modeller The upgrade modeller. * @param modeller The upgrade modeller.

View File

@ -6,6 +6,7 @@
package dan200.computercraft.api.client; package dan200.computercraft.api.client;
import com.mojang.math.Transformation; import com.mojang.math.Transformation;
import dan200.computercraft.impl.client.ClientPlatformHelper;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.model.ModelResourceLocation;
@ -39,7 +40,7 @@ public final class TransformedModel {
public static TransformedModel of(@Nonnull ResourceLocation location) { public static TransformedModel of(@Nonnull ResourceLocation location) {
var modelManager = Minecraft.getInstance().getModelManager(); var modelManager = Minecraft.getInstance().getModelManager();
return new TransformedModel(modelManager.getModel(location)); return new TransformedModel(ClientPlatformHelper.get().getModel(modelManager, location));
} }
public static TransformedModel of(@Nonnull ItemStack item, @Nonnull Transformation transform) { public static TransformedModel of(@Nonnull ItemStack item, @Nonnull Transformation transform) {

View File

@ -11,17 +11,22 @@ import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import java.nio.FloatBuffer;
class TurtleUpgradeModellers { class TurtleUpgradeModellers {
private static final Transformation leftTransform = getMatrixFor(-0.40625f); private static final Transformation leftTransform = getMatrixFor(-0.40625f);
private static final Transformation rightTransform = getMatrixFor(0.40625f); private static final Transformation rightTransform = getMatrixFor(0.40625f);
private static Transformation getMatrixFor(float offset) { private static Transformation getMatrixFor(float offset) {
return new Transformation(new Matrix4f(new float[]{ var matrix = new Matrix4f();
matrix.load(FloatBuffer.wrap(new float[]{
0.0f, 0.0f, -1.0f, 1.0f + offset, 0.0f, 0.0f, -1.0f, 1.0f + offset,
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
})); }));
matrix.transpose();
return new Transformation(matrix);
} }
static final TurtleUpgradeModeller<ITurtleUpgrade> FLAT_ITEM = (upgrade, turtle, side) -> static final TurtleUpgradeModeller<ITurtleUpgrade> FLAT_ITEM = (upgrade, turtle, side) ->

View File

@ -0,0 +1,46 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.impl.client;
import dan200.computercraft.impl.Services;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable;
@ApiStatus.Internal
public interface ClientPlatformHelper {
/**
* Equivalent to {@link ModelManager#getModel(ModelResourceLocation)} but for arbitrary {@link ResourceLocation}s.
*
* @param manager The model manager.
* @param location The model location.
* @return The baked model.
*/
BakedModel getModel(ModelManager manager, ResourceLocation location);
static ClientPlatformHelper get() {
var instance = Instance.INSTANCE;
return instance == null ? Services.raise(ClientPlatformHelper.class, Instance.ERROR) : instance;
}
final class Instance {
static final @Nullable ClientPlatformHelper INSTANCE;
static final @Nullable Throwable ERROR;
static {
var helper = Services.tryLoad(ClientPlatformHelper.class);
INSTANCE = helper.instance();
ERROR = helper.error();
}
private Instance() {
}
}
}

View File

@ -30,7 +30,6 @@ import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -118,7 +117,7 @@ public final class ComputerCraftAPI {
* @param provider The peripheral provider to register. * @param provider The peripheral provider to register.
* @see IPeripheral * @see IPeripheral
* @see IPeripheralProvider * @see IPeripheralProvider
* @deprecated Use {@link ForgeComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)} instead. * @deprecated Use {@code dan200.computercraft.api.ForgeComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)} instead.
*/ */
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
public static void registerPeripheralProvider(@Nonnull IPeripheralProvider provider) { public static void registerPeripheralProvider(@Nonnull IPeripheralProvider provider) {
@ -140,7 +139,7 @@ public final class ComputerCraftAPI {
* *
* @param capability The capability to register. * @param capability The capability to register.
* @see GenericSource * @see GenericSource
* @deprecated Use {@link ForgeComputerCraftAPI} instead. * @deprecated Use {@code dan200.computercraft.api.ForgeComputerCraftAPI} instead.
*/ */
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
public static void registerGenericCapability(@Nonnull Capability<?> capability) { public static void registerGenericCapability(@Nonnull Capability<?> capability) {
@ -199,7 +198,7 @@ public final class ComputerCraftAPI {
* such as {@code turtle.getItemDetail()} or {@code turtle.inspect()}. * such as {@code turtle.getItemDetail()} or {@code turtle.inspect()}.
* *
* @param type The type of object that this provider can provide details for. Should be {@link BlockReference}, * @param type The type of object that this provider can provide details for. Should be {@link BlockReference},
* {@link FluidStack} or {@link ItemStack}. * {@code net.minecraftforge.fluids.FluidStack} or {@link ItemStack}.
* @param provider The detail provider to register. * @param provider The detail provider to register.
* @param <T> The type of object that this provider can provide details for. * @param <T> The type of object that this provider can provide details for.
* @deprecated Use {@link DetailRegistry#addProvider(IDetailProvider)} to register your provider. * @deprecated Use {@link DetailRegistry#addProvider(IDetailProvider)} to register your provider.
@ -229,7 +228,7 @@ public final class ComputerCraftAPI {
* @param side The side to extract the network element from * @param side The side to extract the network element from
* @return The element's node * @return The element's node
* @see IWiredElement#getNode() * @see IWiredElement#getNode()
* @deprecated Use {@link ForgeComputerCraftAPI#getWiredElementAt(BlockGetter, BlockPos, Direction)} * @deprecated Use {@code dan200.computercraft.api.ForgeComputerCraftAPI#getWiredElementAt(BlockGetter, BlockPos, Direction)}
*/ */
@Nonnull @Nonnull
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)

View File

@ -5,10 +5,8 @@
*/ */
package dan200.computercraft.api; package dan200.computercraft.api;
import dan200.computercraft.ComputerCraft; import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
@ -24,7 +22,7 @@ public class ComputerCraftTags {
public static final TagKey<Item> MONITOR = make("monitor"); public static final TagKey<Item> MONITOR = make("monitor");
private static TagKey<Item> make(String name) { private static TagKey<Item> make(String name) {
return ItemTags.create(new ResourceLocation(ComputerCraft.MOD_ID, name)); return TagKey.create(Registry.ITEM_REGISTRY, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
} }
} }
@ -55,7 +53,7 @@ public class ComputerCraftTags {
public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable"); public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
private static TagKey<Block> make(String name) { private static TagKey<Block> make(String name) {
return BlockTags.create(new ResourceLocation(ComputerCraft.MOD_ID, name)); return TagKey.create(Registry.BLOCK_REGISTRY, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
} }
} }
} }

View File

@ -5,7 +5,6 @@
*/ */
package dan200.computercraft.api.peripheral; package dan200.computercraft.api.peripheral;
import dan200.computercraft.api.ForgeComputerCraftAPI;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@ -19,11 +18,14 @@ import javax.annotation.Nonnull;
* <p> * <p>
* If you have a {@link BlockEntity} which acts as a peripheral, you may alternatively expose the {@link IPeripheral} * If you have a {@link BlockEntity} which acts as a peripheral, you may alternatively expose the {@link IPeripheral}
* capability. * capability.
* * <p>
* @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider) * {@code dan200.computercraft.api.ForgeComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)} should be used
* to register a peripheral provider.
*/ */
@FunctionalInterface @FunctionalInterface
public interface IPeripheralProvider { public interface IPeripheralProvider {
// TODO(1.19.3): Move to Forge and fix link above.
/** /**
* Produce an peripheral implementation from a block location. * Produce an peripheral implementation from a block location.
* *
@ -31,7 +33,6 @@ public interface IPeripheralProvider {
* @param pos The position the block is at. * @param pos The position the block is at.
* @param side The side to get the peripheral from. * @param side The side to get the peripheral from.
* @return A peripheral, or {@link LazyOptional#empty()} if there is not a peripheral here you'd like to handle. * @return A peripheral, or {@link LazyOptional#empty()} if there is not a peripheral here you'd like to handle.
* @see ForgeComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)
*/ */
@Nonnull @Nonnull
LazyOptional<IPeripheral> getPeripheral(@Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side); LazyOptional<IPeripheral> getPeripheral(@Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side);

View File

@ -7,7 +7,6 @@ package dan200.computercraft.api.pocket;
import dan200.computercraft.api.upgrades.UpgradeDataProvider; import dan200.computercraft.api.upgrades.UpgradeDataProvider;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
import net.minecraftforge.data.event.GatherDataEvent;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -18,7 +17,6 @@ import java.util.function.Consumer;
* This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function, * This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function,
* construct each upgrade, and pass them off to the provided consumer to generate them. * construct each upgrade, and pass them off to the provided consumer to generate them.
* *
* @see GatherDataEvent To register your data provider
* @see PocketUpgradeSerialiser * @see PocketUpgradeSerialiser
*/ */
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>> { public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>> {

View File

@ -5,17 +5,16 @@
*/ */
package dan200.computercraft.api.pocket; package dan200.computercraft.api.pocket;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.upgrades.IUpgradeBase; import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser; import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem; import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.internal.upgrades.SimpleSerialiser; import dan200.computercraft.impl.upgrades.SimpleSerialiser;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer; import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.RegistryManager; import net.minecraftforge.registries.RegistryManager;
@ -36,11 +35,8 @@ import java.util.function.Function;
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> { public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> {
/** /**
* The ID for the associated registry. * The ID for the associated registry.
* <p>
* This is largely intended for use with Forge Registry methods/classes, such as {@link DeferredRegister} and
* {@link RegistryManager#getRegistry(ResourceKey)}.
*/ */
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraft.MOD_ID, "pocket_upgrade_serialiser")); ResourceKey<Registry<PocketUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser"));
/** /**
* The associated registry. * The associated registry.

View File

@ -8,8 +8,6 @@ package dan200.computercraft.api.turtle;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.IUpgradeBase; import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.event.level.BlockEvent;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -58,8 +56,8 @@ public interface ITurtleUpgrade extends IUpgradeBase {
* Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called * Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called
* by the turtle, and the tool is required to do some work. * by the turtle, and the tool is required to do some work.
* <p> * <p>
* Conforming implementations should fire {@link BlockEvent.BreakEvent} for digging {@link AttackEntityEvent} * Conforming implementations should fire loader-specific events when using the tool, for instance Forge's
* for attacking. * {@code AttackEntityEvent}.
* *
* @param turtle Access to the turtle that the tool resides on. * @param turtle Access to the turtle that the tool resides on.
* @param side Which side of the turtle (left or right) the tool resides on. * @param side Which side of the turtle (left or right) the tool resides on.

View File

@ -8,14 +8,14 @@ package dan200.computercraft.api.turtle;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.ComputerCraftTags; import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.upgrades.UpgradeDataProvider; import dan200.computercraft.api.upgrades.UpgradeDataProvider;
import dan200.computercraft.impl.PlatformHelper;
import net.minecraft.core.Registry;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraftforge.data.event.GatherDataEvent;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -26,7 +26,6 @@ import java.util.function.Consumer;
* This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function, * This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function,
* construct each upgrade, and pass them off to the provided consumer to generate them. * construct each upgrade, and pass them off to the provided consumer to generate them.
* *
* @see GatherDataEvent To register your data provider
* @see TurtleUpgradeSerialiser * @see TurtleUpgradeSerialiser
*/ */
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>> { public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>> {
@ -128,10 +127,10 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
*/ */
public void add(@Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add) { public void add(@Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add) {
add.accept(new Upgrade<>(id, serialiser, s -> { add.accept(new Upgrade<>(id, serialiser, s -> {
s.addProperty("item", ForgeRegistries.ITEMS.getKey(toolItem).toString()); s.addProperty("item", PlatformHelper.get().getRegistryKey(Registry.ITEM_REGISTRY, toolItem).toString());
if (adjective != null) s.addProperty("adjective", adjective); if (adjective != null) s.addProperty("adjective", adjective);
if (craftingItem != null) { if (craftingItem != null) {
s.addProperty("craftItem", ForgeRegistries.ITEMS.getKey(craftingItem).toString()); s.addProperty("craftItem", PlatformHelper.get().getRegistryKey(Registry.ITEM_REGISTRY, craftingItem).toString());
} }
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier); if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
if (breakable != null) s.addProperty("breakable", breakable.location().toString()); if (breakable != null) s.addProperty("breakable", breakable.location().toString());

View File

@ -5,20 +5,17 @@
*/ */
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.client.ComputerCraftAPIClient;
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.api.upgrades.IUpgradeBase; import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser; import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem; import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.internal.upgrades.SimpleSerialiser; import dan200.computercraft.impl.upgrades.SimpleSerialiser;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer; import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.RegistryManager; import net.minecraftforge.registries.RegistryManager;
@ -29,13 +26,12 @@ import java.util.function.Function;
/** /**
* Reads a {@link ITurtleUpgrade} from disk and reads/writes it to a network packet. * Reads a {@link ITurtleUpgrade} from disk and reads/writes it to a network packet.
* <p> * <p>
* These should be registered in a {@link IForgeRegistry} while the game is loading, much like {@link RecipeSerializer}s. * These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s.
* It is suggested you use a {@link DeferredRegister}.
* <p> * <p>
* If your turtle upgrade doesn't have any associated configurable parameters (like most upgrades), you can use * If your turtle upgrade doesn't have any associated configurable parameters (like most upgrades), you can use
* {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser. * {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser.
* *
* <h2>Example</h2> * <h2>Example (Forge)</h2>
* <pre>{@code * <pre>{@code
* static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" ); * static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
* *
@ -57,7 +53,7 @@ import java.util.function.Function;
* }</pre> * }</pre>
* <p> * <p>
* Finally, we need to register a model for our upgrade. This is done with * Finally, we need to register a model for our upgrade. This is done with
* {@link ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller)}: * {@link dan200.computercraft.api.client.ComputerCraftAPIClient#registerTurtleUpgradeModeller}:
* *
* <pre>{@code * <pre>{@code
* // Register our model inside FMLClientSetupEvent * // Register our model inside FMLClientSetupEvent
@ -69,16 +65,13 @@ import java.util.function.Function;
* @param <T> The type of turtle upgrade this is responsible for serialising. * @param <T> The type of turtle upgrade this is responsible for serialising.
* @see ITurtleUpgrade * @see ITurtleUpgrade
* @see TurtleUpgradeDataProvider * @see TurtleUpgradeDataProvider
* @see TurtleUpgradeModeller * @see dan200.computercraft.api.client.turtle.TurtleUpgradeModeller
*/ */
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> { public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> {
/** /**
* The ID for the associated registry. * The ID for the associated registry.
* <p>
* This is largely intended for use with Forge Registry methods/classes, such as {@link DeferredRegister} and
* {@link RegistryManager#getRegistry(ResourceKey)}.
*/ */
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraft.MOD_ID, "turtle_upgrade_serialiser")); ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser"));
/** /**
* The associated registry. * The associated registry.

View File

@ -7,6 +7,7 @@ package dan200.computercraft.api.upgrades;
import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.impl.PlatformHelper;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@ -62,20 +63,20 @@ public interface IUpgradeBase {
* <p> * <p>
* The default check requires that any non-capability NBT is exactly the same as the * The default check requires that any non-capability NBT is exactly the same as the
* crafting item, but this may be relaxed for your upgrade. * crafting item, but this may be relaxed for your upgrade.
* <p>
* This is based on {@code net.minecraftforge.common.crafting.StrictNBTIngredient}'s check.
* *
* @param stack The stack to check. This is guaranteed to be non-empty and have the same item as * @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
* {@link #getCraftingItem()}. * {@link #getCraftingItem()}.
* @return If this stack may be used to equip this upgrade. * @return If this stack may be used to equip this upgrade.
* @see net.minecraftforge.common.crafting.StrictNBTIngredient#test(ItemStack) For the implementation of the default
* check.
*/ */
default boolean isItemSuitable(@Nonnull ItemStack stack) { default boolean isItemSuitable(@Nonnull ItemStack stack) {
var crafting = getCraftingItem(); var crafting = getCraftingItem();
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a // A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
// null one. // null one.
var shareTag = stack.getItem().getShareTag(stack); var shareTag = PlatformHelper.get().getShareTag(stack);
var craftingShareTag = crafting.getItem().getShareTag(crafting); var craftingShareTag = PlatformHelper.get().getShareTag(crafting);
if (shareTag == craftingShareTag) return true; if (shareTag == craftingShareTag) return true;
if (shareTag == null) return craftingShareTag.isEmpty(); if (shareTag == null) return craftingShareTag.isEmpty();
if (craftingShareTag == null) return shareTag.isEmpty(); if (craftingShareTag == null) return shareTag.isEmpty();

View File

@ -8,8 +8,9 @@ package dan200.computercraft.api.upgrades;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem; import dan200.computercraft.impl.PlatformHelper;
import dan200.computercraft.internal.upgrades.SimpleSerialiser; import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.data.CachedOutput; import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
@ -17,14 +18,15 @@ import net.minecraft.data.DataProvider;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryManager;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -84,7 +86,7 @@ public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends Upgr
} }
return new Upgrade<>(id, serialiser, s -> return new Upgrade<>(id, serialiser, s ->
s.addProperty("item", Objects.requireNonNull(ForgeRegistries.ITEMS.getKey(item), "Item is not registered").toString()) s.addProperty("item", PlatformHelper.get().getRegistryKey(Registry.ITEM_REGISTRY, item).toString())
); );
} }
@ -104,7 +106,6 @@ public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends Upgr
@Override @Override
public final void run(@Nonnull CachedOutput cache) throws IOException { public final void run(@Nonnull CachedOutput cache) throws IOException {
var registry = RegistryManager.ACTIVE.getRegistry(this.registry);
var base = generator.getOutputFolder().resolve("data"); var base = generator.getOutputFolder().resolve("data");
Set<ResourceLocation> seen = new HashSet<>(); Set<ResourceLocation> seen = new HashSet<>();
@ -113,7 +114,7 @@ public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends Upgr
if (!seen.add(upgrade.id())) throw new IllegalStateException("Duplicate upgrade " + upgrade.id()); if (!seen.add(upgrade.id())) throw new IllegalStateException("Duplicate upgrade " + upgrade.id());
var json = new JsonObject(); var json = new JsonObject();
json.addProperty("type", Objects.requireNonNull(registry.getKey(upgrade.serialiser()), "Serialiser has not been registered").toString()); json.addProperty("type", PlatformHelper.get().getRegistryKey(registry, upgrade.serialiser()).toString());
upgrade.serialise().accept(json); upgrade.serialise().accept(json);
try { try {
@ -141,7 +142,7 @@ public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends Upgr
@Nonnull @Nonnull
public final R existingSerialiser(@Nonnull ResourceLocation id) { public final R existingSerialiser(@Nonnull ResourceLocation id) {
var result = RegistryManager.ACTIVE.getRegistry(registry).getValue(id); var result = PlatformHelper.get().getRegistryObject(registry, id);
if (result == null) throw new IllegalArgumentException("No such serialiser " + registry); if (result == null) throw new IllegalArgumentException("No such serialiser " + registry);
return result; return result;
} }

View File

@ -27,7 +27,6 @@ import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -56,10 +55,12 @@ public interface ComputerCraftAPIService {
@Nullable @Nullable
IMount createResourceMount(@Nonnull String domain, @Nonnull String subPath); IMount createResourceMount(@Nonnull String domain, @Nonnull String subPath);
@Deprecated
void registerPeripheralProvider(@Nonnull IPeripheralProvider provider); void registerPeripheralProvider(@Nonnull IPeripheralProvider provider);
void registerGenericSource(@Nonnull GenericSource source); void registerGenericSource(@Nonnull GenericSource source);
@Deprecated
void registerGenericCapability(@Nonnull Capability<?> capability); void registerGenericCapability(@Nonnull Capability<?> capability);
void registerBundledRedstoneProvider(@Nonnull IBundledRedstoneProvider provider); void registerBundledRedstoneProvider(@Nonnull IBundledRedstoneProvider provider);
@ -88,8 +89,6 @@ public interface ComputerCraftAPIService {
DetailRegistry<BlockReference> getBlockInWorldDetailRegistry(); DetailRegistry<BlockReference> getBlockInWorldDetailRegistry();
DetailRegistry<FluidStack> getFluidStackDetailRegistry();
final class Instance { final class Instance {
static final @Nullable ComputerCraftAPIService INSTANCE; static final @Nullable ComputerCraftAPIService INSTANCE;
static final @Nullable Throwable ERROR; static final @Nullable Throwable ERROR;

View File

@ -0,0 +1,82 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.impl;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable;
/**
* Abstraction layer for Forge and Fabric. See implementations for more details.
* <p>
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
*/
@ApiStatus.Internal
public interface PlatformHelper {
/**
* Get the current {@link PlatformHelper} instance.
*
* @return The current instance.
*/
static PlatformHelper get() {
var instance = Instance.INSTANCE;
return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance;
}
/**
* Get the unique ID for a registered object.
*
* @param registry The registry to look up this object in.
* @param object The object to look up.
* @param <T> The type of object the registry stores.
* @return The registered object's ID.
* @throws IllegalArgumentException If the registry or object are not registered.
*/
<T> ResourceLocation getRegistryKey(ResourceKey<Registry<T>> registry, T object);
/**
* Look up an ID in a registry, returning the registered object.
*
* @param registry The registry to look up this object in.
* @param id The ID to look up.
* @param <T> The type of object the registry stores.
* @return The resolved registry object.
* @throws IllegalArgumentException If the registry or object are not registered.
*/
<T> T getRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id);
/**
* Get the subset of an {@link ItemStack}'s {@linkplain ItemStack#getTag() tag} which is synced to the client.
*
* @param item The stack.
* @return The item's tag.
*/
@Nullable
default CompoundTag getShareTag(ItemStack item) {
return item.getTag();
}
final class Instance {
static final @Nullable PlatformHelper INSTANCE;
static final @Nullable Throwable ERROR;
static {
// We don't want class initialisation to fail here (as that results in confusing errors). Instead, capture
// the error and rethrow it when accessing. This should be JITted away in the common case.
var helper = Services.tryLoad(PlatformHelper.class);
INSTANCE = helper.instance();
ERROR = helper.error();
}
private Instance() {
}
}
}

View File

@ -3,7 +3,7 @@
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.internal.upgrades; package dan200.computercraft.impl.upgrades;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import dan200.computercraft.api.upgrades.IUpgradeBase; import dan200.computercraft.api.upgrades.IUpgradeBase;
@ -12,6 +12,7 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper; import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -23,6 +24,7 @@ import java.util.function.BiFunction;
* *
* @param <T> The upgrade that this class can serialise and deserialise. * @param <T> The upgrade that this class can serialise and deserialise.
*/ */
@ApiStatus.Internal
public abstract class SerialiserWithCraftingItem<T extends IUpgradeBase> implements UpgradeSerialiser<T> { public abstract class SerialiserWithCraftingItem<T extends IUpgradeBase> implements UpgradeSerialiser<T> {
private final BiFunction<ResourceLocation, ItemStack, T> factory; private final BiFunction<ResourceLocation, ItemStack, T> factory;

View File

@ -3,13 +3,14 @@
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com * Send enquiries to dratcliffe@gmail.com
*/ */
package dan200.computercraft.internal.upgrades; package dan200.computercraft.impl.upgrades;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import dan200.computercraft.api.upgrades.IUpgradeBase; import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser; import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.function.Function; import java.util.function.Function;
@ -21,6 +22,7 @@ import java.util.function.Function;
* *
* @param <T> The upgrade that this class can serialise and deserialise. * @param <T> The upgrade that this class can serialise and deserialise.
*/ */
@ApiStatus.Internal
public abstract class SimpleSerialiser<T extends IUpgradeBase> implements UpgradeSerialiser<T> { public abstract class SimpleSerialiser<T extends IUpgradeBase> implements UpgradeSerialiser<T> {
private final Function<ResourceLocation, T> constructor; private final Function<ResourceLocation, T> constructor;

View File

@ -0,0 +1,18 @@
plugins {
id("cc-tweaked.forge")
id("cc-tweaked.publishing")
}
java {
withJavadocJar()
}
cct.inlineProject(":common-api")
dependencies {
api(project(":core-api"))
}
tasks.javadoc {
include("dan200/computercraft/api/**/*.java")
}

View File

@ -9,7 +9,7 @@ import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.network.wired.IWiredElement; import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralProvider; import dan200.computercraft.api.peripheral.IPeripheralProvider;
import dan200.computercraft.impl.ComputerCraftAPIService; import dan200.computercraft.impl.ComputerCraftAPIForgeService;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
@ -22,6 +22,8 @@ import javax.annotation.Nonnull;
* The forge-specific entrypoint for ComputerCraft's API. * The forge-specific entrypoint for ComputerCraft's API.
*/ */
public final class ForgeComputerCraftAPI { public final class ForgeComputerCraftAPI {
// TODO(1.19.3): Rename me to ComputerCraftAPIForge
private ForgeComputerCraftAPI() { private ForgeComputerCraftAPI() {
} }
@ -61,7 +63,7 @@ public final class ForgeComputerCraftAPI {
} }
@Nonnull @Nonnull
private static ComputerCraftAPIService getInstance() { private static ComputerCraftAPIForgeService getInstance() {
return ComputerCraftAPIService.get(); return ComputerCraftAPIForgeService.get();
} }
} }

View File

@ -5,7 +5,7 @@
*/ */
package dan200.computercraft.api.detail; package dan200.computercraft.api.detail;
import dan200.computercraft.impl.ComputerCraftAPIService; import dan200.computercraft.impl.ComputerCraftAPIForgeService;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
/** /**
@ -15,5 +15,5 @@ public class ForgeDetailRegistries {
/** /**
* Provides details for {@link FluidStack}. * Provides details for {@link FluidStack}.
*/ */
public static final DetailRegistry<FluidStack> FLUID_STACK = ComputerCraftAPIService.get().getFluidStackDetailRegistry(); public static final DetailRegistry<FluidStack> FLUID_STACK = ComputerCraftAPIForgeService.get().getFluidStackDetailRegistry();
} }

View File

@ -0,0 +1,35 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.impl;
import dan200.computercraft.api.ForgeComputerCraftAPI;
import dan200.computercraft.api.detail.DetailRegistry;
import dan200.computercraft.api.peripheral.IPeripheralProvider;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.fluids.FluidStack;
import org.jetbrains.annotations.ApiStatus;
/**
* A Forge-specific version of {@link ComputerCraftAPIService}.
* <p>
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
*
* @see ForgeComputerCraftAPI
*/
@ApiStatus.Internal
public interface ComputerCraftAPIForgeService extends ComputerCraftAPIService {
static ComputerCraftAPIForgeService get() {
return (ComputerCraftAPIForgeService) ComputerCraftAPIService.get();
}
@Override
void registerPeripheralProvider(IPeripheralProvider provider);
@Override
void registerGenericCapability(Capability<?> capability);
DetailRegistry<FluidStack> getFluidStackDetailRegistry();
}

View File

@ -0,0 +1,8 @@
plugins {
id("cc-tweaked.java-convention")
}
// Skip checkstyle here, it's going to be deleted soon anyway!
tasks.checkstyleMain {
enabled = false
}

View File

@ -0,0 +1,12 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package net.minecraftforge.common.capabilities;
@Deprecated
public class Capability<T> {
private Capability() {
}
}

View File

@ -0,0 +1,15 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package net.minecraftforge.common.util;
public final class LazyOptional<T> {
private LazyOptional() {
}
public static <T> LazyOptional<T> empty() {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,10 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package net.minecraftforge.items;
@Deprecated
public interface IItemHandlerModifiable {
}

View File

@ -0,0 +1,10 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package net.minecraftforge.registries;
@Deprecated
public interface IForgeRegistry<T> {
}

View File

@ -0,0 +1,18 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package net.minecraftforge.registries;
@Deprecated
public class RegistryManager {
public static final RegistryManager ACTIVE = new RegistryManager();
private RegistryManager() {
}
public <T> IForgeRegistry<T> getRegistry(Object object) {
throw new UnsupportedOperationException();
}
}

View File

@ -16,9 +16,14 @@ pluginManagement {
val mcVersion: String by settings val mcVersion: String by settings
rootProject.name = "cc-tweaked-$mcVersion" rootProject.name = "cc-tweaked-$mcVersion"
include(":mc-stubs")
include(":core-api") include(":core-api")
include(":core") include(":core")
include(":mc-stubs")
include(":forge-stubs")
include(":common-api")
include(":forge-api")
include(":web") include(":web")
for (project in rootProject.children) { for (project in rootProject.children) {

View File

@ -23,6 +23,7 @@ import dan200.computercraft.api.turtle.TurtleRefuelHandler;
import dan200.computercraft.core.apis.ApiFactories; import dan200.computercraft.core.apis.ApiFactories;
import dan200.computercraft.core.asm.GenericMethod; import dan200.computercraft.core.asm.GenericMethod;
import dan200.computercraft.core.filesystem.FileMount; import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.impl.ComputerCraftAPIForgeService;
import dan200.computercraft.impl.ComputerCraftAPIService; import dan200.computercraft.impl.ComputerCraftAPIService;
import dan200.computercraft.impl.TurtleRefuelHandlers; import dan200.computercraft.impl.TurtleRefuelHandlers;
import dan200.computercraft.impl.detail.DetailRegistryImpl; import dan200.computercraft.impl.detail.DetailRegistryImpl;
@ -58,7 +59,7 @@ import java.io.InputStream;
import static dan200.computercraft.shared.Capabilities.CAPABILITY_WIRED_ELEMENT; import static dan200.computercraft.shared.Capabilities.CAPABILITY_WIRED_ELEMENT;
@AutoService(ComputerCraftAPIService.class) @AutoService(ComputerCraftAPIService.class)
public final class ComputerCraftAPIImpl implements ComputerCraftAPIService { public final class ComputerCraftAPIImpl implements ComputerCraftAPIForgeService {
private final DetailRegistry<ItemStack> itemStackDetails = new DetailRegistryImpl<>(ItemData::fillBasic); private final DetailRegistry<ItemStack> itemStackDetails = new DetailRegistryImpl<>(ItemData::fillBasic);
private final DetailRegistry<BlockReference> blockDetails = new DetailRegistryImpl<>(BlockData::fillBasic); private final DetailRegistry<BlockReference> blockDetails = new DetailRegistryImpl<>(BlockData::fillBasic);
private final DetailRegistry<FluidStack> fluidStackDetails = new DetailRegistryImpl<>(FluidData::fillBasic); private final DetailRegistry<FluidStack> fluidStackDetails = new DetailRegistryImpl<>(FluidData::fillBasic);

View File

@ -0,0 +1,20 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.platform;
import com.google.auto.service.AutoService;
import dan200.computercraft.impl.client.ClientPlatformHelper;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.resources.ResourceLocation;
@AutoService(ClientPlatformHelper.class)
public class ClientPlatformHelperImpl implements ClientPlatformHelper {
@Override
public BakedModel getModel(ModelManager manager, ResourceLocation location) {
return manager.getModel(location);
}
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.platform;
import com.google.auto.service.AutoService;
import dan200.computercraft.impl.PlatformHelper;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.registries.RegistryManager;
import javax.annotation.Nullable;
@AutoService(PlatformHelper.class)
public class PlatformHelperImpl implements PlatformHelper {
@Override
public <T> ResourceLocation getRegistryKey(ResourceKey<Registry<T>> registry, T object) {
var key = RegistryManager.ACTIVE.getRegistry(registry).getKey(object);
if (key == null) throw new IllegalArgumentException(object + " was not registered in " + registry);
return key;
}
@Override
public <T> T getRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id) {
var value = RegistryManager.ACTIVE.getRegistry(registry).getValue(id);
if (value == null) throw new IllegalArgumentException(id + " was not registered in " + registry);
return value;
}
@Nullable
@Override
public CompoundTag getShareTag(ItemStack item) {
return item.getShareTag();
}
}