diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..1c1dad22 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,9 @@ +# Current Lead +* @theglitch76 + +# Maintainers of specific modules. +# Add the current lead to the end of each, because specific overrules general. +# Any org member is encouraged to add themselves to this file when creating a new module, +# but it's not required. +/patchwork-enum-hacks/ @NuclearFarts @theglitch76 +/patchwork-fake-players/ @williambl @theglitch76 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2af9ce51..1d7687fe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,57 +8,40 @@ on: jobs: build: + strategy: + matrix: + java-version: [1.8, 11, 14] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: - java-version: 8 - # https://github.com/actions/cache/blob/master/examples.md#java---gradle - - uses: actions/cache@v2 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-build-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle- + java-version: ${{ matrix.java-version }} - uses: eskatos/gradle-command-action@v1 with: - arguments: remapJar + gradle-version: wrapper + arguments: build -x check + dependencies-cache-enabled: true + configuration-cache-enabled: false # Artifact publishing - uses: actions/upload-artifact@v2 + # In theory we could publish the j11 or even j14 jar, but let's not take any chances + if: ${{ matrix.java-version == 1.8 }} with: + name: Jar Download path: ./build/libs/patchwork-api-?.?.?.jar - checkstyle: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 - with: - java-version: 8 - - uses: actions/cache@v2 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-checkstyle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle- - - uses: eskatos/gradle-command-action@v1 - with: - arguments: checkstyleMain - license: + check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: - java-version: 8 - - uses: actions/cache@v2 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-license-${{ hashFiles('**/*.gradle*') }}-license - restore-keys: | - ${{ runner.os }}-gradle- + java-version: 11 - uses: eskatos/gradle-command-action@v1 with: - arguments: checkLicenses + gradle-version: wrapper + arguments: licenseCheck checkstyleMain + dependencies-cache-enabled: true + configuration-cache-enabled: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e765b8f6..5ee48a18 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,16 +8,6 @@ env: bintray_key: ${{ secrets.bintray_key }} jobs: -# bintray: -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v1 -# - uses: actions/setup-java@v1 -# with: -# java-version: 8 -# - uses: eskatos/gradle-command-action@v1 -# with: -# arguments: bintrayUpload release: runs-on: ubuntu-latest steps: @@ -27,12 +17,10 @@ jobs: java-version: 8 - uses: eskatos/gradle-command-action@v1 with: - arguments: remapJar - - name: Delete -dev jar - run: rm ./build/libs/patchwork-api-*-dev.jar - - uses: ncipollo/release-action@v1.4.0 + arguments: build + - uses: ncipollo/release-action@v1 with: - artifacts: ./build/libs/patchwork-api-*.jar + artifacts: ./build/libs/patchwork-api-?.?.?.jar token: ${{ secrets.GITHUB_TOKEN }} bintray: runs-on: ubuntu-latest @@ -43,4 +31,7 @@ jobs: java-version: 8 - uses: eskatos/gradle-command-action@v1 with: + gradle-version: wrapper arguments: bintrayUpload + dependencies-cache-enabled: true + configuration-cache-enabled: false diff --git a/build.gradle b/build.gradle index 93e6c227..16acf0da 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'eclipse' id 'idea' id 'maven-publish' - id 'fabric-loom' version '0.4-SNAPSHOT' apply false + id 'fabric-loom' version '0.5-SNAPSHOT' apply false id 'net.minecrell.licenser' version '0.4.1' id "org.ajoberstar.grgit" version "3.1.1" id 'com.jfrog.bintray' version '1.8.4' @@ -12,10 +12,13 @@ plugins { def ENV = System.getenv() class Globals { - static def baseVersion = "0.7.0" + static def baseVersion = "0.9.1" static def mcVersion = "1.14.4" static def yarnVersion = "+build.16" } +version = Globals.baseVersion //+ "+" + (ENV.BUILD_NUMBER ? ("build." + ENV.BUILD_NUMBER) : "local") + "-" + getBranch() + +logger.lifecycle("Building Patchwork: " + version) def getSubprojectVersion(project, version) { if (grgit == null) { @@ -46,17 +49,25 @@ allprojects { apply plugin: 'maven-publish' apply plugin: 'fabric-loom' apply plugin: 'net.minecrell.licenser' - + apply plugin: 'com.jfrog.bintray' sourceCompatibility = 1.8 targetCompatibility = 1.8 group = "net.patchworkmc.patchwork-api" + loom { + shareCaches = true + } + + repositories { + maven { url 'https://dl.bintray.com/patchworkmc/Patchwork-Maven/' } + } + dependencies { minecraft "com.mojang:minecraft:$Globals.mcVersion" mappings "net.fabricmc:yarn:${Globals.mcVersion}${Globals.yarnVersion}:v2" - modCompile "net.fabricmc:fabric-loader:0.8.4+build.198" - modCompile "net.fabricmc.fabric-api:fabric-api:0.4.2+build.246-1.14" + modImplementation "net.fabricmc:fabric-loader:0.10.0+build.208" + modImplementation "net.fabricmc.fabric-api:fabric-api:0.15.1+build.260-1.14" implementation 'net.patchworkmc:patchwork-eventbus:2.0.1:all' implementation 'com.google.code.findbugs:jsr305:3.0.2' @@ -69,10 +80,6 @@ allprojects { dev } - repositories { - maven { url 'https://dl.bintray.com/patchworkmc/Patchwork-Maven/' } - } - jar { classifier = "dev" } @@ -113,7 +120,7 @@ allprojects { checkstyle { configFile = rootProject.file("checkstyle.xml") - toolVersion = '8.25' + toolVersion = '8.36.2' } } @@ -136,25 +143,41 @@ subprojects { publishing { publications { - mavenJava(MavenPublication) { - artifact(file("${project.buildDir}/libs/$archivesBaseName-${version}-maven.jar")) { - builtBy remapMavenJar - } - artifact(sourcesJar) { - builtBy remapSourcesJar + create("${archivesBaseName}_mavenJava", MavenPublication) { + afterEvaluate { + artifact(file("${project.buildDir}/libs/$archivesBaseName-${version}-maven.jar")) { + builtBy remapMavenJar + } + artifact(sourcesJar) { + builtBy remapSourcesJar + } } } } + } - repositories { - maven { - url "http://mavenupload.modmuss50.me/" - if (project.hasProperty('mavenPass')) { - credentials { - username 'buildslave' - password project.getProperty('mavenPass') - } - } + bintray { + user = project.hasProperty('bintrayUser') ? project.getProperty('bintrayUser') : System.getenv('bintray_user') + key = project.hasProperty('bintrayKey') ? project.getProperty('bintrayKey') : System.getenv('bintray_key') + publish = true + override = true + publications = ["${archivesBaseName}_mavenJava"] + pkg { + repo = project.bintrayRepo + name = archivesBaseName + user = bintray.user + userOrg = project.repoOwner + licenses = [project.codeLicense] + desc = project.description + websiteUrl = "https://github.com/${project.repoOwner}/${project.repoName}" + issueTrackerUrl = "https://github.com/${project.repoOwner}/${project.repoName}/issues" + vcsUrl = "https://github.com/${project.repoOwner}/${project.repoName}.git" + publicDownloadNumbers = true + + githubRepo = "${project.repoOwner}/${project.repoName}" + version { + name = project.version + released = new Date() } } } @@ -162,13 +185,8 @@ subprojects { javadoc.enabled = false } -task remapMavenJar(type: net.fabricmc.loom.task.RemapJarTask, dependsOn: jar) { - afterEvaluate { - input = file("${project.buildDir}/libs/${archivesBaseName}-${version}-dev.jar") - archiveName = "${archivesBaseName}-${version}-maven.jar" - addNestedDependencies = false - } -} +subprojects.each { bintrayUpload.dependsOn("${it.path}:bintrayUpload") } + bintray { user = project.hasProperty('bintrayUser') ? project.getProperty('bintrayUser') : System.getenv('bintray_user') key = project.hasProperty('bintrayKey') ? project.getProperty('bintrayKey') : System.getenv('bintray_key') @@ -193,7 +211,18 @@ bintray { released = new Date() } } + + javadoc.enabled = false +} + +task remapMavenJar(type: net.fabricmc.loom.task.RemapJarTask, dependsOn: jar) { + afterEvaluate { + input = file("${project.buildDir}/libs/${archivesBaseName}-${version}-dev.jar") + archiveName = "${archivesBaseName}-${version}-maven.jar" + addNestedDependencies = false + } } + publishing { publications { mavenJava(MavenPublication) { @@ -220,6 +249,7 @@ publishing { task licenseFormatAll subprojects {p -> licenseFormatAll.dependsOn("${p.path}:licenseFormat")} subprojects.each {remapJar.dependsOn("${it.path}:remapJar")} + repositories { flatDir { dirs 'jars' @@ -238,35 +268,25 @@ dependencies { minecraft "com.mojang:minecraft:$Globals.mcVersion" mappings "net.fabricmc:yarn:${Globals.mcVersion}${Globals.yarnVersion}:v2" - modCompile "net.fabricmc:fabric-loader:0.8.4+build.198" - modCompile "net.fabricmc.fabric-api:fabric-api:0.4.2+build.246-1.14" + modImplementation "net.fabricmc:fabric-loader:0.10.0+build.208" + modImplementation "net.fabricmc.fabric-api:fabric-api:0.15.1+build.260-1.14" implementation 'com.electronwill.night-config:toml:3.6.2' include 'com.electronwill.night-config:core:3.6.2' include 'com.electronwill.night-config:toml:3.6.2' include 'net.patchworkmc:patchwork-eventbus:2.0.0:all' +} - fileTree(dir: 'jars', include: '*.jar').each { - String baseName = it.name.replace(".jar", "") - String version = "0.1.0" - int split = baseName.lastIndexOf('-') - - if (split != -1) { - version = baseName.substring(split + 1) - baseName = baseName.substring(0, split) - } - - String name = "testmod:${baseName}:${version}" +loom { + shareCaches = true +} - System.out.println("Found test mod: " + it.name.replace(".jar", "") + " -> " + name) - modCompileOnly name - modRuntime name +task copyIdeaFiles(type: Copy) { + if (file(".idea/").exists()) { + from "idea-docs/" + into ".idea/" } } -version = Globals.baseVersion //+ "+" + (ENV.BUILD_NUMBER ? ("build." + ENV.BUILD_NUMBER) : "local") + "-" + getBranch() - -logger.lifecycle("Building Patchwork: " + version) - -apply from: 'https://github.com/FabricMC/fabric-docs/raw/master/gradle/ideconfig.gradle' +copyIdeaFiles diff --git a/gradle.properties b/gradle.properties index a4d4b72a..299ec9b7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,8 @@ -org.gradle.jvmargs=-Xmx2G +# Gradle needs a lot of memory to build in parallel. +# If you're starved, you can reduce this to 1G and disable parallel building. +org.gradle.jvmargs=-Xmx3G +org.gradle.parallel=true +org.gradle.daemon=false repoName=patchwork-api repoOwner=patchworkmc bintrayRepo=Patchwork-Maven diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0800e755..42c2bc9d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip diff --git a/idea-docs/checkstyle-idea.xml b/idea-docs/checkstyle-idea.xml new file mode 100644 index 00000000..f9ee205a --- /dev/null +++ b/idea-docs/checkstyle-idea.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/idea-docs/codeStyles/Project.xml b/idea-docs/codeStyles/Project.xml new file mode 100644 index 00000000..f8d69d4c --- /dev/null +++ b/idea-docs/codeStyles/Project.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/idea-docs/codeStyles/codeStyleConfig.xml b/idea-docs/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/idea-docs/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/patchwork-api-base/build.gradle b/patchwork-api-base/build.gradle new file mode 100644 index 00000000..6cb03206 --- /dev/null +++ b/patchwork-api-base/build.gradle @@ -0,0 +1,2 @@ +archivesBaseName = "patchwork-api-base" +version = getSubprojectVersion(project, "0.1.0") diff --git a/patchwork-fml/src/main/java/net/minecraftforge/api/distmarker/Dist.java b/patchwork-api-base/src/main/java/net/minecraftforge/api/distmarker/Dist.java similarity index 100% rename from patchwork-fml/src/main/java/net/minecraftforge/api/distmarker/Dist.java rename to patchwork-api-base/src/main/java/net/minecraftforge/api/distmarker/Dist.java diff --git a/patchwork-fml/src/main/java/net/minecraftforge/common/MinecraftForge.java b/patchwork-api-base/src/main/java/net/minecraftforge/common/MinecraftForge.java similarity index 100% rename from patchwork-fml/src/main/java/net/minecraftforge/common/MinecraftForge.java rename to patchwork-api-base/src/main/java/net/minecraftforge/common/MinecraftForge.java diff --git a/patchwork-fml/src/main/java/net/minecraftforge/fml/LogicalSide.java b/patchwork-api-base/src/main/java/net/minecraftforge/fml/LogicalSide.java similarity index 100% rename from patchwork-fml/src/main/java/net/minecraftforge/fml/LogicalSide.java rename to patchwork-api-base/src/main/java/net/minecraftforge/fml/LogicalSide.java diff --git a/patchwork-god-classes/src/main/resources/assets.patchwork-god-classes/icon.png b/patchwork-api-base/src/main/resources/assets/patchwork-api-base/icon.png similarity index 100% rename from patchwork-god-classes/src/main/resources/assets.patchwork-god-classes/icon.png rename to patchwork-api-base/src/main/resources/assets/patchwork-api-base/icon.png diff --git a/patchwork-api-base/src/main/resources/fabric.mod.json b/patchwork-api-base/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..48e607a6 --- /dev/null +++ b/patchwork-api-base/src/main/resources/fabric.mod.json @@ -0,0 +1,25 @@ +{ + "schemaVersion": 1, + "id": "patchwork-api-base", + "name": "Patchwork API Base", + "version": "${version}", + "license": "LGPL-2.1-only", + "icon": "assets/patchwork-api-base/icon.png", + "contact": { + "issues": "https://github.com/PatchworkMC/patchwork-api/issues", + "sources": "https://github.com/PatchworkMC/patchwork-api" + }, + "authors": [ + "PatchworkMC" + ], + "depends": { + // This is mirrored in the top-level project because of a Loader issue + "fabricloader": ">=0.10.0", + "fabric": ">=0.15.1" + }, + "description": "Common classes that don't require Mixins or involve mod loading", + "custom": { + "modmenu:api": true, + "modmenu:parent": "patchwork" + } +} diff --git a/patchwork-biomes/build.gradle b/patchwork-biomes/build.gradle index 4c73edc9..3521c4ab 100644 --- a/patchwork-biomes/build.gradle +++ b/patchwork-biomes/build.gradle @@ -1,2 +1,6 @@ archivesBaseName = "patchwork-biomes" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') +} diff --git a/patchwork-biomes/src/main/resources/fabric.mod.json b/patchwork-biomes/src/main/resources/fabric.mod.json index 538e1337..b083e6ef 100644 --- a/patchwork-biomes/src/main/resources/fabric.mod.json +++ b/patchwork-biomes/src/main/resources/fabric.mod.json @@ -13,8 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", - "patchwork-fml": "*" + "patchwork-api-base": "*" }, "description": "Implementation of the Forge Biomes API.", "entrypoints": { diff --git a/patchwork-capabilities/build.gradle b/patchwork-capabilities/build.gradle index a18cc291..d01ea5fd 100644 --- a/patchwork-capabilities/build.gradle +++ b/patchwork-capabilities/build.gradle @@ -1,6 +1,6 @@ archivesBaseName = "patchwork-capabilities" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.4.0") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') } diff --git a/patchwork-capabilities/src/main/java/net/minecraftforge/common/capabilities/CapabilityInject.java b/patchwork-capabilities/src/main/java/net/minecraftforge/common/capabilities/CapabilityInject.java index eb6c4fc9..5fcb3a99 100644 --- a/patchwork-capabilities/src/main/java/net/minecraftforge/common/capabilities/CapabilityInject.java +++ b/patchwork-capabilities/src/main/java/net/minecraftforge/common/capabilities/CapabilityInject.java @@ -24,6 +24,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import net.patchworkmc.api.capability.CapabilityRegisteredCallback; + /** * When placed on a field, the field will be set to an * instance of {@link Capability} once that capability is registered. @@ -48,9 +50,12 @@ * * Warning: Capability injections are run in the thread that the capability is registered. * Due to parallel mod loading, this can potentially be off of the main thread. + * + * @deprecated Don't use this in patchwork - use {@link CapabilityRegisteredCallback}. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) +@Deprecated public @interface CapabilityInject { /** * The capability interface to listen for registration. diff --git a/patchwork-capabilities/src/main/java/net/minecraftforge/common/capabilities/CapabilityProvider.java b/patchwork-capabilities/src/main/java/net/minecraftforge/common/capabilities/CapabilityProvider.java index a2d23d43..3215229d 100644 --- a/patchwork-capabilities/src/main/java/net/minecraftforge/common/capabilities/CapabilityProvider.java +++ b/patchwork-capabilities/src/main/java/net/minecraftforge/common/capabilities/CapabilityProvider.java @@ -23,13 +23,13 @@ import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; -import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.math.Direction; +import net.patchworkmc.impl.capability.CapabilityEvents; + @ParametersAreNonnullByDefault public abstract class CapabilityProvider implements ICapabilityProvider { protected final Class baseClass; @@ -45,14 +45,7 @@ public final void gatherCapabilities() { } public void gatherCapabilities(@Nullable ICapabilityProvider parent) { - AttachCapabilitiesEvent event = new AttachCapabilitiesEvent<>(baseClass, (B) this); - MinecraftForge.EVENT_BUS.post(event); - - if (!event.getCapabilities().isEmpty() || parent != null) { - capabilities = new CapabilityDispatcher(event.getCapabilities(), event.getListeners(), parent); - } else { - capabilities = null; - } + capabilities = CapabilityEvents.gatherCapabilities(baseClass, this, parent); } public final @Nullable CapabilityDispatcher getCapabilities() { diff --git a/patchwork-capabilities/src/main/java/net/minecraftforge/energy/CapabilityEnergy.java b/patchwork-capabilities/src/main/java/net/minecraftforge/energy/CapabilityEnergy.java new file mode 100644 index 00000000..1f366d2c --- /dev/null +++ b/patchwork-capabilities/src/main/java/net/minecraftforge/energy/CapabilityEnergy.java @@ -0,0 +1,57 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.energy; + +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.Capability.IStorage; +import net.minecraftforge.common.capabilities.CapabilityManager; + +import net.minecraft.nbt.IntTag; +import net.minecraft.nbt.Tag; +import net.minecraft.util.math.Direction; + +import net.patchworkmc.api.capability.CapabilityRegisteredCallback; + +public class CapabilityEnergy { + public static Capability ENERGY = null; + + public static void register() { + CapabilityRegisteredCallback.event(IEnergyStorage.class).register(cap -> ENERGY = cap); + + CapabilityManager.INSTANCE.register(IEnergyStorage.class, new IStorage() { + @Override + public Tag writeNBT(Capability capability, IEnergyStorage instance, Direction side) { + return new IntTag(instance.getEnergyStored()); + } + + @Override + public void readNBT(Capability capability, IEnergyStorage instance, Direction side, Tag nbt) { + if (!(instance instanceof EnergyStorage)) { + throw new IllegalArgumentException("Can not deserialize to an instance that isn't the default implementation"); + } + + ((EnergyStorage) instance).energy = ((IntTag) nbt).getInt(); + } + }, + () -> new EnergyStorage(1000) + ); + } +} + diff --git a/patchwork-capabilities/src/main/java/net/minecraftforge/energy/EnergyStorage.java b/patchwork-capabilities/src/main/java/net/minecraftforge/energy/EnergyStorage.java new file mode 100644 index 00000000..d291cb52 --- /dev/null +++ b/patchwork-capabilities/src/main/java/net/minecraftforge/energy/EnergyStorage.java @@ -0,0 +1,103 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.energy; + +/** + *

Reference implementation of {@link IEnergyStorage}. Use/extend this or implement your own.

+ * + *

Derived from the Redstone Flux power system designed by King Lemming and originally utilized in Thermal Expansion and related mods. + * Created with consent and permission of King Lemming and Team CoFH. Released with permission under LGPL 2.1 when bundled with Forge.

+ */ +public class EnergyStorage implements IEnergyStorage { + protected int energy; + protected int capacity; + protected int maxReceive; + protected int maxExtract; + + public EnergyStorage(int capacity) { + this(capacity, capacity, capacity, 0); + } + + public EnergyStorage(int capacity, int maxTransfer) { + this(capacity, maxTransfer, maxTransfer, 0); + } + + public EnergyStorage(int capacity, int maxReceive, int maxExtract) { + this(capacity, maxReceive, maxExtract, 0); + } + + public EnergyStorage(int capacity, int maxReceive, int maxExtract, int energy) { + this.capacity = capacity; + this.maxReceive = maxReceive; + this.maxExtract = maxExtract; + this.energy = Math.max(0, Math.min(capacity, energy)); + } + + @Override + public int receiveEnergy(int maxReceive, boolean simulate) { + if (!canReceive()) { + return 0; + } + + int energyReceived = Math.min(capacity - energy, Math.min(this.maxReceive, maxReceive)); + + if (!simulate) { + energy += energyReceived; + } + + return energyReceived; + } + + @Override + public int extractEnergy(int maxExtract, boolean simulate) { + if (!canExtract()) { + return 0; + } + + int energyExtracted = Math.min(energy, Math.min(this.maxExtract, maxExtract)); + + if (!simulate) { + energy -= energyExtracted; + } + + return energyExtracted; + } + + @Override + public int getEnergyStored() { + return energy; + } + + @Override + public int getMaxEnergyStored() { + return capacity; + } + + @Override + public boolean canExtract() { + return this.maxExtract > 0; + } + + @Override + public boolean canReceive() { + return this.maxReceive > 0; + } +} + diff --git a/patchwork-capabilities/src/main/java/net/minecraftforge/energy/IEnergyStorage.java b/patchwork-capabilities/src/main/java/net/minecraftforge/energy/IEnergyStorage.java new file mode 100644 index 00000000..e47b20dd --- /dev/null +++ b/patchwork-capabilities/src/main/java/net/minecraftforge/energy/IEnergyStorage.java @@ -0,0 +1,74 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.energy; + +/** + *

An energy storage is the unit of interaction with Energy inventories.

+ * + *

A reference implementation can be found at {@link EnergyStorage}.

+ * + *

Derived from the Redstone Flux power system designed by King Lemming and originally utilized in Thermal Expansion and related mods. + * Created with consent and permission of King Lemming and Team CoFH. Released with permission under LGPL 2.1 when bundled with Forge.

+ */ +public interface IEnergyStorage { + /** + * Adds energy to the storage. Returns quantity of energy that was accepted. + * + * @param maxReceive + * Maximum amount of energy to be inserted. + * @param simulate + * If TRUE, the insertion will only be simulated. + * @return Amount of energy that was (or would have been, if simulated) accepted by the storage. + */ + int receiveEnergy(int maxReceive, boolean simulate); + + /** + * Removes energy from the storage. Returns quantity of energy that was removed. + * + * @param maxExtract + * Maximum amount of energy to be extracted. + * @param simulate + * If TRUE, the extraction will only be simulated. + * @return Amount of energy that was (or would have been, if simulated) extracted from the storage. + */ + int extractEnergy(int maxExtract, boolean simulate); + + /** + * Returns the amount of energy currently stored. + */ + int getEnergyStored(); + + /** + * Returns the maximum amount of energy that can be stored. + */ + int getMaxEnergyStored(); + + /** + * Returns if this storage can have energy extracted. + * If this is false, then any calls to extractEnergy will return 0. + */ + boolean canExtract(); + + /** + * Used to determine if this storage can receive energy. + * If this is false, then any calls to receiveEnergy will return 0. + */ + boolean canReceive(); +} diff --git a/patchwork-capabilities/src/main/java/net/patchworkmc/api/capability/CapabilityRegisteredCallback.java b/patchwork-capabilities/src/main/java/net/patchworkmc/api/capability/CapabilityRegisteredCallback.java index 0186551d..c9e9f095 100644 --- a/patchwork-capabilities/src/main/java/net/patchworkmc/api/capability/CapabilityRegisteredCallback.java +++ b/patchwork-capabilities/src/main/java/net/patchworkmc/api/capability/CapabilityRegisteredCallback.java @@ -29,6 +29,10 @@ public interface CapabilityRegisteredCallback { void onCapabilityRegistered(Capability capability); static Event> event(Class type) { - return CapabilityRegisteredCallbackInternal.getOrCreateEvent(type); + return CapabilityRegisteredCallbackInternal.getOrCreateEvent(type.getName()); + } + + static Event> event(String className) { + return CapabilityRegisteredCallbackInternal.getOrCreateEvent(className); } } diff --git a/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/BaseCapabilityProvider.java b/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/BaseCapabilityProvider.java index 31a3d2c1..1b682810 100644 --- a/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/BaseCapabilityProvider.java +++ b/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/BaseCapabilityProvider.java @@ -21,11 +21,8 @@ import javax.annotation.Nullable; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.capabilities.CapabilityDispatcher; import net.minecraftforge.common.capabilities.CapabilityProvider; import net.minecraftforge.common.capabilities.ICapabilityProvider; -import net.minecraftforge.event.AttachCapabilitiesEvent; public class BaseCapabilityProvider extends CapabilityProvider { private final T provider; @@ -37,13 +34,6 @@ public BaseCapabilityProvider(Class baseClass, T provider) { @Override public void gatherCapabilities(@Nullable ICapabilityProvider parent) { - AttachCapabilitiesEvent event = new AttachCapabilitiesEvent<>(baseClass, provider); - MinecraftForge.EVENT_BUS.post(event); - - if (!event.getCapabilities().isEmpty() || parent != null) { - capabilities = new CapabilityDispatcher(event.getCapabilities(), event.getListeners(), parent); - } else { - capabilities = null; - } + capabilities = CapabilityEvents.gatherCapabilities(baseClass, provider, parent); } } diff --git a/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/CapabilityEvents.java b/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/CapabilityEvents.java new file mode 100644 index 00000000..42794d67 --- /dev/null +++ b/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/CapabilityEvents.java @@ -0,0 +1,43 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.capability; + +import javax.annotation.Nullable; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.capabilities.CapabilityDispatcher; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.event.AttachCapabilitiesEvent; + +public class CapabilityEvents { + // This is less restrictive than Forge's implementation, since patchwork can't make vanilla extend stuff at random. + @SuppressWarnings("unchecked") + @Nullable + public static CapabilityDispatcher gatherCapabilities(Class type, T provider, @Nullable ICapabilityProvider parent) { + AttachCapabilitiesEvent event = new AttachCapabilitiesEvent((Class) type, provider); + MinecraftForge.EVENT_BUS.post(event); + + if (!event.getCapabilities().isEmpty() || parent != null) { + return new CapabilityDispatcher(event.getCapabilities(), event.getListeners(), parent); + } else { + return null; + } + } +} diff --git a/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/CapabilityRegisteredCallbackInternal.java b/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/CapabilityRegisteredCallbackInternal.java index f8b56981..604093aa 100644 --- a/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/CapabilityRegisteredCallbackInternal.java +++ b/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/CapabilityRegisteredCallbackInternal.java @@ -19,7 +19,7 @@ package net.patchworkmc.impl.capability; -import java.util.IdentityHashMap; +import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.LogManager; @@ -33,10 +33,10 @@ public class CapabilityRegisteredCallbackInternal { private static final Logger LOGGER = LogManager.getLogger(CapabilityRegisteredCallback.class); - private static final Map, Event> CALLBACKS = new IdentityHashMap<>(); + private static final Map> CALLBACKS = new HashMap<>(); @SuppressWarnings("unchecked") - public static Event> getOrCreateEvent(Class type) { + public static Event> getOrCreateEvent(String type) { return (Event>) CALLBACKS.computeIfAbsent(type, $ -> EventFactory.createArrayBacked(CapabilityRegisteredCallback.class, capability -> { }, callbacks -> capability -> { boolean error = false; Throwable throwable = new Throwable(); @@ -51,7 +51,7 @@ public static Event> getOrCreateEvent(Class< } if (error) { - LOGGER.error("An uncaught exception was thrown while processing a CapabilityRegisteredCallback<{}>", type.getName(), throwable); + LOGGER.error("An uncaught exception was thrown while processing a CapabilityRegisteredCallback<{}>", type, throwable); } })); } diff --git a/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/PatchworkCapabilities.java b/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/PatchworkCapabilities.java new file mode 100644 index 00000000..0f27ddef --- /dev/null +++ b/patchwork-capabilities/src/main/java/net/patchworkmc/impl/capability/PatchworkCapabilities.java @@ -0,0 +1,31 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.capability; + +import net.minecraftforge.energy.CapabilityEnergy; + +import net.fabricmc.api.ModInitializer; + +public class PatchworkCapabilities implements ModInitializer { + @Override + public void onInitialize() { + CapabilityEnergy.register(); + } +} diff --git a/patchwork-capabilities/src/main/resources/fabric.mod.json b/patchwork-capabilities/src/main/resources/fabric.mod.json index 94f7d084..c690e4cf 100644 --- a/patchwork-capabilities/src/main/resources/fabric.mod.json +++ b/patchwork-capabilities/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "id": "patchwork-capabilities", - "name": "Patchwork Capabilities support", + "name": "Patchwork Capabilities", "version": "${version}", "license": "LGPL-2.1-only", "icon": "assets/patchwork-capabilities/icon.png", @@ -13,12 +13,16 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", - "patchwork-fml": "*" + "patchwork-api-base": "*" }, "mixins": [ "patchwork-capabilities.mixins.json" ], + "entrypoints": { + "main": [ + "net.patchworkmc.impl.capability.PatchworkCapabilities" + ] + }, "description": "Implementation of the Forge Capabilities API", "custom": { "modmenu:api": true, diff --git a/patchwork-data-generators/build.gradle b/patchwork-data-generators/build.gradle index f92c9094..41620ba2 100644 --- a/patchwork-data-generators/build.gradle +++ b/patchwork-data-generators/build.gradle @@ -1,7 +1,8 @@ archivesBaseName = "patchwork-data-generators" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") dependencies { - compile project(path: ':patchwork-events-lifecycle', configuration: 'dev') - compile project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-events-lifecycle', configuration: 'dev') + implementation project(path: ':patchwork-fml', configuration: 'dev') } diff --git a/patchwork-data-generators/src/main/resources/fabric.mod.json b/patchwork-data-generators/src/main/resources/fabric.mod.json index 3750849e..1bc564f2 100644 --- a/patchwork-data-generators/src/main/resources/fabric.mod.json +++ b/patchwork-data-generators/src/main/resources/fabric.mod.json @@ -13,7 +13,8 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4" + "patchwork-api-base": "*", + "patchwork-fml": "*" }, "mixins": [ ], diff --git a/patchwork-dispatcher/build.gradle b/patchwork-dispatcher/build.gradle index 3efbace5..34ec3ee5 100644 --- a/patchwork-dispatcher/build.gradle +++ b/patchwork-dispatcher/build.gradle @@ -1,9 +1,10 @@ archivesBaseName = "patchwork-dispatcher" -version = getSubprojectVersion(project, "0.3.0") +version = getSubprojectVersion(project, "0.4.1") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') - compile project(path: ':patchwork-registries', configuration: 'dev') - compile project(path: ':patchwork-events-lifecycle', configuration: 'dev') - compile project(path: ':patchwork-events-rendering', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-registries', configuration: 'dev') + implementation project(path: ':patchwork-events-lifecycle', configuration: 'dev') + implementation project(path: ':patchwork-model-loader', configuration: 'dev') } diff --git a/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/Patchwork.java b/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/Patchwork.java index 6a0d999b..0c05c5eb 100644 --- a/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/Patchwork.java +++ b/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/Patchwork.java @@ -19,23 +19,23 @@ package net.patchworkmc.impl; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.Event; -import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.ModContainer; import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModLoadingContext; -import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLLoadCompleteEvent; @@ -45,39 +45,37 @@ import net.minecraftforge.fml.javafmlmod.FMLModContainer; import net.minecraftforge.registries.ForgeRegistries; -import net.minecraft.client.MinecraftClient; import net.minecraft.server.dedicated.DedicatedServer; -import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; import net.patchworkmc.api.ForgeInitializer; -import net.patchworkmc.impl.event.lifecycle.LifecycleEvents; -import net.patchworkmc.impl.event.render.RenderEvents; import net.patchworkmc.impl.registries.RegistryEventDispatcher; -public class Patchwork implements ModInitializer { +public class Patchwork { private static final Logger LOGGER = LogManager.getLogger(Patchwork.class); - private static void dispatch(Map mods, Event event) { + private static void dispatch(Collection mods, Event event) { dispatch(mods, container -> event); } - private static void dispatch(Map mods, Function provider) { - for (FMLModContainer container : mods.values()) { + /** + * Fire the specific event for all ModContainers on the {@link Mod.EventBusSubscriber.Bus.MOD} Event bus. + */ + private static void dispatch(Collection mods, Function provider) { + for (FMLModContainer container : mods) { ModLoadingContext.get().setActiveContainer(container, new FMLJavaModLoadingContext(container)); Event event = provider.apply(container); LOGGER.debug("Firing event for modid {} : {}", container.getModId(), event.toString()); - container.getEventBus().post(event); + container.patchwork$acceptEvent(event); LOGGER.debug("Fired event for modid {} : {}", container.getModId(), event.toString()); ModLoadingContext.get().setActiveContainer(null, "minecraft"); } } - @Override - public void onInitialize() { + public static void gatherAndInitializeMods() { ForgeRegistries.init(); Map mods = new HashMap<>(); @@ -101,12 +99,40 @@ public void onInitialize() { ModLoadingContext.get().setActiveContainer(container, new FMLJavaModLoadingContext(container)); try { + // TODO: Supposed to call "container.setMod()" here, but this requires a WIP Patchwork-Patcher feature. initializer.onForgeInitialize(); } catch (Throwable t) { if (error == null) { error = new PatchworkInitializationException("Failed to construct Patchwork mods"); } + Throwable checked; + + if (t instanceof BootstrapMethodError) { + checked = t.getCause(); + } else { + checked = t; + } + + if (checked instanceof NoClassDefFoundError || checked instanceof NoSuchMethodError || checked instanceof NoSuchFieldError) { + final String unDefinedClass = checked.getMessage().substring(checked.getMessage().lastIndexOf(' ') + 1).replace('/', '.'); + String type; + + if (checked instanceof NoClassDefFoundError) { + type = "class"; + } else if (checked instanceof NoSuchMethodError) { + type = "method"; + } else { + type = "field"; + } + + if (unDefinedClass.startsWith("net.minecraft.") || (unDefinedClass.startsWith("net.minecraftforge.") && !unDefinedClass.startsWith("net.minecraftforge.lex."))) { + throw new PatchworkInitializationException("Patchwork mod " + initializer.getModId() + " tried to access an unimplemented " + type + ".", t); + } else { + throw new PatchworkInitializationException("Patchwork mod " + initializer.getModId() + " tried to access a missing " + type + " from a missing and undeclared, or outdated dependency.", t); + } + } + error.addSuppressed(t); } @@ -122,26 +148,65 @@ public void onInitialize() { ModList.get().setLoadedMods(mods.values()); // Send initialization events - dispatch(mods, new RegistryEvent.NewRegistry()); - RegistryEventDispatcher.dispatchRegistryEvents(event -> dispatch(mods, event)); - dispatch(mods, FMLCommonSetupEvent::new); - - DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> { - dispatch(mods, container -> new FMLClientSetupEvent(MinecraftClient::getInstance, container)); - RenderEvents.registerEventDispatcher(event -> dispatch(mods, event)); - }); + dispatch(mods.values(), new RegistryEvent.NewRegistry()); + RegistryEventDispatcher.dispatchRegistryEvents(event -> dispatch(mods.values(), event)); + } - DistExecutor.runWhenOn(Dist.DEDICATED_SERVER, () -> () -> { - Object gameInstance = FabricLoader.getInstance().getGameInstance(); - Supplier supplier = () -> (DedicatedServer) gameInstance; + /** + * This is called on the ResourceLoader's thread when a resource loading happens, i.e. during client start-up or F3+T is pressed. + * Forge fires the FMLCommonSetupEvent and LifeCycleEvents(FMLClientSetupEvent and FMLDedicatedServerSetupEvent) on its own thread in parallel. Sequence cannot be guaranteed. + * IMPORTANT: In Patchwork, we fire all events on the main thread (Client Thread or Server Thread). + * @param lifeCycleEvent + * @param preSidedRunnable Fired before the LifeCycleEvent, on the main thread. Sequence cannot be guaranteed. + * @param postSidedRunnable Fired after the LifeCycleEvent, on the main thread. Sequence cannot be guaranteed. + */ + public static void loadMods(Function lifeCycleEvent, Consumer>> preSidedRunnable, Consumer>> postSidedRunnable) { + List mods = ModList.get().applyForEachModContainer(m -> (FMLModContainer) m).collect(Collectors.toList()); + + // Loading mod config + // TODO: Load client and common configs here + + // Mod setup: SETUP + dispatch(mods, FMLCommonSetupEvent::new); + // Mod setup: SIDED SETUP + preSidedRunnable.accept(c -> dispatch(mods, c.get())); + dispatch(mods, lifeCycleEvent); + postSidedRunnable.accept(c -> dispatch(mods, c.get())); + // Mod setup complete + } - dispatch(mods, container -> new FMLDedicatedServerSetupEvent(supplier, container)); - }); + /** + * In Patchwork, we fire all of following events on the main thread (Client Thread or Server Thread). + */ + public static void finishMods() { + List mods = ModList.get().applyForEachModContainer(m -> (FMLModContainer) m).collect(Collectors.toList()); + // Mod setup: ENQUEUE IMC dispatch(mods, InterModEnqueueEvent::new); + // Mod setup: PROCESS IMC dispatch(mods, InterModProcessEvent::new); - LifecycleEvents.setLoadCompleteCallback(() -> dispatch(mods, FMLLoadCompleteEvent::new)); + // Mod setup: Final completion + dispatch(mods, FMLLoadCompleteEvent::new); + // Freezing data, TODO: do we need freezing? + // GameData.freezeData(); + // NetworkRegistry.lock(); + } + + public static void beginServerModLoading() { + Object gameInstance = FabricLoader.getInstance().getGameInstance(); + Supplier supplier = () -> (DedicatedServer) gameInstance; + + LOGGER.debug("Patchwork Dedicated Server Mod Loader: Start mod loading."); + Patchwork.gatherAndInitializeMods(); + Patchwork.loadMods(container -> new FMLDedicatedServerSetupEvent(supplier, container), dummy -> { }, dummy -> { }); + } + + public static void endOfServerModLoading() { + LOGGER.debug("Patchwork Dedicated Server Mod Loader: Finish mod loading."); + Patchwork.finishMods(); + LOGGER.debug("Patchwork Dedicated Server Mod Loader: Complete mod loading"); + // Assume there's no error. MinecraftForge.EVENT_BUS.start(); } } diff --git a/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/PatchworkClientModLoader.java b/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/PatchworkClientModLoader.java new file mode 100644 index 00000000..6adb4ba3 --- /dev/null +++ b/patchwork-dispatcher/src/main/java/net/patchworkmc/impl/PatchworkClientModLoader.java @@ -0,0 +1,100 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; + +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.ResourceReloadListener; +import net.minecraft.util.profiler.Profiler; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.resource.ClientBuiltinResourcePackProvider; +import net.minecraft.client.resource.ClientResourcePackProfile; +import net.minecraft.resource.ReloadableResourceManager; +import net.minecraft.resource.ResourcePackManager; + +public class PatchworkClientModLoader { + private static final Logger LOGGER = LogManager.getLogger(PatchworkClientModLoader.class); + private static boolean loading; + private static MinecraftClient mc; + + public static void begin(final MinecraftClient minecraft, final ResourcePackManager defaultResourcePacks, + final ReloadableResourceManager mcResourceManager, ClientBuiltinResourcePackProvider metadataSerializer) { + loading = true; + PatchworkClientModLoader.mc = minecraft; + Patchwork.gatherAndInitializeMods(); + mcResourceManager.registerListener(PatchworkClientModLoader::onReload); + } + + /** + * @param syncExecutor The main thread executor + */ + private static CompletableFuture onReload(final ResourceReloadListener.Synchronizer stage, final ResourceManager resourceManager, + final Profiler prepareProfiler, final Profiler executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) { + return CompletableFuture.runAsync(() -> startModLoading(syncExecutor), asyncExecutor) + .thenCompose(stage::whenPrepared) + .thenRunAsync(() -> finishModLoading(syncExecutor), asyncExecutor); + } + + private static void startModLoading(Executor mainThreadExecutor) { + LOGGER.debug("Patchwork Client Mod Loader: Start mod loading."); + mainThreadExecutor.execute(() -> Patchwork.loadMods(container -> new FMLClientSetupEvent(() -> PatchworkClientModLoader.mc, container), + PatchworkClientModLoader::preSidedRunnable, PatchworkClientModLoader::postSidedRunnable)); + } + + private static void preSidedRunnable(Consumer> perModContainerEventProcessor) { + perModContainerEventProcessor.accept(ModelRegistryEvent::new); + } + + private static void postSidedRunnable(Consumer> perModContainerEventProcessor) { + } + + private static void finishModLoading(Executor executor) { + LOGGER.debug("Patchwork Client Mod Loader: Finish mod loading."); + Patchwork.finishMods(); + loading = false; + // reload game settings on main thread + executor.execute(() -> mc.options.load()); + } + + /** + * @return true if an error occurred so that we can cancel the normal title screen. + */ + public static boolean completeModLoading() { + LOGGER.debug("Patchwork Client Mod Loader: Complete mod loading"); + // Assume there's no error. + MinecraftForge.EVENT_BUS.start(); + return false; + } + + // TODO: Reserved for future use + public static void onResourceReloadComplete(boolean errorFree) { + } +} diff --git a/patchwork-dispatcher/src/main/java/net/patchworkmc/mixin/MixinMinecraftClient.java b/patchwork-dispatcher/src/main/java/net/patchworkmc/mixin/MixinMinecraftClient.java new file mode 100644 index 00000000..633c7532 --- /dev/null +++ b/patchwork-dispatcher/src/main/java/net/patchworkmc/mixin/MixinMinecraftClient.java @@ -0,0 +1,54 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.resource.ReloadableResourceManager; + +import net.patchworkmc.impl.PatchworkClientModLoader; + +@Mixin(MinecraftClient.class) +public abstract class MixinMinecraftClient { + @Shadow + private ReloadableResourceManager resourceManager; + + @Inject(method = "init", at = @At(value = "INVOKE", shift = Shift.BEFORE, ordinal = 0, target = "net/minecraft/resource/ResourcePackManager.scanPacks()V")) + private void initForgeModsOnClient(CallbackInfo ci) { + MinecraftClient me = (MinecraftClient) (Object) this; + PatchworkClientModLoader.begin(me, me.getResourcePackManager(), resourceManager, me.getResourcePackDownloader()); + } + + // this.setOverlay(new SplashScreen(this, this.resourceManager.beginInitialMonitoredReload(SystemUtil.getServerWorkerExecutor(), this, voidFuture), () -> { + // if (SharedConstants.isDevelopment) this.checkGameData(); + // + if (net.minecraftforge.fml.client.ClientModLoader.completeModLoading()) return; // Do not overwrite the error sceen + // + // Show either ConnectScreen or TitleScreen + // } + @Inject(method = "method_18504", at = @At("RETURN")) + private void onResourceReloadComplete(CallbackInfo ci) { + PatchworkClientModLoader.onResourceReloadComplete(!PatchworkClientModLoader.completeModLoading()); + } +} diff --git a/patchwork-dispatcher/src/main/java/net/patchworkmc/mixin/MixinMinecraftDedicatedServer.java b/patchwork-dispatcher/src/main/java/net/patchworkmc/mixin/MixinMinecraftDedicatedServer.java new file mode 100644 index 00000000..f2f873cf --- /dev/null +++ b/patchwork-dispatcher/src/main/java/net/patchworkmc/mixin/MixinMinecraftDedicatedServer.java @@ -0,0 +1,43 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.server.dedicated.MinecraftDedicatedServer; + +import net.patchworkmc.impl.Patchwork; + +@Mixin(MinecraftDedicatedServer.class) +public abstract class MixinMinecraftDedicatedServer { + @Inject(method = "setupServer", at = @At(value = "INVOKE", shift = Shift.AFTER, ordinal = 0, target = "org/apache/logging/log4j/Logger.info(Ljava/lang/String;)V")) + private void initForgeModsOnServer(CallbackInfoReturnable ci) { + Patchwork.beginServerModLoading(); + } + + @Inject(method = "setupServer", at = @At(value = "NEW", shift = Shift.BEFORE, ordinal = 0, target = "net/minecraft/server/dedicated/DedicatedPlayerManager")) + private void endOfModLoading(CallbackInfoReturnable ci) { + Patchwork.endOfServerModLoading(); + } +} diff --git a/patchwork-loot/src/main/resources/patchwork-loot/icon.png b/patchwork-dispatcher/src/main/resources/assets/patchwork-dispatcher/icon.png similarity index 100% rename from patchwork-loot/src/main/resources/patchwork-loot/icon.png rename to patchwork-dispatcher/src/main/resources/assets/patchwork-dispatcher/icon.png diff --git a/patchwork-dispatcher/src/main/resources/fabric.mod.json b/patchwork-dispatcher/src/main/resources/fabric.mod.json index 2b63f05d..9fec4d55 100644 --- a/patchwork-dispatcher/src/main/resources/fabric.mod.json +++ b/patchwork-dispatcher/src/main/resources/fabric.mod.json @@ -10,18 +10,16 @@ "license": "LGPL-2.1-only", "icon": "assets/patchwork-dispatcher/icon.png", "environment": "*", - "entrypoints": { - "main": [ - "net.patchworkmc.impl.Patchwork" - ] - }, "depends": { - "fabricloader": ">=0.8.4", - "fabric": "*", + "patchwork-api-base": "*", "patchwork-fml": "*", "patchwork-registries": "*", - "patchwork-events-lifecycle": "*" + "patchwork-events-lifecycle": "*", + "patchwork-model-loader": "*" }, + "mixins": [ + "patchwork-dispatcher.mixins.json" + ], "custom": { "modmenu:api": true, "modmenu:parent": "patchwork" diff --git a/patchwork-dispatcher/src/main/resources/patchwork-dispatcher.mixins.json b/patchwork-dispatcher/src/main/resources/patchwork-dispatcher.mixins.json new file mode 100644 index 00000000..90025d4b --- /dev/null +++ b/patchwork-dispatcher/src/main/resources/patchwork-dispatcher.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin", + "compatibilityLevel": "JAVA_8", + "server": [ + "MixinMinecraftDedicatedServer" + ], + "client": [ + "MixinMinecraftClient" + ], + "injectors": { + "defaultRequire": 1, + "maxShiftBy": 2 + } +} diff --git a/patchwork-enum-hacks/build.gradle b/patchwork-enum-hacks/build.gradle index 5e41db54..91078b2d 100644 --- a/patchwork-enum-hacks/build.gradle +++ b/patchwork-enum-hacks/build.gradle @@ -1,2 +1,6 @@ archivesBaseName = "patchwork-enum-hacks" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') +} diff --git a/patchwork-enum-hacks/src/main/resources/fabric.mod.json b/patchwork-enum-hacks/src/main/resources/fabric.mod.json index 4fbe8282..f9b4a1aa 100644 --- a/patchwork-enum-hacks/src/main/resources/fabric.mod.json +++ b/patchwork-enum-hacks/src/main/resources/fabric.mod.json @@ -11,7 +11,7 @@ "license": "LGPL-2.1-only", "environment": "*", "depends": { - "fabricloader": ">=0.8.4" + "patchwork-api-base": "*" }, "mixins": [ "patchwork-enum-hacks.mixins.json" diff --git a/patchwork-events-entity/build.gradle b/patchwork-events-entity/build.gradle index 3a776fa4..a403607b 100644 --- a/patchwork-events-entity/build.gradle +++ b/patchwork-events-entity/build.gradle @@ -1,8 +1,8 @@ archivesBaseName = "patchwork-events-entity" -version = getSubprojectVersion(project, "0.4.0") +version = getSubprojectVersion(project, "0.6.0") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') - compile project(path: ':patchwork-extensions', configuration: 'dev') - compile project(path: ':patchwork-extensions-item', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-extensions', configuration: 'dev') + implementation project(path: ':patchwork-extensions-item', configuration: 'dev') } diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/client/event/RenderPlayerEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/client/event/RenderPlayerEvent.java new file mode 100644 index 00000000..36346cd3 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/client/event/RenderPlayerEvent.java @@ -0,0 +1,79 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.event; + +import net.minecraftforge.event.entity.player.PlayerEvent; + +import net.minecraft.client.render.entity.PlayerEntityRenderer; +import net.minecraft.entity.player.PlayerEntity; + +public abstract class RenderPlayerEvent extends PlayerEvent { + private final PlayerEntityRenderer renderer; + private final float partialRenderTick; + private final double x; + private final double y; + private final double z; + + public RenderPlayerEvent(PlayerEntity player, PlayerEntityRenderer renderer, float partialRenderTick, double x, double y, double z) { + super(player); + this.renderer = renderer; + this.partialRenderTick = partialRenderTick; + this.x = x; + this.y = y; + this.z = z; + } + + public PlayerEntityRenderer getRenderer() { + return renderer; + } + + public float getPartialRenderTick() { + return partialRenderTick; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } + + public static class Pre extends RenderPlayerEvent { + public Pre(PlayerEntity player, PlayerEntityRenderer renderer, float tick, double x, double y, double z) { + super(player, renderer, tick, x, y, z); + } + + @Override + public boolean isCancelable() { + return true; + } + } + + public static class Post extends RenderPlayerEvent { + public Post(PlayerEntity player, PlayerEntityRenderer renderer, float tick, double x, double y, double z) { + super(player, renderer, tick, x, y, z); + } + } +} diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/EntityTravelToDimensionEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/EntityTravelToDimensionEvent.java new file mode 100644 index 00000000..104695f8 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/EntityTravelToDimensionEvent.java @@ -0,0 +1,56 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.event.entity; + +import net.minecraftforge.common.MinecraftForge; + +import net.minecraft.entity.Entity; +import net.minecraft.world.dimension.DimensionType; + +/** + *

EntityTravelToDimensionEvent is fired before an Entity travels to a dimension.

+ * + *

{@link #dimension} contains the id of the dimension the entity is traveling to.

+ * + *

This event is cancelable. + * If this event is canceled, the Entity does not travel to the dimension.

+ * + *

This event does not have a result.

+ * + *

This event is fired on the {@link MinecraftForge#EVENT_BUS}.

+ */ +public class EntityTravelToDimensionEvent extends EntityEvent { + private final DimensionType dimension; + + public EntityTravelToDimensionEvent(Entity entity, DimensionType dimension) { + super(entity); + this.dimension = dimension; + } + + public DimensionType getDimension() { + return dimension; + } + + @Override + public boolean isCancelable() { + return true; + } +} + diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/ProjectileImpactEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/ProjectileImpactEvent.java new file mode 100644 index 00000000..f84f8807 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/ProjectileImpactEvent.java @@ -0,0 +1,97 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.event.entity; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.projectile.ExplosiveProjectileEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.entity.thrown.ThrownEntity; +import net.minecraft.util.hit.HitResult; + +/** + * This event is fired when a projectile entity impacts something. + * + *

Subclasses of this event exist for more specific types of projectile.

+ * + *

This event is fired for all vanilla projectiles by Patchwork. + * Custom projectiles should fire this event via {@link net.patchworkmc.impl.event.entity.EntityEvents}, check the result, + * and cancel the impact if false.

+ * + *

This event is cancelable. When canceled, the impact will not be processed. + * Killing or other handling of the entity after event cancellation is up to the modder.

+ * + *

This event is fired on the {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}.

+ */ +public class ProjectileImpactEvent extends EntityEvent { + private final HitResult ray; + + public ProjectileImpactEvent(Entity entity, HitResult ray) { + super(entity); + this.ray = ray; + } + + @Override + public boolean isCancelable() { + return true; + } + + public HitResult getRayTraceResult() { + return ray; + } + + public static class Arrow extends ProjectileImpactEvent { + private final ProjectileEntity arrow; + + public Arrow(ProjectileEntity arrow, HitResult ray) { + super(arrow, ray); + this.arrow = arrow; + } + + public ProjectileEntity getArrow() { + return arrow; + } + } + + public static class Fireball extends ProjectileImpactEvent { + private final ExplosiveProjectileEntity fireball; + + public Fireball(ExplosiveProjectileEntity fireball, HitResult ray) { + super(fireball, ray); + this.fireball = fireball; + } + + public ExplosiveProjectileEntity getFireball() { + return fireball; + } + } + + public static class Throwable extends ProjectileImpactEvent { + private final ThrownEntity throwable; + + public Throwable(ThrownEntity throwable, HitResult ray) { + super(throwable, ray); + this.throwable = throwable; + } + + public ThrownEntity getThrowable() { + return throwable; + } + } +} diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/living/AnimalTameEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/living/AnimalTameEvent.java new file mode 100644 index 00000000..297ddbed --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/living/AnimalTameEvent.java @@ -0,0 +1,58 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.event.entity.living; + +import net.minecraftforge.common.MinecraftForge; + +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.player.PlayerEntity; + +/** + * This event is fired when an {@link AnimalEntity} is tamed. + * + *

It is fired via ForgeEventFactory#onAnimalTame(AnimalEntity, PlayerEntity). + * Forge fires this event for applicable vanilla animals, mods need to fire it themselves. + * This event is cancellable. If canceled, taming the animal will fail. + * This event is fired on the {@link MinecraftForge#EVENT_BUS}. + */ + +public class AnimalTameEvent extends LivingEvent { + private final AnimalEntity animal; + private final PlayerEntity tamer; + + public AnimalTameEvent(AnimalEntity animal, PlayerEntity tamer) { + super(animal); + this.animal = animal; + this.tamer = tamer; + } + + public AnimalEntity getAnimal() { + return animal; + } + + public PlayerEntity getTamer() { + return tamer; + } + + @Override + public boolean isCancelable() { + return true; + } +} diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/EntityItemPickupEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/EntityItemPickupEvent.java new file mode 100644 index 00000000..508e9b9c --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/EntityItemPickupEvent.java @@ -0,0 +1,56 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.event.entity.player; + +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; + +/** + *

This event is called when a player collides with a EntityItem on the ground. + * The event can be canceled, and no further processing will be done.

+ * + *

You can set the result of this event to ALLOW which will trigger the + * processing of achievements, FML's event, play the sound, and kill the + * entity if all the items are picked up.

+ * + *

setResult(ALLOW) is the same as the old setHandled()

+ */ +public class EntityItemPickupEvent extends PlayerEvent { + private final ItemEntity item; + + public EntityItemPickupEvent(PlayerEntity player, ItemEntity item) { + super(player); + this.item = item; + } + + public ItemEntity getItem() { + return item; + } + + @Override + public boolean isCancelable() { + return true; + } + + @Override + public boolean hasResult() { + return true; + } +} diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/ItemFishedEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/ItemFishedEvent.java new file mode 100644 index 00000000..166ad91f --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/ItemFishedEvent.java @@ -0,0 +1,92 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.event.entity.player; + +import java.util.List; + +import javax.annotation.Nonnegative; + +import com.google.common.base.Preconditions; + +import net.minecraft.entity.projectile.FishingBobberEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.DefaultedList; + +/** + * This event is called when a player fishes an item. + * + *

This event is cancellable. + * Canceling the event will cause the player to receive no items at all. + * The hook will still take the damage specified

+ */ + +public class ItemFishedEvent extends PlayerEvent { + private final DefaultedList stacks = DefaultedList.of(); + private final FishingBobberEntity hook; + private int rodDamage; + + public ItemFishedEvent(List stacks, int rodDamage, FishingBobberEntity hook) { + super(hook.getOwner()); + this.stacks.addAll(stacks); + this.rodDamage = rodDamage; + this.hook = hook; + } + + /** + * Get the damage the rod will take. + * + * @return The damage the rod will take + */ + public int getRodDamage() { + return rodDamage; + } + + /** + * Specifies the amount of damage that the fishing rod should take. + * This is not added to the pre-existing damage to be taken. + * + * @param rodDamage The damage the rod will take. Must be nonnegative + */ + public void damageRodBy(@Nonnegative int rodDamage) { + Preconditions.checkArgument(rodDamage >= 0); + this.rodDamage = rodDamage; + } + + /** + * Use this to get the items the player will receive. + * You cannot use this to modify the drops the player will get. + * If you want to affect the loot, you should use LootTables. + */ + public DefaultedList getDrops() { + return stacks; + } + + /** + * Use this to stuff related to the hook itself, like the position of the bobber. + */ + public FishingBobberEntity getHookEntity() { + return hook; + } + + @Override + public boolean isCancelable() { + return true; + } +} diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/ItemTooltipEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/ItemTooltipEvent.java new file mode 100644 index 00000000..bac5a6c4 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/ItemTooltipEvent.java @@ -0,0 +1,79 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.event.entity.player; + +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.client.item.TooltipContext; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; + +public class ItemTooltipEvent extends PlayerEvent { + private final TooltipContext flags; + @Nonnull + private final ItemStack itemStack; + private final List toolTip; + + /** + * This event is fired in {@link ItemStack#getTooltip(PlayerEntity, TooltipContext)}, which in turn is called from it's respective {@link net.minecraft.client.gui.screen.ingame.ContainerScreen}. + * Tooltips are also gathered with a null entityPlayer during startup by {@link net.minecraft.client.MinecraftClient#initializeSearchableContainers()}. + */ + public ItemTooltipEvent(@Nonnull ItemStack itemStack, @Nullable PlayerEntity entityPlayer, List list, TooltipContext flags) { + super(entityPlayer); + this.itemStack = itemStack; + this.toolTip = list; + this.flags = flags; + } + + /** + * Use to determine if the advanced information on item tooltips is being shown, toggled by F3+H. + */ + public TooltipContext getFlags() { + return flags; + } + + /** + * The {@link ItemStack} with the tooltip. + */ + @Nonnull + public ItemStack getItemStack() { + return itemStack; + } + + /** + * The {@link ItemStack} tooltip. + */ + public List getToolTip() { + return toolTip; + } + + /** + * This event is fired with a null player during startup when populating search trees for tooltips. + */ + @Override + @Nullable + public PlayerEntity getPlayer() { + return super.getPlayer(); + } +} diff --git a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java index 4a0e47c9..42860d11 100644 --- a/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java +++ b/patchwork-events-entity/src/main/java/net/minecraftforge/event/entity/player/PlayerEvent.java @@ -19,10 +19,16 @@ package net.minecraftforge.event.entity.player; +import javax.annotation.Nonnull; + import net.minecraftforge.event.entity.living.LivingEvent; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.world.dimension.DimensionType; import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; /** * PlayerEvent is fired whenever an event involving Living entities occurs. @@ -57,16 +63,6 @@ public PlayerEntity getPlayer() { return playerEntity; } - /** - * Called on the server at the end of {@link net.minecraft.server.PlayerManager#onPlayerConnect(net.minecraft.network.ClientConnection, net.minecraft.server.network.ServerPlayerEntity)} - * when the player has finished logging in. - */ - public static class PlayerLoggedInEvent extends PlayerEvent { - public PlayerLoggedInEvent(PlayerEntity player) { - super(player); - } - } - /** * Fired when an Entity is started to be "tracked" by this player (the player receives updates about this entity, e.g. motion). */ @@ -131,17 +127,125 @@ public boolean isWasDeath() { } } + public static class ItemPickupEvent extends PlayerEvent { + /** + * Original EntityItem with current remaining stack size. + */ + private final ItemEntity originalEntity; + /** + * Clone item stack, containing the item and amount picked up. + */ + private final ItemStack stack; + + public ItemPickupEvent(PlayerEntity player, ItemEntity entPickedUp, ItemStack stack) { + super(player); + this.originalEntity = entPickedUp; + this.stack = stack; + } + + public ItemStack getStack() { + return stack; + } + + public ItemEntity getOriginalEntity() { + return originalEntity; + } + } + + public static class ItemCraftedEvent extends PlayerEvent { + @Nonnull + private final ItemStack crafting; + private final Inventory craftMatrix; + + public ItemCraftedEvent(PlayerEntity player, @Nonnull ItemStack crafting, Inventory craftMatrix) { + super(player); + this.crafting = crafting; + this.craftMatrix = craftMatrix; + } + + @Nonnull + public ItemStack getCrafting() { + return this.crafting; + } + + public Inventory getInventory() { + return this.craftMatrix; + } + } + + public static class ItemSmeltedEvent extends PlayerEvent { + @Nonnull + private final ItemStack smelting; + + public ItemSmeltedEvent(PlayerEntity player, @Nonnull ItemStack crafting) { + super(player); + this.smelting = crafting; + } + + @Nonnull + public ItemStack getSmelting() { + return this.smelting; + } + } + + /** + * Called on the server at the end of {@link net.minecraft.server.PlayerManager#onPlayerConnect(net.minecraft.network.ClientConnection, net.minecraft.server.network.ServerPlayerEntity)} + * when the player has finished logging in. + */ + public static class PlayerLoggedInEvent extends PlayerEvent { + public PlayerLoggedInEvent(PlayerEntity player) { + super(player); + } + } + + public static class PlayerLoggedOutEvent extends PlayerEvent { + public PlayerLoggedOutEvent(PlayerEntity player) { + super(player); + } + } + + public static class PlayerRespawnEvent extends PlayerEvent { + private final boolean alive; + + public PlayerRespawnEvent(PlayerEntity player, boolean alive) { + super(player); + this.alive = alive; + } + + /** + * Did this respawn event come from the player conquering the end? + * TODO: Forge should name this to isAlive. + * @return if this respawn was because the player conquered the end + */ + public boolean isEndConquered() { + return this.alive; + } + } + + public static class PlayerChangedDimensionEvent extends PlayerEvent { + private final DimensionType fromDim; + private final DimensionType toDim; + + public PlayerChangedDimensionEvent(PlayerEntity player, DimensionType fromDim, DimensionType toDim) { + super(player); + this.fromDim = fromDim; + this.toDim = toDim; + } + + public DimensionType getFrom() { + return this.fromDim; + } + + public DimensionType getTo() { + return this.toDim; + } + } + /*TODO Events: HarvestCheck BreakSpeed NameFormat LoadFromFile SaveToFile - Visibility - ItemPickupEvent - ItemCraftedEvent - ItemSmeltedEvent - PlayerLoggedOutEvent - PlayerRespawnEvent - PlayerChangedDimensionEvent*/ + Visibility called by ForgeHooks.getPlayerVisibilityDistance, but latter is not called elsewhere*/ } diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java index 3c753fd4..39fc5e20 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/EntityEvents.java @@ -19,20 +19,27 @@ package net.patchworkmc.impl.event.entity; +import java.util.List; +import java.util.Collection; + import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.extensions.IForgeItem; import net.minecraftforge.event.entity.EntityEvent; import net.minecraftforge.event.entity.EntityJoinWorldEvent; +import net.minecraftforge.event.entity.EntityTravelToDimensionEvent; +import net.minecraftforge.event.entity.ProjectileImpactEvent; +import net.minecraftforge.event.entity.living.AnimalTameEvent; import net.minecraftforge.event.entity.living.LivingAttackEvent; import net.minecraftforge.event.entity.living.LivingDamageEvent; import net.minecraftforge.event.entity.living.LivingDeathEvent; +import net.minecraftforge.event.entity.living.LivingDropsEvent; import net.minecraftforge.event.entity.living.LivingEvent; import net.minecraftforge.event.entity.living.LivingFallEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; import net.minecraftforge.event.entity.living.LivingSpawnEvent; import net.minecraftforge.event.entity.player.AttackEntityEvent; -import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.event.entity.player.ItemTooltipEvent; import net.minecraftforge.event.entity.player.PlayerFlyableFallEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.eventbus.api.Event; @@ -40,26 +47,37 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import net.minecraft.client.item.TooltipContext; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityDimensions; import net.minecraft.entity.EntityPose; +import net.minecraft.entity.ItemEntity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.SpawnType; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.passive.AnimalEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.ExplosiveProjectileEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.entity.thrown.ThrownEntity; +import net.minecraft.entity.vehicle.StorageMinecartEntity; import net.minecraft.item.ItemStack; -import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; +import net.minecraft.util.hit.HitResult; import net.minecraft.world.IWorld; import net.minecraft.world.MobSpawnerLogic; import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.fabric.api.event.player.UseItemCallback; +import net.patchworkmc.mixin.event.entity.StorageMinecartEntityAccessor; + public class EntityEvents implements ModInitializer { private static final Logger LOGGER = LogManager.getLogger("patchwork-events-entity"); @@ -91,11 +109,6 @@ public static void onEnteringChunk(Entity entity, int newChunkX, int newChunkZ, MinecraftForge.EVENT_BUS.post(new EntityEvent.EnteringChunk(entity, newChunkX, newChunkZ, oldChunkX, oldChunkZ)); } - // PlayerEvents - public static void onPlayerLoggedIn(ServerPlayerEntity playerEntity) { - MinecraftForge.EVENT_BUS.post(new PlayerEvent.PlayerLoggedInEvent(playerEntity)); - } - public static boolean onLivingAttack(LivingEntity entity, DamageSource src, float damage) { return MinecraftForge.EVENT_BUS.post(new LivingAttackEvent(entity, src, damage)); } @@ -123,6 +136,10 @@ public static float onLivingDamage(LivingEntity entity, DamageSource src, float return MinecraftForge.EVENT_BUS.post(event) ? 0 : event.getAmount(); } + public static boolean onLivingDrops(LivingEntity entity, DamageSource source, Collection drops, int lootingLevel, boolean recentlyHit) { + return MinecraftForge.EVENT_BUS.post(new LivingDropsEvent(entity, source, drops, lootingLevel, recentlyHit)); + } + public static float getEyeHeight(Entity entity, EntityPose pose, EntityDimensions size, float defaultHeight) { EntityEvent.EyeHeight event = new EntityEvent.EyeHeight(entity, pose, size, defaultHeight); MinecraftForge.EVENT_BUS.post(event); @@ -181,6 +198,45 @@ public static boolean attackEntity(PlayerEntity player, Entity target) { return !item.onLeftClickEntity(stack, player, target); } + public static void onItemTooltip(ItemStack itemStack, PlayerEntity entityPlayer, List list, TooltipContext flags) { + MinecraftForge.EVENT_BUS.post(new ItemTooltipEvent(itemStack, entityPlayer, list, flags)); + } + + public static boolean onAnimalTame(AnimalEntity animal, PlayerEntity tamer) { + return MinecraftForge.EVENT_BUS.post(new AnimalTameEvent(animal, tamer)); + } + + public static boolean onProjectileImpact(Entity entity, HitResult ray) { + return MinecraftForge.EVENT_BUS.post(new ProjectileImpactEvent(entity, ray)); + } + + public static boolean onProjectileImpact(ProjectileEntity arrow, HitResult ray) { + return MinecraftForge.EVENT_BUS.post(new ProjectileImpactEvent.Arrow(arrow, ray)); + } + + public static boolean onProjectileImpact(ExplosiveProjectileEntity fireball, HitResult ray) { + return MinecraftForge.EVENT_BUS.post(new ProjectileImpactEvent.Fireball(fireball, ray)); + } + + public static boolean onProjectileImpact(ThrownEntity throwable, HitResult ray) { + return MinecraftForge.EVENT_BUS.post(new ProjectileImpactEvent.Throwable(throwable, ray)); + } + + public static boolean onTravelToDimension(Entity entity, DimensionType dimensionType) { + EntityTravelToDimensionEvent event = new EntityTravelToDimensionEvent(entity, dimensionType); + boolean result = !MinecraftForge.EVENT_BUS.post(event); + + if (!result) { + // Revert variable back to true as it would have been set to false + + if (entity instanceof StorageMinecartEntity) { + ((StorageMinecartEntityAccessor) entity).dropContentsWhenDead(true); + } + } + + return result; + } + @Override public void onInitialize() { UseItemCallback.EVENT.register((player, world, hand) -> { diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/PlayerEvents.java b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/PlayerEvents.java new file mode 100644 index 00000000..4180f018 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/impl/event/entity/PlayerEvents.java @@ -0,0 +1,71 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.event.entity; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.player.EntityItemPickupEvent; +import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.eventbus.api.Event; + +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.world.dimension.DimensionType; + +public class PlayerEvents { + public static void firePlayerChangedDimensionEvent(PlayerEntity player, DimensionType fromDim, DimensionType toDim) { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.PlayerChangedDimensionEvent(player, fromDim, toDim)); + } + + public static void firePlayerLoggedIn(PlayerEntity player) { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.PlayerLoggedInEvent(player)); + } + + public static void firePlayerLoggedOut(PlayerEntity player) { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.PlayerLoggedOutEvent(player)); + } + + public static void firePlayerRespawnEvent(PlayerEntity player, boolean alive) { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.PlayerRespawnEvent(player, alive)); + } + + public static void firePlayerItemPickupEvent(PlayerEntity player, ItemEntity item, ItemStack clone) { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.ItemPickupEvent(player, item, clone)); + } + + public static void firePlayerCraftingEvent(PlayerEntity player, ItemStack crafted, Inventory craftMatrix) { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.ItemCraftedEvent(player, crafted, craftMatrix)); + } + + public static void firePlayerSmeltedEvent(PlayerEntity player, ItemStack smelted) { + MinecraftForge.EVENT_BUS.post(new PlayerEvent.ItemSmeltedEvent(player, smelted)); + } + + /** + * + * @return -1 if the event was cancelled, 0 if the event was denied, 1 if the event was accepted + */ + public static int onItemPickup(PlayerEntity player, ItemEntity entityItem) { + Event event = new EntityItemPickupEvent(player, entityItem); + if (MinecraftForge.EVENT_BUS.post(event)) return -1; + return event.getResult() == Event.Result.ALLOW ? 1 : 0; + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinCraftingResultSlot.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinCraftingResultSlot.java new file mode 100644 index 00000000..560c80cb --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinCraftingResultSlot.java @@ -0,0 +1,58 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.At.Shift; + +import net.minecraft.container.CraftingResultSlot; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.CraftingInventory; +import net.minecraft.item.ItemStack; + +import net.patchworkmc.impl.event.entity.PlayerEvents; + +@Mixin(CraftingResultSlot.class) +public abstract class MixinCraftingResultSlot { + @Shadow + @Final + private PlayerEntity player; + + @Shadow + @Final + private CraftingInventory craftingInv; + + @Inject(method = "onCrafted(Lnet/minecraft/item/ItemStack;)V", + at = @At( + value = "INVOKE", + shift = Shift.AFTER, + ordinal = 0, + target = "net/minecraft/item/ItemStack.onCraft(Lnet/minecraft/world/World;Lnet/minecraft/entity/player/PlayerEntity;I)V" + ) + ) + private void onStackCrafted(ItemStack stack, CallbackInfo ci) { + PlayerEvents.firePlayerCraftingEvent(player, stack, craftingInv); + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinEntity.java index 94513412..1731af44 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinEntity.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinEntity.java @@ -24,12 +24,14 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityDimensions; import net.minecraft.entity.EntityPose; import net.minecraft.entity.EntityType; import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; import net.patchworkmc.impl.event.entity.EntityEvents; @@ -52,4 +54,14 @@ public void hookConstructor(EntityType type, World world, CallbackInfo ci) { EntityEvents.onEntityConstruct(entity); } + + @Inject(method = "changeDimension", + at = @At("HEAD"), + cancellable = true + ) + void patchwork_fireTravelToDimensionEventChangeDimension(DimensionType newDimension, CallbackInfoReturnable cir) { + if (!EntityEvents.onTravelToDimension((Entity) (Object) this, newDimension)) { + cir.setReturnValue(null); + } + } } diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinExplosiveProjectileEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinExplosiveProjectileEntity.java new file mode 100644 index 00000000..5e08f1aa --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinExplosiveProjectileEntity.java @@ -0,0 +1,48 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.entity.projectile.ExplosiveProjectileEntity; +import net.minecraft.util.hit.HitResult; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +@Mixin(ExplosiveProjectileEntity.class) +public abstract class MixinExplosiveProjectileEntity { + @Shadow + protected abstract void onCollision(HitResult hitResult); + + /** + * Mixin to the redirect the onCollision method, to call {@link net.minecraftforge.event.entity.ProjectileImpactEvent}. + * + *

Note: In future versions, the @Redirect should be replaced with a @ModifyVariable to the bl flag.

+ */ + @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/projectile/ExplosiveProjectileEntity;onCollision(Lnet/minecraft/util/hit/HitResult;)V")) + private void hookTick(ExplosiveProjectileEntity entity, HitResult hitResult) { + if (!EntityEvents.onProjectileImpact(entity, hitResult)) { + this.onCollision(hitResult); + } + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinFishingBobberEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinFishingBobberEntity.java new file mode 100644 index 00000000..9b5d6213 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinFishingBobberEntity.java @@ -0,0 +1,72 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import java.util.List; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.player.ItemFishedEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import net.minecraft.entity.projectile.FishingBobberEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.context.LootContext; + +@Mixin(FishingBobberEntity.class) +public abstract class MixinFishingBobberEntity { + @Shadow + private boolean field_7176; // is stuck to block + + @Shadow + public abstract void remove(); + + @Unique + private final ThreadLocal rodDamage = ThreadLocal.withInitial(() -> -1); + + @Inject(method = "method_6957", at = @At(value = "INVOKE", target = "Lnet/minecraft/advancement/criterion/FishingRodHookedCriterion;trigger(Lnet/minecraft/server/network/ServerPlayerEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/entity/projectile/FishingBobberEntity;Ljava/util/Collection;)V", ordinal = 1), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) + private void onReeledIn(ItemStack itemStack, CallbackInfoReturnable cir, int i, LootContext.Builder builder, LootTable lootTable, List list) { + ItemFishedEvent event = new ItemFishedEvent(list, this.field_7176 ? 2 : 1, (FishingBobberEntity) (Object) this); + MinecraftForge.EVENT_BUS.post(event); + + if (event.isCanceled()) { + this.remove(); + cir.setReturnValue(event.getRodDamage()); + } else { + rodDamage.set(event.getRodDamage()); + } + } + + @Inject(method = "method_6957", at = @At("RETURN"), cancellable = true) + private void onRodDamage(ItemStack itemStack, CallbackInfoReturnable cir) { + Integer damage = rodDamage.get(); + + if (damage != -1) { + cir.setReturnValue(damage); + rodDamage.remove(); + } + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinFurnaceOutputSlot.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinFurnaceOutputSlot.java new file mode 100644 index 00000000..b79e43c2 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinFurnaceOutputSlot.java @@ -0,0 +1,47 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.container.FurnaceOutputSlot; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; + +import net.patchworkmc.impl.event.entity.PlayerEvents; + +@Mixin(FurnaceOutputSlot.class) +public abstract class MixinFurnaceOutputSlot { + @Shadow + @Final + private PlayerEntity player; + + @Inject(method = "onCrafted(Lnet/minecraft/item/ItemStack;)V", + at = @At("RETURN") + ) + private void onCraftingFinished(ItemStack stack, CallbackInfo ci) { + PlayerEvents.firePlayerSmeltedEvent(player, stack); + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinHorseBondWithPlayerGoal.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinHorseBondWithPlayerGoal.java new file mode 100644 index 00000000..2b1487ab --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinHorseBondWithPlayerGoal.java @@ -0,0 +1,55 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import java.util.Random; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.entity.ai.goal.HorseBondWithPlayerGoal; +import net.minecraft.entity.passive.HorseBaseEntity; +import net.minecraft.entity.player.PlayerEntity; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +@Mixin(HorseBondWithPlayerGoal.class) +public class MixinHorseBondWithPlayerGoal { + @Shadow + @Final + private HorseBaseEntity horse; + + @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Ljava/util/Random;nextInt(I)I", ordinal = 1)) + private int redirectHorseBondWithPlayerCheck(Random random, int bound) { + int temper = horse.getTemper(); + int nextInt = random.nextInt(bound); + + if (nextInt < temper) { + if (EntityEvents.onAnimalTame(horse, (PlayerEntity) horse.getPassengerList().get(0))) { + return Integer.MAX_VALUE; // Force nextInt > temper + } + } + + return nextInt; + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java new file mode 100644 index 00000000..e4ad21b8 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemEntity.java @@ -0,0 +1,68 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; + +import net.patchworkmc.impl.event.entity.PlayerEvents; + +@Mixin(ItemEntity.class) +public abstract class MixinItemEntity { + @Inject(method = "onPlayerCollision", + at = @At( + value = "INVOKE", + shift = Shift.BEFORE, + ordinal = 0, + target = "net/minecraft/item/ItemStack.isEmpty()Z" + ) + ) + private void onPlayerPickUpItemEntity(PlayerEntity player, CallbackInfo ci) { + ItemEntity me = (ItemEntity) (Object) this; + PlayerEvents.firePlayerItemPickupEvent(player, me, me.getStack().copy()); + } + + int eventResult; + + @Inject(method = "onPlayerCollision", + at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getCount()I"), + cancellable = true + ) + void patchwork_fireItemPickupEvent(PlayerEntity player, CallbackInfo ci) { + eventResult = PlayerEvents.onItemPickup(player, (ItemEntity) (Object) this); + if (eventResult != 1) ci.cancel(); + } + + @Redirect(method = "onPlayerCollision", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerInventory;insertStack(Lnet/minecraft/item/ItemStack;)Z") + ) + boolean patchwork_skipIfEventNotAllowed(PlayerInventory playerInventory, ItemStack stack) { + return eventResult == 1 || playerInventory.insertStack(stack); + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemStack.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemStack.java new file mode 100644 index 00000000..6389a5de --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinItemStack.java @@ -0,0 +1,42 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import java.util.List; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.client.item.TooltipContext; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +@Mixin(ItemStack.class) +public class MixinItemStack { + @Inject(method = "getTooltip", at = @At("RETURN")) + private void onGetTooltip(PlayerEntity player, TooltipContext context, CallbackInfoReturnable> cir) { + EntityEvents.onItemTooltip((ItemStack) (Object) this, player, cir.getReturnValue(), context); + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLivingEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLivingEntity.java index 21fe772a..d7e2f0d1 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLivingEntity.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLivingEntity.java @@ -22,9 +22,7 @@ import java.util.ArrayList; import java.util.Collection; -import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.extensions.IForgeEntity; -import net.minecraftforge.event.entity.living.LivingDropsEvent; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -154,7 +152,7 @@ private void hookDropForDropsEvent(DamageSource src, CallbackInfo info) { IForgeEntity forgeEntity = (IForgeEntity) this; Collection drops = forgeEntity.captureDrops(null); - if (!MinecraftForge.EVENT_BUS.post(new LivingDropsEvent(entity, src, drops, dropLootingLevel.get(), playerHitTimer > 0))) { + if (!EntityEvents.onLivingDrops(entity, src, drops, dropLootingLevel.get(), playerHitTimer > 0)) { for (ItemEntity item : drops) { forgeEntity.getEntity().world.spawnEntity(item); } diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLlamaSpitEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLlamaSpitEntity.java new file mode 100644 index 00000000..6581436e --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinLlamaSpitEntity.java @@ -0,0 +1,52 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.entity.projectile.LlamaSpitEntity; +import net.minecraft.util.hit.HitResult; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +@Mixin(LlamaSpitEntity.class) +public class MixinLlamaSpitEntity { + /** + * Mixin to the projectile hit method, to call {@link net.minecraftforge.event.entity.ProjectileImpactEvent}. + * + *

This will cancel the rest of method_7481 if the forge event pipeline requests it, but a Fabric mod with higher + * priority can still inject into its head.

+ * + *

This mixin is implemented differently from the other onProjectileImpact mixins because there is no check that + * the hit didn't miss before calling onHit, so we need to maintain calls to onHit for missed shots, but still need + * to check that it didn't miss before calling onProjectileImpact.

+ */ + @Inject(method = "method_7481", at = @At("HEAD"), cancellable = true) + private void hookHit(HitResult hitResult, CallbackInfo callback) { + LlamaSpitEntity entity = (LlamaSpitEntity) (Object) this; + + if (EntityEvents.onProjectileImpact(entity, hitResult)) { + callback.cancel(); + } + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinOcelotEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinOcelotEntity.java new file mode 100644 index 00000000..63d8a693 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinOcelotEntity.java @@ -0,0 +1,53 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.OcelotEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.Hand; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +@Mixin(OcelotEntity.class) +public abstract class MixinOcelotEntity extends AnimalEntity { + @Shadow + protected abstract void showEmoteParticle(boolean positive); + + protected MixinOcelotEntity() { + //noinspection ConstantConditions + super(null, null); + } + + @Inject(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/OcelotEntity;setTrusting(Z)V"), cancellable = true) + private void onOcelotTrusting(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + if (EntityEvents.onAnimalTame(this, player)) { + this.showEmoteParticle(false); + this.world.sendEntityStatus(this, (byte) 40); + cir.setReturnValue(true); + } + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinPlayerEntityRenderer.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinPlayerEntityRenderer.java new file mode 100644 index 00000000..2096e1ed --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinPlayerEntityRenderer.java @@ -0,0 +1,50 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import net.minecraftforge.client.event.RenderPlayerEvent; +import net.minecraftforge.common.MinecraftForge; + +import net.minecraft.client.network.AbstractClientPlayerEntity; +import net.minecraft.client.render.entity.PlayerEntityRenderer; + +@Mixin(PlayerEntityRenderer.class) +public class MixinPlayerEntityRenderer { + @Inject(method = "render", at = @At(value = "INVOKE", ordinal = 0, shift = Shift.BEFORE, + target = "net/minecraft/client/network/AbstractClientPlayerEntity.isInSneakingPose()Z"), cancellable = true) + private void preRender(AbstractClientPlayerEntity playerEntity, double x, double y, double z, float entityYaw, float partialTicks, CallbackInfo ci) { + PlayerEntityRenderer me = (PlayerEntityRenderer) (Object) this; + + if (MinecraftForge.EVENT_BUS.post(new RenderPlayerEvent.Pre(playerEntity, me, partialTicks, x, y, z))) { + ci.cancel(); + } + } + + @Inject(method = "render", at = @At("RETURN")) + private void postRender(AbstractClientPlayerEntity playerEntity, double x, double y, double z, float entityYaw, float partialTicks, CallbackInfo ci) { + PlayerEntityRenderer me = (PlayerEntityRenderer) (Object) this; + MinecraftForge.EVENT_BUS.post(new RenderPlayerEvent.Post(playerEntity, me, partialTicks, x, y, z)); + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinPlayerManager.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinPlayerManager.java index afe509d9..3669c9ff 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinPlayerManager.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinPlayerManager.java @@ -23,17 +23,29 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import net.minecraft.network.ClientConnection; import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.world.dimension.DimensionType; -import net.patchworkmc.impl.event.entity.EntityEvents; +import net.patchworkmc.impl.event.entity.PlayerEvents; @Mixin(PlayerManager.class) public class MixinPlayerManager { @Inject(method = "onPlayerConnect", at = @At("RETURN")) private void hookPlayerLogin(ClientConnection connection, ServerPlayerEntity player, CallbackInfo callback) { - EntityEvents.onPlayerLoggedIn(player); + PlayerEvents.firePlayerLoggedIn(player); + } + + @Inject(method = "remove", at = @At("HEAD")) + private void hookPlayerLogout(ServerPlayerEntity player, CallbackInfo callback) { + PlayerEvents.firePlayerLoggedOut(player); + } + + @Inject(method = "respawnPlayer", at = @At("RETURN")) + private void hookPlayerRespawn(ServerPlayerEntity player, DimensionType dimension, boolean alive, CallbackInfoReturnable callback) { + PlayerEvents.firePlayerRespawnEvent(player, alive); } } diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinProjectileEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinProjectileEntity.java new file mode 100644 index 00000000..27da7c5d --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinProjectileEntity.java @@ -0,0 +1,54 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.util.hit.HitResult; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +@Mixin(ProjectileEntity.class) +public class MixinProjectileEntity { + /** + * Mixin to the projectile hit method, to call {@link net.minecraftforge.event.entity.ProjectileImpactEvent}. + * + *

This will cancel the rest of onHit if the event pipeline requests it, but a Fabric mod with higher + * priority can still inject into its head.

+ * + *

This mixin is implemented differently from the other onProjectileImpact mixins because there is no check that + * the hit didn't miss before calling onHit, so we need to maintain calls to onHit for missed shots, but still need + * to check that it didn't miss before calling onProjectileImpact.

+ */ + @Inject(method = "onHit(Lnet/minecraft/util/hit/HitResult;)V", at = @At("HEAD"), cancellable = true) + private void hookHit(HitResult hitResult, CallbackInfo callback) { + if (hitResult.getType() != HitResult.Type.MISS) { + ProjectileEntity entity = (ProjectileEntity) (Object) this; + + if (EntityEvents.onProjectileImpact(entity, hitResult)) { + callback.cancel(); + } + } + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinServerPlayerEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinServerPlayerEntity.java index 90bfc860..73066402 100644 --- a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinServerPlayerEntity.java +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinServerPlayerEntity.java @@ -19,21 +19,37 @@ package net.patchworkmc.mixin.event.entity; +import com.mojang.authlib.GameProfile; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerEvent; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; +import net.minecraft.world.level.LevelProperties; import net.patchworkmc.impl.event.entity.EntityEvents; +import net.patchworkmc.impl.event.entity.PlayerEvents; @Mixin(ServerPlayerEntity.class) -public class MixinServerPlayerEntity { +public abstract class MixinServerPlayerEntity extends PlayerEntity { + public MixinServerPlayerEntity(World world, GameProfile profile) { + super(world, profile); + } + @Inject(method = "onDeath", at = @At("HEAD"), cancellable = true) private void hookDeath(DamageSource source, CallbackInfo callback) { LivingEntity entity = (LivingEntity) (Object) this; @@ -49,4 +65,75 @@ private void hookCopyFromForCloneEvent(ServerPlayerEntity oldPlayer, boolean ali ServerPlayerEntity speThis = (ServerPlayerEntity) (Object) this; MinecraftForge.EVENT_BUS.post(new PlayerEvent.Clone(speThis, oldPlayer, !alive)); } + + @Inject(method = "teleport", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/network/ServerPlayerEntity;getServerWorld()Lnet/minecraft/server/world/ServerWorld;" + ), + cancellable = true + ) + void patchwork_fireTravelToDimensionEventTeleport(ServerWorld targetWorld, double x, double y, double z, float yaw, float pitch, CallbackInfo ci) { + if (!EntityEvents.onTravelToDimension(this, targetWorld.dimension.getType())) { + ci.cancel(); + } + } + + //////////////////////////////////// + // PlayerChangedDimensionEvent + //////////////////////////////////// + @Inject(method = "teleport", + at = @At( + value = "INVOKE", + shift = Shift.AFTER, + ordinal = 0, + target = "net/minecraft/server/PlayerManager.method_14594(Lnet/minecraft/server/network/ServerPlayerEntity;)V" + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) // PlayerManager.method_14594 -> sendInventory + private void teleport_sendInventory(ServerWorld targetWorld, double x, double y, double z, float yaw, float pitch, CallbackInfo info, ServerWorld serverWorld, LevelProperties levelProperties) { + ServerPlayerEntity me = (ServerPlayerEntity) (Object) this; + PlayerEvents.firePlayerChangedDimensionEvent(me, serverWorld.dimension.getType(), me.dimension); + } + + @Unique + private static final ThreadLocal changeDimension_from = new ThreadLocal<>(); + + @Inject(method = "changeDimension", + at = @At("HEAD"), + cancellable = true + ) + void patchwork_fireTravelToDimensionEventChangeDimensionPlayer(DimensionType newDimension, CallbackInfoReturnable cir) { + if (!EntityEvents.onTravelToDimension(this, newDimension)) { + cir.setReturnValue(null); + } + } + + @Inject(method = "changeDimension", + at = @At( + value = "INVOKE", + shift = Shift.AFTER, + ordinal = 0, + target = "net/minecraft/server/MinecraftServer.getWorld(Lnet/minecraft/world/dimension/DimensionType;)Lnet/minecraft/server/world/ServerWorld;" + ) + ) + private void changeDimension_getWorld(DimensionType newDimension, CallbackInfoReturnable info) { + ServerPlayerEntity me = (ServerPlayerEntity) (Object) this; + changeDimension_from.set(me.dimension); + } + + @Inject(method = "changeDimension", + at = @At( + value = "FIELD", + shift = Shift.AFTER, + ordinal = 0, + target = "net/minecraft/server/network/ServerPlayerEntity.field_13979:I" + ) + ) // ServerPlayerEntity.field_13979 -> lastFoodLevel + private void changeDimension_lastFoodLevel(DimensionType newDimension, CallbackInfoReturnable info) { + ServerPlayerEntity me = (ServerPlayerEntity) (Object) this; + DimensionType from = changeDimension_from.get(); + changeDimension_from.set(null); + PlayerEvents.firePlayerChangedDimensionEvent(me, from, newDimension); + } } diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinShulkerBulletEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinShulkerBulletEntity.java new file mode 100644 index 00000000..4f9a1062 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinShulkerBulletEntity.java @@ -0,0 +1,48 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.entity.projectile.ShulkerBulletEntity; +import net.minecraft.util.hit.HitResult; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +@Mixin(ShulkerBulletEntity.class) +public abstract class MixinShulkerBulletEntity { + @Shadow + protected abstract void onHit(HitResult hitResult); + + /** + * Mixin to the redirect the ShulkerBulletEntity hit method, to call {@link net.minecraftforge.event.entity.ProjectileImpactEvent}. + * + *

Note: In future versions, the @Redirect should be replaced with a @ModifyVariable to the bl flag.

+ */ + @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/projectile/ShulkerBulletEntity;onHit(Lnet/minecraft/util/hit/HitResult;)V")) + private void hookTick(ShulkerBulletEntity entity, HitResult hitResult) { + if (!EntityEvents.onProjectileImpact(entity, hitResult)) { + this.onHit(hitResult); + } + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinTameableEntitySubclass.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinTameableEntitySubclass.java new file mode 100644 index 00000000..3151c730 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinTameableEntitySubclass.java @@ -0,0 +1,54 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import java.util.Random; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.entity.passive.ParrotEntity; +import net.minecraft.entity.passive.TameableEntity; +import net.minecraft.entity.passive.WolfEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.Hand; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +// CatEntity is intentionally omitted. See MinecraftForge/#7171 +@Mixin({ParrotEntity.class, WolfEntity.class}) +public abstract class MixinTameableEntitySubclass extends TameableEntity { + protected MixinTameableEntitySubclass() { + //noinspection ConstantConditions + super(null, null); + } + + @Redirect(method = "interactMob", at = @At(value = "INVOKE", target = "Ljava/util/Random;nextInt(I)I")) + private int redirectTameCheck(Random random, int bound, PlayerEntity player, Hand hand) { + int i = random.nextInt(bound); + + if (i != 0 || EntityEvents.onAnimalTame(this, player)) { + return -1; // Check failed + } + + return 0; // Check succeeds + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinThrownEntity.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinThrownEntity.java new file mode 100644 index 00000000..bc0d7ef0 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/MixinThrownEntity.java @@ -0,0 +1,48 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.entity.thrown.ThrownEntity; +import net.minecraft.util.hit.HitResult; + +import net.patchworkmc.impl.event.entity.EntityEvents; + +@Mixin(ThrownEntity.class) +public abstract class MixinThrownEntity { + @Shadow + protected abstract void onCollision(HitResult hitResult); + + /** + * Mixin to the redirect the ThrownEntity hit method, to call {@link net.minecraftforge.event.entity.ProjectileImpactEvent}. + * + *

Note: In future versions, the @Redirect should be replaced with a @ModifyVariable to the bl flag.

+ */ + @Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/thrown/ThrownEntity;onCollision(Lnet/minecraft/util/hit/HitResult;)V")) + private void hookTick(ThrownEntity entity, HitResult hitResult) { + if (!EntityEvents.onProjectileImpact(entity, hitResult)) { + this.onCollision(hitResult); + } + } +} diff --git a/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/StorageMinecartEntityAccessor.java b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/StorageMinecartEntityAccessor.java new file mode 100644 index 00000000..7a36eb59 --- /dev/null +++ b/patchwork-events-entity/src/main/java/net/patchworkmc/mixin/event/entity/StorageMinecartEntityAccessor.java @@ -0,0 +1,31 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.event.entity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.entity.vehicle.StorageMinecartEntity; + +@Mixin(StorageMinecartEntity.class) +public interface StorageMinecartEntityAccessor { + @Accessor("field_7733") + void dropContentsWhenDead(boolean value); +} diff --git a/patchwork-events-entity/src/main/resources/fabric.mod.json b/patchwork-events-entity/src/main/resources/fabric.mod.json index a9cd92a8..50574655 100644 --- a/patchwork-events-entity/src/main/resources/fabric.mod.json +++ b/patchwork-events-entity/src/main/resources/fabric.mod.json @@ -6,7 +6,7 @@ "name": "Patchwork Entity Events", "description": "Provides an implementation of Minecraft Forge's Entity Events", "authors": [ - "coderbot" + "PatchworkMC" ], "license": "LGPL-2.1-only", "environment": "*", @@ -16,9 +16,8 @@ ] }, "depends": { - "fabricloader": ">=0.8.4", - "fabric": "*", - "patchwork-fml": "*", + "patchwork-api-base": "*", + "patchwork-extensions": "*", "patchwork-extensions-item": "*" }, "mixins": [ diff --git a/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json b/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json index 7b7c0180..895bcd44 100644 --- a/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json +++ b/patchwork-events-entity/src/main/resources/patchwork-events-entity.mixins.json @@ -3,24 +3,37 @@ "package": "net.patchworkmc.mixin.event.entity", "compatibilityLevel": "JAVA_8", "mixins": [ + "MixinCraftingResultSlot", "MixinEntity", "MixinEntityTrackerEntry", "MixinEntityType", "MixinExperienceOrbEntity", + "MixinExplosiveProjectileEntity", + "MixinFishingBobberEntity", + "MixinFurnaceOutputSlot", + "MixinHorseBondWithPlayerGoal", + "MixinItemEntity", "MixinLivingEntity", + "MixinLlamaSpitEntity", "MixinMobEntity", "MixinMobSpawnerLogic", "MixinPlayerEntity", "MixinPlayerManager", + "MixinProjectileEntity", "MixinServerPlayerEntity", "MixinServerWorld", + "MixinShulkerBulletEntity", "MixinSpawnHelper", - "MixinWorldChunk" + "MixinThrownEntity", + "MixinWorldChunk", + "StorageMinecartEntityAccessor" ], "client": [ - "MixinClientWorld", "MixinClientPlayerEntity", - "MixinOtherClientPlayerEntity" + "MixinClientWorld", + "MixinItemStack", + "MixinOtherClientPlayerEntity", + "MixinPlayerEntityRenderer" ], "injectors": { "defaultRequire": 1 diff --git a/patchwork-events-input/build.gradle b/patchwork-events-input/build.gradle index 3dccb3dc..f1ce9aa8 100644 --- a/patchwork-events-input/build.gradle +++ b/patchwork-events-input/build.gradle @@ -1,6 +1,6 @@ archivesBaseName = "patchwork-events-input" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') } diff --git a/patchwork-events-input/src/main/java/net/patchworkmc/api/input/ForgeMouse.java b/patchwork-events-input/src/main/java/net/patchworkmc/api/input/ForgeMouse.java new file mode 100644 index 00000000..364e019f --- /dev/null +++ b/patchwork-events-input/src/main/java/net/patchworkmc/api/input/ForgeMouse.java @@ -0,0 +1,26 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.api.input; + +public interface ForgeMouse { + boolean isMiddleDown(); + double getXVelocity(); + double getYVelocity(); +} diff --git a/patchwork-events-input/src/main/java/net/patchworkmc/impl/event/input/InputEvents.java b/patchwork-events-input/src/main/java/net/patchworkmc/impl/event/input/InputEvents.java new file mode 100644 index 00000000..65a2951b --- /dev/null +++ b/patchwork-events-input/src/main/java/net/patchworkmc/impl/event/input/InputEvents.java @@ -0,0 +1,48 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.event.input; + +import net.minecraftforge.client.event.InputEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.eventbus.api.Event; + +import net.minecraft.client.Mouse; + +import net.patchworkmc.api.input.ForgeMouse; + +public class InputEvents { + public static void fireMouseInput(int button, int action, int mods) { + MinecraftForge.EVENT_BUS.post(new InputEvent.MouseInputEvent(button, action, mods)); + } + + public static void fireKeyInput(int key, int scanCode, int action, int modifiers) { + MinecraftForge.EVENT_BUS.post(new InputEvent.KeyInputEvent(key, scanCode, action, modifiers)); + } + + public static boolean onMouseScroll(Mouse mouseHelper, double scrollDelta) { + final Event event = new InputEvent.MouseScrollEvent(scrollDelta, mouseHelper.wasLeftButtonClicked(), ((ForgeMouse) mouseHelper).isMiddleDown(), mouseHelper.wasRightButtonClicked(), mouseHelper.getX(), mouseHelper.getY()); + + return MinecraftForge.EVENT_BUS.post(event); + } + + public static boolean onRawMouseClicked(int button, int action, int mods) { + return MinecraftForge.EVENT_BUS.post(new InputEvent.RawMouseEvent(button, action, mods)); + } +} diff --git a/patchwork-events-input/src/main/java/net/patchworkmc/mixin/event/input/MixinKeyboard.java b/patchwork-events-input/src/main/java/net/patchworkmc/mixin/event/input/MixinKeyboard.java index 70d1d82b..0891efb3 100644 --- a/patchwork-events-input/src/main/java/net/patchworkmc/mixin/event/input/MixinKeyboard.java +++ b/patchwork-events-input/src/main/java/net/patchworkmc/mixin/event/input/MixinKeyboard.java @@ -19,8 +19,6 @@ package net.patchworkmc.mixin.event.input; -import net.minecraftforge.client.event.InputEvent; -import net.minecraftforge.common.MinecraftForge; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -30,15 +28,17 @@ import net.minecraft.client.Keyboard; import net.minecraft.client.MinecraftClient; +import net.patchworkmc.impl.event.input.InputEvents; + @Mixin(Keyboard.class) public abstract class MixinKeyboard { @Shadow MinecraftClient client; @Inject(method = "onKey", at = @At("RETURN")) - private void fireKeyInput(long window, int key, int scancode, int i, int j, CallbackInfo info) { + private void fireKeyInput(long window, int key, int scancode, int action, int modifiers, CallbackInfo info) { if (window == this.client.window.getHandle()) { - MinecraftForge.EVENT_BUS.post(new InputEvent.KeyInputEvent(key, scancode, i, j)); + InputEvents.fireKeyInput(key, scancode, action, modifiers); } } } diff --git a/patchwork-events-input/src/main/java/net/patchworkmc/mixin/event/input/MixinMouse.java b/patchwork-events-input/src/main/java/net/patchworkmc/mixin/event/input/MixinMouse.java index 06827afb..37d3d483 100644 --- a/patchwork-events-input/src/main/java/net/patchworkmc/mixin/event/input/MixinMouse.java +++ b/patchwork-events-input/src/main/java/net/patchworkmc/mixin/event/input/MixinMouse.java @@ -19,9 +19,6 @@ package net.patchworkmc.mixin.event.input; -import net.minecraftforge.client.event.InputEvent; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.eventbus.api.Event; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -32,37 +29,52 @@ import net.minecraft.client.Mouse; -@Mixin(Mouse.class) -public abstract class MixinMouse { - @Shadow - boolean middleButtonClicked; - @Shadow - abstract boolean wasLeftButtonClicked(); - @Shadow - abstract boolean wasRightButtonClicked(); - @Shadow - abstract double getX(); - @Shadow - abstract double getY(); +import net.patchworkmc.api.input.ForgeMouse; +import net.patchworkmc.impl.event.input.InputEvents; +@Mixin(Mouse.class) +public abstract class MixinMouse implements ForgeMouse { @Inject(method = "onMouseButton", at = @At("RETURN"), cancellable = true) private void fireMouseInput(long window, int button, int action, int mods, CallbackInfo info) { - MinecraftForge.EVENT_BUS.post(new InputEvent.MouseInputEvent(button, action, mods)); + InputEvents.fireMouseInput(button, action, mods); } @Inject(method = "onMouseButton", at = @At(value = "FIELD", ordinal = 3, target = "Lnet/minecraft/client/Mouse;client:Lnet/minecraft/client/MinecraftClient;", shift = Shift.BEFORE), cancellable = true) private void onRawMouseClicked(long window, int button, int action, int mods, CallbackInfo info) { - if (MinecraftForge.EVENT_BUS.post(new InputEvent.RawMouseEvent(button, action, mods))) { + if (InputEvents.onRawMouseClicked(button, action, mods)) { info.cancel(); } } @Inject(method = "onMouseScroll", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;isSpectator()Z", shift = Shift.BEFORE), cancellable = true) - private void onMouseScroll(long window, double d, double e, CallbackInfo info, double f, float i) { - final Event event = new InputEvent.MouseScrollEvent(f, wasLeftButtonClicked(), middleButtonClicked, wasRightButtonClicked(), getX(), getY()); - - if (MinecraftForge.EVENT_BUS.post(event)) { + private void onMouseScroll(long window, double d, double e, CallbackInfo info, double scrollDelta, float i) { + if (InputEvents.onMouseScroll((Mouse) (Object) this, scrollDelta)) { info.cancel(); } } + + // Methods added by forge + @Shadow + boolean middleButtonClicked; + + @Shadow + double cursorDeltaX; + + @Shadow + double cursorDeltaY; + + @Override + public boolean isMiddleDown() { + return middleButtonClicked; + } + + @Override + public double getXVelocity() { + return cursorDeltaX; + } + + @Override + public double getYVelocity() { + return cursorDeltaY; + } } diff --git a/patchwork-events-input/src/main/resources/fabric.mod.json b/patchwork-events-input/src/main/resources/fabric.mod.json index 8ec7b9f3..7b8dd8a5 100644 --- a/patchwork-events-input/src/main/resources/fabric.mod.json +++ b/patchwork-events-input/src/main/resources/fabric.mod.json @@ -13,8 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", - "patchwork-fml": "*" + "patchwork-api-base": "*" }, "mixins": [ "patchwork-events-input.mixins.json" diff --git a/patchwork-events-lifecycle/build.gradle b/patchwork-events-lifecycle/build.gradle index 6d10be9c..3ffddffa 100644 --- a/patchwork-events-lifecycle/build.gradle +++ b/patchwork-events-lifecycle/build.gradle @@ -1,6 +1,7 @@ archivesBaseName = "patchwork-events-lifecycle" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.1") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-fml', configuration: 'dev') } diff --git a/patchwork-events-lifecycle/src/main/java/net/minecraftforge/event/TickEvent.java b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/event/TickEvent.java index b05ac040..16e4ac59 100644 --- a/patchwork-events-lifecycle/src/main/java/net/minecraftforge/event/TickEvent.java +++ b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/event/TickEvent.java @@ -45,11 +45,11 @@ public enum Phase { START, END; } - /* TODO public static class ServerTickEvent extends TickEvent { + public static class ServerTickEvent extends TickEvent { public ServerTickEvent(Phase phase) { super(Type.SERVER, LogicalSide.SERVER, phase); } - }*/ + } public static class ClientTickEvent extends TickEvent { public ClientTickEvent(Phase phase) { @@ -77,12 +77,12 @@ public PlayerTickEvent(Phase phase, PlayerEntity player) { } } - /* TODO public static class RenderTickEvent extends TickEvent { + public static class RenderTickEvent extends TickEvent { public final float renderTickTime; public RenderTickEvent(Phase phase, float renderTickTime) { super(Type.RENDER, LogicalSide.CLIENT, phase); this.renderTickTime = renderTickTime; } - }*/ + } } diff --git a/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/event/server/FMLServerStoppedEvent.java b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/event/server/FMLServerStoppedEvent.java new file mode 100644 index 00000000..fdd56c06 --- /dev/null +++ b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/event/server/FMLServerStoppedEvent.java @@ -0,0 +1,35 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fml.event.server; + +import net.minecraft.server.MinecraftServer; + +/** + * Called after {@link FMLServerStoppingEvent} when the server has completely shut down. + * Called immediately before shutting down, on the dedicated server, and before returning + * to the main menu on the client. + * + * @author cpw + */ +public class FMLServerStoppedEvent extends ServerLifecycleEvent { + public FMLServerStoppedEvent(MinecraftServer server) { + super(server); + } +} diff --git a/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java index c3c1b8fc..034bf4bb 100644 --- a/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java +++ b/patchwork-events-lifecycle/src/main/java/net/minecraftforge/fml/server/ServerLifecycleHooks.java @@ -19,6 +19,8 @@ package net.minecraftforge.fml.server; +import java.util.concurrent.CountDownLatch; + import net.minecraft.server.MinecraftServer; import net.patchworkmc.impl.event.lifecycle.LifecycleEvents; @@ -28,6 +30,7 @@ */ public class ServerLifecycleHooks { public static MinecraftServer currentServer; + public static volatile CountDownLatch exitLatch = null; public static MinecraftServer getCurrentServer() { return currentServer; @@ -48,4 +51,8 @@ public static boolean handleServerStarting(final MinecraftServer server) { public static void handleServerStarted(final MinecraftServer server) { LifecycleEvents.handleServerStarted(server); } + + public static void handleServerStopped(final MinecraftServer server) { + LifecycleEvents.handleServerStopped(server); + } } diff --git a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/impl/event/lifecycle/LifecycleEvents.java b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/impl/event/lifecycle/LifecycleEvents.java index 8c950a0c..b1bc0e4e 100644 --- a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/impl/event/lifecycle/LifecycleEvents.java +++ b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/impl/event/lifecycle/LifecycleEvents.java @@ -20,6 +20,7 @@ package net.patchworkmc.impl.event.lifecycle; import java.nio.file.Path; +import java.util.concurrent.CountDownLatch; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.TickEvent; @@ -30,20 +31,22 @@ import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent; import net.minecraftforge.fml.event.server.FMLServerStartedEvent; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; +import net.minecraftforge.fml.event.server.FMLServerStoppedEvent; import net.minecraftforge.fml.loading.FileUtils; import net.minecraftforge.fml.server.ServerLifecycleHooks; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.MinecraftDedicatedServer; +import net.minecraft.server.integrated.IntegratedServer; import net.minecraft.world.World; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.server.ServerStartCallback; import net.fabricmc.fabric.api.event.world.WorldTickCallback; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; public class LifecycleEvents implements ModInitializer { - private static Runnable loadCompleteCallback; - public static void fireWorldTickEvent(TickEvent.Phase phase, World world) { LogicalSide side = world.isClient() ? LogicalSide.CLIENT : LogicalSide.SERVER; TickEvent.WorldTickEvent event = new TickEvent.WorldTickEvent(side, phase, world); @@ -55,16 +58,33 @@ public static void fireClientTickEvent(TickEvent.Phase phase) { MinecraftForge.EVENT_BUS.post(new TickEvent.ClientTickEvent(phase)); } + public static void fireRenderTickEvent(TickEvent.Phase phase, float renderTickTime) { + // TODO - Call net.minecraftforge.client.model.animation.Animation#setClientPartialTickTime on start phase + MinecraftForge.EVENT_BUS.post(new TickEvent.RenderTickEvent(phase, renderTickTime)); + } + + public static void fireServerTickEvent(TickEvent.Phase phase) { + MinecraftForge.EVENT_BUS.post(new TickEvent.ServerTickEvent(phase)); + } + public static void firePlayerTickEvent(TickEvent.Phase phase, PlayerEntity player) { MinecraftForge.EVENT_BUS.post(new TickEvent.PlayerTickEvent(phase, player)); } - public static void handleServerStarting(final MinecraftServer server) { + /** + * Mixes into {@link IntegratedServer} and {@link MinecraftDedicatedServer} in order to implement + * {@link net.minecraftforge.fml.event.server.FMLServerStartingEvent}. This event fires right before the implementations + * return true from setupServer. Returning false from the callback cancels the + * server's startup, however, it's important to note that this event isn't actually cancellable in Forge! + */ + public static boolean handleServerStarting(final MinecraftServer server) { // TODO: Forge loads language data here. I haven't found any mods that use this behavior. if (MinecraftForge.EVENT_BUS.post(new FMLServerStartingEvent(server))) { throw new UnsupportedOperationException("FMLServerStartingEvent is not cancellable!"); } + + return true; } public static void handleServerStarted(final MinecraftServer server) { @@ -85,12 +105,16 @@ private static Path getServerConfigPath(final MinecraftServer server) { return serverConfig; } - public static void setLoadCompleteCallback(Runnable callback) { - loadCompleteCallback = callback; - } + public static void handleServerStopped(final MinecraftServer server) { + MinecraftForge.EVENT_BUS.post(new FMLServerStoppedEvent(server)); + ServerLifecycleHooks.currentServer = null; + LogicalSidedProvider.setServer(null); + CountDownLatch latch = ServerLifecycleHooks.exitLatch; - public static void handleLoadComplete() { - loadCompleteCallback.run(); + if (latch != null) { + latch.countDown(); + ServerLifecycleHooks.exitLatch = null; + } } @Override @@ -112,5 +136,8 @@ public void onInitialize() { throw new UnsupportedOperationException("A mod tried to set the server's motd during handling FMLServerStartedEvent, this isn't implemented yet."); } }); + + ServerTickEvents.START_SERVER_TICK.register(server -> fireServerTickEvent(TickEvent.Phase.START)); + ServerTickEvents.END_SERVER_TICK.register(server -> fireServerTickEvent(TickEvent.Phase.END)); } } diff --git a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinIntegratedServer.java b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinIntegratedServer.java index 7ad7fcd9..aee17cf2 100644 --- a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinIntegratedServer.java +++ b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinIntegratedServer.java @@ -22,6 +22,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import net.minecraft.server.MinecraftServer; @@ -35,4 +36,10 @@ public class MixinIntegratedServer { private void onServerAboutToStart(CallbackInfoReturnable cir) { LifecycleEvents.handleServerAboutToStart((MinecraftServer) (Object) this); } + + @Inject(method = "setupServer", at = @At(value = "RETURN", ordinal = 0), cancellable = true, slice = + @Slice(from = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.setMotd(Ljava/lang/String;)V"))) + private void handleServerStarting(CallbackInfoReturnable cir) { + cir.setReturnValue(LifecycleEvents.handleServerStarting((MinecraftServer) (Object) this)); + } } diff --git a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftClient.java b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftClient.java index d1e5d76d..88bd6bf4 100644 --- a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftClient.java +++ b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftClient.java @@ -19,14 +19,18 @@ package net.patchworkmc.mixin.event.lifecycle; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.objectweb.asm.Opcodes; import net.minecraftforge.event.TickEvent; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.RenderTickCounter; import net.patchworkmc.impl.event.lifecycle.LifecycleEvents; @@ -47,8 +51,32 @@ private void hookClientTickEnd(CallbackInfo info) { LifecycleEvents.fireClientTickEvent(TickEvent.Phase.END); } - @Inject(method = "init", at = @At("RETURN")) - private void hookClientInit(CallbackInfo ci) { - LifecycleEvents.handleLoadComplete(); + @Shadow + @Final + private RenderTickCounter renderTickCounter; + + // Note: for some reason, ordinal does not consider ldc, which means ldc is completely useless here. + // It's kept here for the sake of reference/readability only. + @Inject(method = "render", at = @At( + value = "INVOKE_STRING", + target = "net/minecraft/util/profiler/DisableableProfiler.swap(Ljava/lang/String;)V", + args = "ldc=gameRenderer", + ordinal = 1)) + private void hookRenderTickStart(CallbackInfo ci) { + LifecycleEvents.fireRenderTickEvent(TickEvent.Phase.START, this.renderTickCounter.tickDelta); + } + + @Inject(method = "render", + slice = @Slice(from = @At( + value = "INVOKE_STRING", + target = "net/minecraft/util/profiler/DisableableProfiler.swap(Ljava/lang/String;)V", + args = "ldc=toasts")), + at = @At( + value = "INVOKE", + target = "net/minecraft/util/profiler/DisableableProfiler.pop()V", + shift = At.Shift.AFTER, + ordinal = 0)) + private void hookRenderTickEnd(CallbackInfo ci) { + LifecycleEvents.fireRenderTickEvent(TickEvent.Phase.END, this.renderTickCounter.tickDelta); } } diff --git a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftDedicatedServer.java b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftDedicatedServer.java index 8988b3f7..b0b51ed3 100644 --- a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftDedicatedServer.java +++ b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftDedicatedServer.java @@ -22,7 +22,8 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.MinecraftDedicatedServer; @@ -32,8 +33,13 @@ @Mixin(MinecraftDedicatedServer.class) public class MixinMinecraftDedicatedServer { @Inject(method = "setupServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/UserCache;setUseRemote(Z)V", shift = At.Shift.AFTER)) - private void onServerAboutToStart(CallbackInfo ci) { - LifecycleEvents.handleLoadComplete(); // This is a "multithreaded" event that would be called around this time. + private void onServerAboutToStart(CallbackInfoReturnable cir) { LifecycleEvents.handleServerAboutToStart((MinecraftServer) (Object) this); } + + @Inject(method = "setupServer", at = @At(value = "RETURN", ordinal = 0), cancellable = true, slice = + @Slice(from = @At(value = "INVOKE", target = "net/minecraft/item/Item.appendStacks(Lnet/minecraft/item/ItemGroup;Lnet/minecraft/util/DefaultedList;)V"))) + private void handleServerStarting(CallbackInfoReturnable cir) { + cir.setReturnValue(LifecycleEvents.handleServerStarting((MinecraftServer) (Object) this)); + } } diff --git a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServerSubclass.java b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java similarity index 56% rename from patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServerSubclass.java rename to patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java index ec0edf93..6527d290 100644 --- a/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServerSubclass.java +++ b/patchwork-events-lifecycle/src/main/java/net/patchworkmc/mixin/event/lifecycle/MixinMinecraftServer.java @@ -22,24 +22,16 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.dedicated.MinecraftDedicatedServer; -import net.minecraft.server.integrated.IntegratedServer; import net.patchworkmc.impl.event.lifecycle.LifecycleEvents; -/** - * Mixes into {@link IntegratedServer} and {@link MinecraftDedicatedServer} in order to implement - * {@link net.minecraftforge.fml.event.server.FMLServerStartingEvent}. This event fires right before the implementations - * return true from setupServer. Returning false from the callback cancels the - * server's startup, however, it's important to note that this event isn't actually cancellable in Forge! - */ -@Mixin({IntegratedServer.class, MinecraftDedicatedServer.class}) -public class MixinMinecraftServerSubclass { - @Inject(method = "setupServer", at = @At("RETURN")) - private void hookSetupEnd(CallbackInfoReturnable callback) { - LifecycleEvents.handleServerStarting((MinecraftServer) (Object) this); +@Mixin(MinecraftServer.class) +public class MixinMinecraftServer { + @Inject(method = "run", at = @At(value = "INVOKE", target = "net/minecraft/server/MinecraftServer.exit ()V")) + private void serverStoppedHook(CallbackInfo ci) { + LifecycleEvents.handleServerStopped((MinecraftServer) (Object) this); } } diff --git a/patchwork-events-lifecycle/src/main/resources/fabric.mod.json b/patchwork-events-lifecycle/src/main/resources/fabric.mod.json index 5b3f6691..6f88cf66 100644 --- a/patchwork-events-lifecycle/src/main/resources/fabric.mod.json +++ b/patchwork-events-lifecycle/src/main/resources/fabric.mod.json @@ -13,7 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", + "patchwork-api-base": "*", "patchwork-fml": "*" }, "entrypoints": { diff --git a/patchwork-events-lifecycle/src/main/resources/patchwork-events-lifecycle.mixins.json b/patchwork-events-lifecycle/src/main/resources/patchwork-events-lifecycle.mixins.json index 3aeabb4d..91c77547 100644 --- a/patchwork-events-lifecycle/src/main/resources/patchwork-events-lifecycle.mixins.json +++ b/patchwork-events-lifecycle/src/main/resources/patchwork-events-lifecycle.mixins.json @@ -3,7 +3,6 @@ "package": "net.patchworkmc.mixin.event.lifecycle", "compatibilityLevel": "JAVA_8", "mixins": [ - "MixinMinecraftServerSubclass", "MixinPlayerEntity", "MixinServerWorld" ], diff --git a/patchwork-events-rendering/build.gradle b/patchwork-events-rendering/build.gradle index bd15b80f..adb09277 100644 --- a/patchwork-events-rendering/build.gradle +++ b/patchwork-events-rendering/build.gradle @@ -1,6 +1,7 @@ archivesBaseName = "patchwork-client-colors" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.1") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-fml', configuration: 'dev') } diff --git a/patchwork-events-rendering/src/main/java/net/patchworkmc/impl/event/render/RenderEvents.java b/patchwork-events-rendering/src/main/java/net/patchworkmc/impl/event/render/RenderEvents.java index e70e9b83..c00d6e3f 100644 --- a/patchwork-events-rendering/src/main/java/net/patchworkmc/impl/event/render/RenderEvents.java +++ b/patchwork-events-rendering/src/main/java/net/patchworkmc/impl/event/render/RenderEvents.java @@ -20,13 +20,12 @@ package net.patchworkmc.impl.event.render; import java.util.Set; -import java.util.function.Consumer; import net.minecraftforge.client.event.ColorHandlerEvent; import net.minecraftforge.client.event.DrawBlockHighlightEvent; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.ModLoader; import net.minecraft.client.color.block.BlockColors; import net.minecraft.client.color.item.ItemColors; @@ -39,26 +38,20 @@ import net.minecraft.util.hit.HitResult; public class RenderEvents { - private static Consumer eventDispatcher; - - public static void registerEventDispatcher(Consumer dispatcher) { - eventDispatcher = dispatcher; - } - public static void onBlockColorsInit(BlockColors blockColors) { - eventDispatcher.accept(new ColorHandlerEvent.Block(blockColors)); + ModLoader.get().postEvent(new ColorHandlerEvent.Block(blockColors)); } public static void onItemColorsInit(ItemColors itemColors, BlockColors blockColors) { - eventDispatcher.accept(new ColorHandlerEvent.Item(itemColors, blockColors)); + ModLoader.get().postEvent(new ColorHandlerEvent.Item(itemColors, blockColors)); } public static void onTextureStitchPre(SpriteAtlasTexture spriteAtlasTexture, Set set) { - eventDispatcher.accept(new TextureStitchEvent.Pre(spriteAtlasTexture, set)); + ModLoader.get().postEvent(new TextureStitchEvent.Pre(spriteAtlasTexture, set)); } public static void onTextureStitchPost(SpriteAtlasTexture spriteAtlasTexture) { - eventDispatcher.accept(new TextureStitchEvent.Post(spriteAtlasTexture)); + ModLoader.get().postEvent(new TextureStitchEvent.Post(spriteAtlasTexture)); } /** diff --git a/patchwork-events-rendering/src/main/resources/fabric.mod.json b/patchwork-events-rendering/src/main/resources/fabric.mod.json index 28112c8e..d8f37a2d 100644 --- a/patchwork-events-rendering/src/main/resources/fabric.mod.json +++ b/patchwork-events-rendering/src/main/resources/fabric.mod.json @@ -13,7 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", + "patchwork-api-base": "*", "patchwork-fml": "*" }, "mixins": [ diff --git a/patchwork-events-world/build.gradle b/patchwork-events-world/build.gradle index b7289c8d..7f45ad89 100644 --- a/patchwork-events-world/build.gradle +++ b/patchwork-events-world/build.gradle @@ -1,6 +1,6 @@ archivesBaseName = "patchwork-events-world" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.4.0") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') } diff --git a/patchwork-events-world/src/main/java/net/minecraftforge/event/world/ChunkEvent.java b/patchwork-events-world/src/main/java/net/minecraftforge/event/world/ChunkEvent.java new file mode 100644 index 00000000..3d81bf29 --- /dev/null +++ b/patchwork-events-world/src/main/java/net/minecraftforge/event/world/ChunkEvent.java @@ -0,0 +1,93 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.event.world; + +import net.minecraftforge.common.MinecraftForge; + +import net.minecraft.world.IWorld; +import net.minecraft.world.chunk.Chunk; + +import net.patchworkmc.impl.event.world.WorldEvents; + +/** + * ChunkEvent is fired when an event involving a chunk occurs.
+ * If a method utilizes this {@link net.minecraftforge.eventbus.api.Event} as + * its parameter, the method will receive every child event of this class.
+ *
+ * {@link #Chunk} contains the Chunk this event is affecting.
+ *
+ * All children of this event are fired on the + * {@link MinecraftForge#EVENT_BUS}.
+ */ +public class ChunkEvent extends WorldEvent { + private final Chunk chunk; + + public ChunkEvent(Chunk chunk) { + super(WorldEvents.getWorldForChunk(chunk)); + this.chunk = chunk; + } + + public ChunkEvent(Chunk chunk, IWorld world) { + super(world); + this.chunk = chunk; + } + + public Chunk getChunk() { + return chunk; + } + + /** + * ChunkEvent.Load is fired when vanilla Minecraft attempts to load a Chunk into + * the world.
+ * This event is fired during chunk loading in
+ * {@link ChunkProviderClient#loadChunk(int, int)},
+ * Chunk.onChunkLoad().
+ *
+ * This event is not {@link net.minecraftforge.eventbus.api.Cancelable}.
+ *
+ * This event does not have a result. {@link HasResult}
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ */ + public static class Load extends ChunkEvent { + public Load(Chunk chunk) { + super(chunk); + } + } + + /** + * ChunkEvent.Unload is fired when vanilla Minecraft attempts to unload a Chunk + * from the world.
+ * This event is fired during chunk unloading in
+ * Chunk.onChunkUnload().
+ *
+ * This event is not {@link net.minecraftforge.eventbus.api.Cancelable}.
+ *
+ * This event does not have a result. {@link HasResult}
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ */ + public static class Unload extends ChunkEvent { + public Unload(Chunk chunk) { + super(chunk); + } + } +} + diff --git a/patchwork-events-world/src/main/java/net/minecraftforge/event/world/ChunkWatchEvent.java b/patchwork-events-world/src/main/java/net/minecraftforge/event/world/ChunkWatchEvent.java index eb9233a2..61796d1d 100644 --- a/patchwork-events-world/src/main/java/net/minecraftforge/event/world/ChunkWatchEvent.java +++ b/patchwork-events-world/src/main/java/net/minecraftforge/event/world/ChunkWatchEvent.java @@ -91,9 +91,9 @@ public Watch(ServerPlayerEntity player, ChunkPos pos, ServerWorld world) { * *

This event is fired on the {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}.

*/ - /* TODO public static class UnWatch extends ChunkWatchEvent { + public static class UnWatch extends ChunkWatchEvent { public UnWatch(ServerPlayerEntity player, ChunkPos pos, ServerWorld world) { super(player, pos, world); } - }*/ + } } diff --git a/patchwork-events-world/src/main/java/net/patchworkmc/impl/event/world/WorldEvents.java b/patchwork-events-world/src/main/java/net/patchworkmc/impl/event/world/WorldEvents.java index 4ea4cc1e..36b6f931 100644 --- a/patchwork-events-world/src/main/java/net/patchworkmc/impl/event/world/WorldEvents.java +++ b/patchwork-events-world/src/main/java/net/patchworkmc/impl/event/world/WorldEvents.java @@ -22,8 +22,11 @@ import java.util.Collections; import java.util.List; +import javax.annotation.Nullable; + import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.DifficultyChangeEvent; +import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.event.world.ChunkWatchEvent; import net.minecraftforge.event.world.WorldEvent; @@ -35,9 +38,16 @@ import net.minecraft.world.Difficulty; import net.minecraft.world.IWorld; import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.WorldChunk; +import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.level.LevelInfo; -public class WorldEvents { +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; + +public class WorldEvents implements ModInitializer { public static boolean onCreateWorldSpawn(IWorld world, LevelInfo settings) { return MinecraftForge.EVENT_BUS.post(new WorldEvent.CreateSpawnPosition(world, settings)); } @@ -64,16 +74,42 @@ public static void onWorldSave(IWorld world) { MinecraftForge.EVENT_BUS.post(new WorldEvent.Save(world)); } - // TODO: Is this actually a "world" event? - public static void onDifficultyChange(Difficulty difficulty, Difficulty oldDifficulty) { - MinecraftForge.EVENT_BUS.post(new DifficultyChangeEvent(difficulty, oldDifficulty)); - } - public static void fireChunkWatch(boolean watch, ServerPlayerEntity entity, ChunkPos chunkpos, ServerWorld world) { if (watch) { MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.Watch(entity, chunkpos, world)); } else { - throw new UnsupportedOperationException("Cannot Unwatch a chunk yet"); + MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.UnWatch(entity, chunkpos, world)); } } + + /** + * Called by ChunkEvent. + * @return the IWorld instance holding the given chunk, null if not applicable. + */ + @Nullable + public static IWorld getWorldForChunk(Chunk chunk) { + // replaces IForgeChunk.getWorldForge() + return chunk instanceof WorldChunk ? ((WorldChunk) chunk).getWorld() : null; + } + + @Override + public void onInitialize() { + ServerWorldEvents.LOAD.register((server, world) -> { + // Fabric fires this much earlier than Forge does for the overworld + // So, we're going to manually fire it for the overworld. + if (world.getDimension().getType() != DimensionType.OVERWORLD) { + onWorldLoad(world); + } + }); + + // Fire ChunkEvent.Load on server side, the other location is in MixinThreadedAnvilChunkStorage + ServerChunkEvents.CHUNK_LOAD.register((server, chunk) -> MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(chunk))); + // Fire ChunkEvent.Unload on server side + ServerChunkEvents.CHUNK_UNLOAD.register((server, chunk) -> MinecraftForge.EVENT_BUS.post(new ChunkEvent.Unload(chunk))); + } + + // TODO: Is this actually a "world" event? + public static void onDifficultyChange(Difficulty difficulty, Difficulty oldDifficulty) { + MinecraftForge.EVENT_BUS.post(new DifficultyChangeEvent(difficulty, oldDifficulty)); + } } diff --git a/patchwork-events-world/src/main/java/net/patchworkmc/impl/event/world/WorldEventsClient.java b/patchwork-events-world/src/main/java/net/patchworkmc/impl/event/world/WorldEventsClient.java new file mode 100644 index 00000000..17e69d10 --- /dev/null +++ b/patchwork-events-world/src/main/java/net/patchworkmc/impl/event/world/WorldEventsClient.java @@ -0,0 +1,45 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.event.world; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.ChunkEvent; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents; + +public class WorldEventsClient implements ClientModInitializer { + @Override + public void onInitializeClient() { + /////////////////////////////////////////////// + /// ChunkEvent.Load and Unload on client side + /////////////////////////////////////////////// + ClientChunkEvents.CHUNK_LOAD.register((server, chunk) -> MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(chunk))); + ClientChunkEvents.CHUNK_UNLOAD.register((server, chunk) -> MinecraftForge.EVENT_BUS.post(new ChunkEvent.Unload(chunk))); + // Fabric fires the chunk unload event after it's been removed from the client's chunk graph. This probably won't cause any issues. + /* ClientChunkManager.unload(II)V + * if (method_20181(chunk, x, z)) { + * net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.ChunkEvent.Unload(chunk)); + * this.chunks.method_20183(i, chunk, (WorldChunk)null); + * ClientChunkEvents.CHUNK_UNLOAD + * } + */ + } +} diff --git a/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinMinecraftServer.java b/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinMinecraftServer.java index f55dfcb0..45265395 100644 --- a/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinMinecraftServer.java +++ b/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinMinecraftServer.java @@ -20,14 +20,15 @@ package net.patchworkmc.mixin.event.world; import java.io.IOException; -import java.util.Iterator; import java.util.Map; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerTask; @@ -47,42 +48,10 @@ public MixinMinecraftServer(String name) { @Final private Map worlds; - /* - // This is a variant of the world load hook that is less likely to break mods and more likely to break on updates. - // Should get called once per loop, regardless of which if branch it takes. - @Inject( - method = "createWorlds", - slice = @Slice( - from = @At(value = "INVOKE", target = "java/util/Iterator.hasNext ()Z") - ), - at = @At(value = "JUMP", opcode = Opcodes.GOTO), - locals = LocalCapture.CAPTURE_FAILHARD - ) - private void hookCreateWorlds(WorldSaveHandler worldSaveHandler, LevelProperties properties, LevelInfo levelInfo, WorldGenerationProgressListener worldGenerationProgressListener, CallbackInfo ci, ServerWorld serverWorld, ServerWorld serverWorld2, Iterator var7, DimensionType dimensionType) { - WorldEvents.onWorldLoad(this.worlds.get(dimensionType)); - } - - */ - - // This injection gets called at the beginning of each loop, and is used to special case the overworld dimension type. - @Redirect(method = "createWorlds", at = @At(value = "INVOKE", target = "java/util/Iterator.next ()Ljava/lang/Object;")) - private Object proxyNextWorldToSpecialCaseOverworld(Iterator iterator) { - DimensionType type = iterator.next(); - - if (type == DimensionType.OVERWORLD) { - WorldEvents.onWorldLoad(this.worlds.get(type)); - } - - return type; - } - - // This injection handles every other dimension type. - @Redirect(method = "createWorlds", at = @At(value = "INVOKE", target = "java/util/Map.put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 1)) - private Object proxyPutWorld(Map worlds, Object type, Object world) { - worlds.put(type, world); - WorldEvents.onWorldLoad((ServerWorld) world); - - return world; + // Fabric usually fires the event much earlier in the method, so this is just picking a point closer to when Forge would fire it. + @Inject(method = "createWorlds", at = @At(value = "INVOKE", target = "net/minecraft/world/dimension/DimensionType.getAll ()Ljava/lang/Iterable;")) + private void fireLoadForOverworld(CallbackInfo info) { + WorldEvents.onWorldLoad(worlds.get(DimensionType.OVERWORLD)); } @Redirect(method = "shutdown", at = @At(value = "INVOKE", target = "net/minecraft/server/world/ServerWorld.close ()V")) diff --git a/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinServerPlayerInteractionManager.java b/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinServerPlayerInteractionManager.java deleted file mode 100644 index 7b62fa81..00000000 --- a/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinServerPlayerInteractionManager.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Minecraft Forge, Patchwork Project - * Copyright (c) 2016-2020, 2019-2020 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation version 2.1 - * of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.patchworkmc.mixin.event.world; - -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.world.BlockEvent; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import net.minecraft.block.BlockState; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; -import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.network.ServerPlayerInteractionManager; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.EmptyBlockView; -import net.minecraft.world.GameMode; - -@Mixin(ServerPlayerInteractionManager.class) -public class MixinServerPlayerInteractionManager { - @Shadow - public ServerWorld world; - @Shadow - public ServerPlayerEntity player; - @Shadow - private GameMode gameMode; - - @Inject(method = "tryBreakBlock", at = @At("HEAD"), cancellable = true) - private void hookBreakBlock(BlockPos pos, CallbackInfoReturnable callback) { - boolean preCancelEvent = false; - - ItemStack itemstack = player.getMainHandStack(); - - if (!itemstack.isEmpty() && !itemstack.getItem().canMine(world.getBlockState(pos), world, pos, player)) { - preCancelEvent = true; - } - - // method_21701 => canMine - // Isn't the function really canNotMine? - - if (player.method_21701(world, pos, gameMode)) { - preCancelEvent = true; - } - - // Tell client the block is gone immediately then process events - if (world.getBlockEntity(pos) == null) { - player.networkHandler.sendPacket(new BlockUpdateS2CPacket(EmptyBlockView.INSTANCE, pos)); - } - - // Post the block break event - BlockState state = world.getBlockState(pos); - BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(world, pos, state, player); - event.setCanceled(preCancelEvent); - MinecraftForge.EVENT_BUS.post(event); - - // Handle if the event is canceled - if (event.isCanceled()) { - // Let the client know the block still exists - player.networkHandler.sendPacket(new BlockUpdateS2CPacket(world, pos)); - - // Update any block entity data for this block - BlockEntity entity = world.getBlockEntity(pos); - - if (entity != null) { - BlockEntityUpdateS2CPacket packet = entity.toUpdatePacket(); - - if (packet != null) { - player.networkHandler.sendPacket(packet); - } - } - - callback.setReturnValue(false); - } else if (event.getExpToDrop() != 0) { - // TODO: Drop experience - throw new UnsupportedOperationException("Cannot drop exp from a BreakEvent yet"); - } - } -} diff --git a/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinThreadedAnvilChunkStorage.java b/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinThreadedAnvilChunkStorage.java index 4141d95c..bc3bc70d 100644 --- a/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinThreadedAnvilChunkStorage.java +++ b/patchwork-events-world/src/main/java/net/patchworkmc/mixin/event/world/MixinThreadedAnvilChunkStorage.java @@ -22,14 +22,21 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.ChunkEvent; +import net.minecraft.nbt.CompoundTag; import net.minecraft.network.Packet; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ThreadedAnvilChunkStorage; import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.chunk.Chunk; import net.patchworkmc.impl.event.world.WorldEvents; @@ -40,8 +47,20 @@ public class MixinThreadedAnvilChunkStorage { @Inject(method = "sendWatchPackets", at = @At("HEAD")) private void fireWatchEvents(ServerPlayerEntity player, ChunkPos pos, Packet[] packets, boolean withinMaxWatchDistance, boolean withinViewDistance, CallbackInfo callback) { - if (withinViewDistance && !withinMaxWatchDistance) { - WorldEvents.fireChunkWatch(true, player, pos, world); + if (this.world == player.world && withinMaxWatchDistance != withinViewDistance) { + WorldEvents.fireChunkWatch(withinViewDistance, player, pos, this.world); } } + + // Lambda in "private CompletableFuture> method_20619(ChunkPos chunkPos)" + // chunk.setLastSaveTime(this.world.getTime()); + // + MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(chunk)); + // return Either.left(chunk); + @SuppressWarnings("rawtypes") + @Inject(method = "method_17256", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", shift = Shift.AFTER, ordinal = 0, target = + "net/minecraft/world/chunk/Chunk.setLastSaveTime(J)V")) + public void onServerChunkLoad(ChunkPos chunkPos, CallbackInfoReturnable cir, CompoundTag compoundTag, boolean bl, Chunk chunk) { + // Fire ChunkEvent.Load on server side + MinecraftForge.EVENT_BUS.post(new ChunkEvent.Load(chunk)); + } } diff --git a/patchwork-events-world/src/main/resources/fabric.mod.json b/patchwork-events-world/src/main/resources/fabric.mod.json index d228f734..412740f3 100644 --- a/patchwork-events-world/src/main/resources/fabric.mod.json +++ b/patchwork-events-world/src/main/resources/fabric.mod.json @@ -13,8 +13,15 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", - "patchwork-fml": "*" + "patchwork-api-base": "*" + }, + "entrypoints": { + "main": [ + "net.patchworkmc.impl.event.world.WorldEvents" + ], + "client": [ + "net.patchworkmc.impl.event.world.WorldEventsClient" + ] }, "mixins": [ "patchwork-events-world.mixins.json" diff --git a/patchwork-events-world/src/main/resources/patchwork-events-world.mixins.json b/patchwork-events-world/src/main/resources/patchwork-events-world.mixins.json index c05375cc..6a959e02 100644 --- a/patchwork-events-world/src/main/resources/patchwork-events-world.mixins.json +++ b/patchwork-events-world/src/main/resources/patchwork-events-world.mixins.json @@ -5,10 +5,9 @@ "mixins": [ "MixinChunkGenerator", "MixinMinecraftServer", - "MixinServerPlayerInteractionManager", "MixinServerWorld", "MixinSpawnHelper", - "MixinThreadedAnvilChunkStorage", + "MixinThreadedAnvilChunkStorage" ], "client": [ "MixinClientWorld", diff --git a/patchwork-extensions-block/build.gradle b/patchwork-extensions-block/build.gradle index 7de9a209..69bbc8e6 100644 --- a/patchwork-extensions-block/build.gradle +++ b/patchwork-extensions-block/build.gradle @@ -1,11 +1,13 @@ archivesBaseName = "patchwork-extensions-block" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.4.0") dependencies { - compile project(path: ':patchwork-enum-hacks', configuration: 'dev') - compile project(path: ':patchwork-tooltype', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-enum-hacks', configuration: 'dev') + implementation project(path: ':patchwork-extensions-item', configuration: 'dev') + implementation project(path: ':patchwork-tooltype', configuration: 'dev') } -minecraft { +loom { accessWidener "src/main/resources/patchwork-extensions-block.accesswidener" } diff --git a/patchwork-extensions-block/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java b/patchwork-extensions-block/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java index 4c540345..2a860bff 100644 --- a/patchwork-extensions-block/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java +++ b/patchwork-extensions-block/src/main/java/net/minecraftforge/common/extensions/IForgeBlock.java @@ -27,6 +27,7 @@ import javax.annotation.Nullable; import net.minecraftforge.common.IPlantable; +import net.minecraftforge.common.ToolType; import net.minecraft.block.BedBlock; import net.minecraft.block.Block; @@ -82,10 +83,12 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.patchworkmc.impl.extensions.block.BlockHarvestManager; +import net.patchworkmc.impl.extensions.block.PatchworkBlock; import net.patchworkmc.mixin.extensions.block.FireBlockAccessor; import net.patchworkmc.mixin.extensions.block.PlantBlockAccessor; -public interface IForgeBlock { +public interface IForgeBlock extends PatchworkBlock { default Block getBlock() { return (Block) this; } @@ -204,7 +207,7 @@ default BlockEntity createTileEntity(BlockState state, BlockView world) { return null; } - /* TODO IForgeBlock#canHarvestBlock indirectly requires ToolType (via ForgeHooks#canHarvestBlock) + /* TODO IForgeBlock#canHarvestBlock indirectly requires ToolType (via ForgeHooks#canHarvestBlock) */ /** * Determines if the player can harvest this block, obtaining it's drops when the block is destroyed. * @@ -212,10 +215,10 @@ default BlockEntity createTileEntity(BlockState state, BlockView world) { * @param pos The block's current position * @param player The player damaging the block * @return True to spawn the drops - * + */ default boolean canHarvestBlock(BlockState state, BlockView world, BlockPos pos, PlayerEntity player) { - return ForgeHooks.canHarvestBlock(state, player, world, pos); - }*/ + return BlockHarvestManager.canHarvestBlock(state, player, world, pos); + } // TODO Call locations: Patches: ServerPlayerInteractionManager* /** @@ -240,7 +243,7 @@ default boolean canHarvestBlock(BlockState state, BlockView world, BlockPos pos, */ default boolean removedByPlayer(BlockState state, World world, BlockPos pos, PlayerEntity player, boolean willHarvest, FluidState fluid) { getBlock().onBreak(world, pos, state, player); - return world.removeBlock(pos, false); + return world.setBlockState(pos, fluid.getBlockState(), world.isClient ? 11 : 3); } // TODO Call locations: Patches: LivingEntity*, PlayerEntity*, Forge classes: ForgeEventFactory (called from LivingEntity patch) @@ -645,6 +648,7 @@ default boolean isBeaconBase(BlockState state, CollisionView world, BlockPos pos // TODO Call locations: Forge classes: BreakEvent* /** * Gathers how much experience this block drops when broken. + * TODO: there's no equivalent callback in Fabric API, so for now Fabric mods should always return 0 here. * * @param state The current state * @param world The world @@ -778,12 +782,12 @@ default boolean getWeakChanges(BlockState state, CollisionView world, BlockPos p return false; } - /* TODO IForgeBlock#getHarvestTool needs ToolType + /* TODO IForgeBlock#getHarvestTool needs ToolType */ /** * Queries the class of tool required to harvest this block, if null is returned * we assume that anything can harvest this block. - * - ToolType getHarvestTool(BlockState state);*/ + */ + ToolType getHarvestTool(BlockState state); // TODO Call locations: Patches: PickaxeItem*, Forge classes: ForgeHooks* /** @@ -793,18 +797,18 @@ default boolean getWeakChanges(BlockState state, CollisionView world, BlockPos p */ int getHarvestLevel(BlockState state); - /* TODO IForgeBlock#isToolEffective needs ToolType + /* TODO IForgeBlock#isToolEffective needs ToolType */ /** * Checks if the specified tool type is efficient on this block, * meaning that it digs at full speed. - * + */ default boolean isToolEffective(BlockState state, ToolType tool) { if (tool == ToolType.PICKAXE && (this.getBlock() == Blocks.REDSTONE_ORE || this.getBlock() == Blocks.REDSTONE_LAMP || this.getBlock() == Blocks.OBSIDIAN)) { return false; } return tool == getHarvestTool(state); - }*/ + } // TODO Call locations: Forge classes: ForgeHooksClient /** diff --git a/patchwork-extensions-block/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java b/patchwork-extensions-block/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java index 55e94ee2..0f0c4182 100644 --- a/patchwork-extensions-block/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java +++ b/patchwork-extensions-block/src/main/java/net/minecraftforge/common/extensions/IForgeBlockState.java @@ -25,6 +25,7 @@ import javax.annotation.Nullable; import net.minecraftforge.common.IPlantable; +import net.minecraftforge.common.ToolType; import net.minecraft.block.Block; import net.minecraft.block.BlockEntityProvider; @@ -149,7 +150,7 @@ default BlockEntity createTileEntity(BlockView world) { return patchwork$getForgeBlock().createTileEntity(getBlockState(), world); } - /* TODO IForgeBlockState#canHarvestBlock indirectly requires ToolType + /* TODO IForgeBlockState#canHarvestBlock indirectly requires ToolType */ /** * Determines if the player can harvest this block, obtaining it's drops when the block is destroyed. * @@ -157,10 +158,10 @@ default BlockEntity createTileEntity(BlockView world) { * @param pos The block's current position * @param player The player damaging the block * @return True to spawn the drops - * + */ default boolean canHarvestBlock(BlockView world, BlockPos pos, PlayerEntity player) { return patchwork$getForgeBlock().canHarvestBlock(getBlockState(), world, pos, player); - }*/ + } /** * Called when a player removes a block. This is responsible for @@ -582,14 +583,14 @@ default boolean getWeakChanges(CollisionView world, BlockPos pos) { return patchwork$getForgeBlock().getWeakChanges(getBlockState(), world, pos); } - /* TODO IForgeBlockState#getHarvestTool needs ToolType + /* TODO IForgeBlockState#getHarvestTool needs ToolType */ /** * Queries the class of tool required to harvest this block, if null is returned * we assume that anything can harvest this block. - * + */ default ToolType getHarvestTool() { return patchwork$getForgeBlock().getHarvestTool(getBlockState()); - }*/ + } default int getHarvestLevel() { return patchwork$getForgeBlock().getHarvestLevel(getBlockState()); @@ -605,6 +606,7 @@ default boolean isToolEffective(ToolType tool) { }*/ /** + * TODO: do not bother implementing hooks, deprecated since 1.13 * Can return IExtendedBlockState. */ default BlockState getExtendedState(BlockView world, BlockPos pos) { @@ -612,6 +614,7 @@ default BlockState getExtendedState(BlockView world, BlockPos pos) { } /** + * TODO: do not bother implementing hooks, deprecated since 1.15 * Queries if this block should render in a given layer. * A custom {@link net.minecraft.client.render.model.BakedModel} can use {@link net.minecraftforge.client.MinecraftForgeClient#getRenderLayer()} to alter the model based on layer. */ diff --git a/patchwork-events-world/src/main/java/net/minecraftforge/event/world/BlockEvent.java b/patchwork-extensions-block/src/main/java/net/minecraftforge/event/world/BlockEvent.java similarity index 54% rename from patchwork-events-world/src/main/java/net/minecraftforge/event/world/BlockEvent.java rename to patchwork-extensions-block/src/main/java/net/minecraftforge/event/world/BlockEvent.java index 13ccffd6..a46a5aef 100644 --- a/patchwork-events-world/src/main/java/net/minecraftforge/event/world/BlockEvent.java +++ b/patchwork-extensions-block/src/main/java/net/minecraftforge/event/world/BlockEvent.java @@ -19,14 +19,23 @@ package net.minecraftforge.event.world; +import java.util.List; + +import net.minecraftforge.common.extensions.IForgeBlockState; import net.minecraftforge.eventbus.api.Event; +import net.minecraft.item.ItemStack; +import net.minecraft.util.DefaultedList; import net.minecraft.block.BlockState; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.enchantment.Enchantments; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IWorld; import net.minecraft.world.World; +import net.patchworkmc.impl.extensions.block.BlockHarvestManager; + public class BlockEvent extends Event { private final IWorld world; private final BlockPos pos; @@ -69,17 +78,14 @@ public BreakEvent(World world, BlockPos pos, BlockState state, PlayerEntity play this.player = player; this.exp = 0; - // TODO: BlockState#getExpDrop - - /* // Handle empty block or player unable to break block scenario - if (state == null || !ForgeHooks.canHarvestBlock(state, player, world, pos)) { + if (state == null || !BlockHarvestManager.canHarvestBlock(state, player, world, pos)) { this.exp = 0; } else { - int bonusLevel = EnchantmentHelper.getLevel(Enchantments.FORTUNE, player.getHeldItemMainhand()); - int silklevel = EnchantmentHelper.getLevel(Enchantments.SILK_TOUCH, player.getHeldItemMainhand()); - this.exp = state.getExpDrop(world, pos, bonusLevel, silklevel); - }*/ + int bonusLevel = EnchantmentHelper.getLevel(Enchantments.FORTUNE, player.getMainHandStack()); + int silklevel = EnchantmentHelper.getLevel(Enchantments.SILK_TOUCH, player.getMainHandStack()); + this.exp = ((IForgeBlockState) state).getExpDrop(world, pos, bonusLevel, silklevel); + } } public PlayerEntity getPlayer() { @@ -109,4 +115,55 @@ public boolean isCancelable() { return true; } } + + /** + * Fired when a block is about to drop it's harvested items. The {@link #drops} array can be amended, as can the {@link #dropChance}. + * Note well: the {@link #harvester} player field is null in a variety of scenarios. Code expecting null. + * + *

The {@link #dropChance} is used to determine which items in this array will actually drop, compared to a random number. If you wish, you + * can pre-filter yourself, and set {@link #dropChance} to 1.0f to always drop the contents of the {@link #drops} array.

+ * + *

{@link #isSilkTouching} is set if this is considered a silk touch harvesting operation, vs a normal harvesting operation. Act accordingly.

+ */ + @Deprecated + public static class HarvestDropsEvent extends BlockEvent { + private final int fortuneLevel; + private final DefaultedList drops; + private final boolean isSilkTouching; + private final PlayerEntity harvester; // May be null for non-player harvesting such as explosions or machines + private float dropChance; // Change to e.g. 1.0f, if you manipulate the list and want to guarantee it always drops + + public HarvestDropsEvent(World world, BlockPos pos, BlockState state, int fortuneLevel, float dropChance, DefaultedList drops, PlayerEntity harvester, boolean isSilkTouching) { + super(world, pos, state); + this.fortuneLevel = fortuneLevel; + this.setDropChance(dropChance); + this.drops = drops; + this.isSilkTouching = isSilkTouching; + this.harvester = harvester; + } + + public int getFortuneLevel() { + return fortuneLevel; + } + + public List getDrops() { + return drops; + } + + public boolean isSilkTouching() { + return isSilkTouching; + } + + public float getDropChance() { + return dropChance; + } + + public void setDropChance(float dropChance) { + this.dropChance = dropChance; + } + + public PlayerEntity getHarvester() { + return harvester; + } + } } diff --git a/patchwork-extensions-block/src/main/java/net/patchworkmc/impl/extensions/block/BlockContext.java b/patchwork-extensions-block/src/main/java/net/patchworkmc/impl/extensions/block/BlockContext.java index 5f06ae28..be0317d9 100644 --- a/patchwork-extensions-block/src/main/java/net/patchworkmc/impl/extensions/block/BlockContext.java +++ b/patchwork-extensions-block/src/main/java/net/patchworkmc/impl/extensions/block/BlockContext.java @@ -40,30 +40,30 @@ public static ThreadLocal createContext() { return ThreadLocal.withInitial(() -> BlockContext.CLEAN_MARKER); } - public static void setContext(ThreadLocal stack, Object value) { - Object oldValue = stack.get(); + public static void setContext(ThreadLocal context, Object value) { + Object oldValue = context.get(); if (oldValue != CLEAN_MARKER) { throw new IllegalStateException("The context is not clean."); } - stack.set(value); + context.set(value); } @SuppressWarnings("unchecked") - public static T getContext(ThreadLocal stack) { - Object oldValue = stack.get(); + public static T getContext(ThreadLocal context) { + Object oldValue = context.get(); if (oldValue == CLEAN_MARKER) { throw new IllegalStateException("The context is not set."); } - return (T) stack.get(); + return (T) context.get(); } @SuppressWarnings("unchecked") - public static T getContextOr(ThreadLocal stack, T defaultValue) { - Object value = stack.get(); + public static T getContextOr(ThreadLocal context, T defaultValue) { + Object value = context.get(); if (value == CLEAN_MARKER) { return defaultValue; @@ -73,14 +73,14 @@ public static T getContextOr(ThreadLocal stack, T defaultValue) { } @SuppressWarnings("unchecked") - public static T releaseContext(ThreadLocal stack) { - Object oldValue = stack.get(); + public static T releaseContext(ThreadLocal context) { + Object oldValue = context.get(); if (oldValue == CLEAN_MARKER) { throw new IllegalStateException("The context is not set."); } - stack.set(CLEAN_MARKER); + context.remove(); return (T) oldValue; } diff --git a/patchwork-extensions-block/src/main/java/net/patchworkmc/impl/extensions/block/BlockHarvestManager.java b/patchwork-extensions-block/src/main/java/net/patchworkmc/impl/extensions/block/BlockHarvestManager.java new file mode 100644 index 00000000..349cd9a9 --- /dev/null +++ b/patchwork-extensions-block/src/main/java/net/patchworkmc/impl/extensions/block/BlockHarvestManager.java @@ -0,0 +1,173 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.extensions.block; + +import java.util.Stack; + +import javax.annotation.Nonnull; + +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.ToolType; +import net.minecraftforge.common.extensions.IForgeBlockState; +import net.minecraftforge.common.extensions.IForgeItem; +import net.minecraftforge.event.world.BlockEvent; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.OreBlock; +import net.minecraft.block.RedstoneOreBlock; +import net.minecraft.block.SpawnerBlock; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; +import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.DefaultedList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.BlockView; +import net.minecraft.world.EmptyBlockView; +import net.minecraft.world.GameMode; +import net.minecraft.world.World; + +public class BlockHarvestManager { + private static final ThreadLocal> expDrops = ThreadLocal.withInitial(Stack::new); + + private static void checkExpDropStack() { + if (expDrops.get().isEmpty()) { + throw new IllegalStateException("Patchwork's experience drop stack is not balanced!"); + } + } + + public static void pushExpDropStack(int exp) { + expDrops.get().push(exp); + } + + public static int getLastExpDrop() { + checkExpDropStack(); + return expDrops.get().lastElement(); + } + + public static int popExpDropStack() { + checkExpDropStack(); + return expDrops.get().pop(); + } + + /** + * Called by Mixins and ForgeHooks.canHarvestBlock, + * Requires harvest levels. + */ + @SuppressWarnings("unused") + public static boolean canHarvestBlock(@Nonnull BlockState state, @Nonnull PlayerEntity player, @Nonnull BlockView world, @Nonnull BlockPos pos) { + // state = state.getActualState(world, pos); + if (state.getMaterial().canBreakByHand()) { + return true; + } + + ItemStack stack = player.getMainHandStack(); + ToolType tool = null; // TODO: Unimplemented: ((IForgeBlockState) state).getHarvestTool(); + + if (stack.isEmpty() || tool == null) { + return player.isUsingEffectiveTool(state); + } + + int toolLevel = ((IForgeItem) stack.getItem()).getHarvestLevel(stack, tool, player, state); + + if (toolLevel < 0) { + return player.isUsingEffectiveTool(state); + } + + return toolLevel >= ((IForgeBlockState) state).getHarvestLevel(); + } + + public static boolean isVanillaBlock(Block block) { + if (block instanceof OreBlock || block instanceof RedstoneOreBlock) { + return true; + } + + if (block instanceof SpawnerBlock) { + return true; + } + + return false; + } + + /** + * Called by Mixin and ForgeHooks. + * @return experience dropped, -1 = block breaking is cancelled. + */ + public static int onBlockBreakEvent(World world, GameMode gameMode, ServerPlayerEntity player, BlockPos pos) { + // Logic from tryHarvestBlock for pre-canceling the event + boolean preCancelEvent = false; + + ItemStack itemstack = player.getMainHandStack(); + + if (!itemstack.isEmpty() && !itemstack.getItem().canMine(world.getBlockState(pos), world, pos, player)) { + preCancelEvent = true; + } + + // method_21701 => canMine + // Isn't the function really canNotMine? + + if (player.method_21701(world, pos, gameMode)) { + preCancelEvent = true; + } + + // Tell client the block is gone immediately then process events + if (world.getBlockEntity(pos) == null) { + player.networkHandler.sendPacket(new BlockUpdateS2CPacket(EmptyBlockView.INSTANCE, pos)); + } + + // Post the block break event + BlockState state = world.getBlockState(pos); + BlockEvent.BreakEvent event = new BlockEvent.BreakEvent(world, pos, state, player); + event.setCanceled(preCancelEvent); + MinecraftForge.EVENT_BUS.post(event); + + // Handle if the event is canceled + if (event.isCanceled()) { + // Let the client know the block still exists + player.networkHandler.sendPacket(new BlockUpdateS2CPacket(world, pos)); + + // Update any block entity data for this block + BlockEntity entity = world.getBlockEntity(pos); + + if (entity != null) { + BlockEntityUpdateS2CPacket packet = entity.toUpdatePacket(); + + if (packet != null) { + player.networkHandler.sendPacket(packet); + } + } + + return -1; // Cancelled + } else { + return event.getExpToDrop(); + } + } + + // TODO: Leaving this unfired is intentional. See: https://github.com/MinecraftForge/MinecraftForge/issues/5828 + @Deprecated + public static float fireBlockHarvesting(DefaultedList drops, World world, BlockPos pos, BlockState state, int fortune, float dropChance, boolean silkTouch, PlayerEntity player) { + BlockEvent.HarvestDropsEvent event = new BlockEvent.HarvestDropsEvent(world, pos, state, fortune, dropChance, drops, player, silkTouch); + MinecraftForge.EVENT_BUS.post(event); + return event.getDropChance(); + } +} diff --git a/patchwork-extensions-block/src/main/java/net/patchworkmc/impl/extensions/block/PatchworkBlock.java b/patchwork-extensions-block/src/main/java/net/patchworkmc/impl/extensions/block/PatchworkBlock.java new file mode 100644 index 00000000..68bd014e --- /dev/null +++ b/patchwork-extensions-block/src/main/java/net/patchworkmc/impl/extensions/block/PatchworkBlock.java @@ -0,0 +1,26 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.extensions.block; + +import java.util.Random; + +public interface PatchworkBlock { + Random getRandom(); +} diff --git a/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/MixinBlock.java b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/MixinBlock.java index a93ee87d..7db399eb 100644 --- a/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/MixinBlock.java +++ b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/MixinBlock.java @@ -21,12 +21,14 @@ import java.util.HashSet; import java.util.Map; +import java.util.Random; import java.util.Set; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; +import net.minecraftforge.common.ToolType; import net.minecraftforge.common.extensions.IForgeBlock; import net.minecraft.block.Block; @@ -40,6 +42,8 @@ @Mixin(Block.class) public class MixinBlock implements IForgeBlock { + protected Random RANDOM = new Random(); + @Shadow @Final private float slipperiness; @@ -54,6 +58,11 @@ public float getSlipperiness(BlockState state, CollisionView world, BlockPos pos return slipperiness; } + @Override + public ToolType getHarvestTool(BlockState state) { + throw new UnsupportedOperationException("Harvest levels not yet implemented"); // TODO implement getHarvestLevel + } + @Override public int getHarvestLevel(BlockState state) { throw new UnsupportedOperationException("Harvest levels not yet implemented"); // TODO implement getHarvestLevel, really sucks for vanilla blocks so i'm putting it off @@ -75,4 +84,9 @@ public Set getTags() { return this.cachedTags; } + + @Override + public Random getRandom() { + return RANDOM; + } } diff --git a/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/blockentity/MixinWorldChunk.java b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/blockentity/MixinWorldChunk.java index 9c6b4246..875646e0 100644 --- a/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/blockentity/MixinWorldChunk.java +++ b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/blockentity/MixinWorldChunk.java @@ -19,7 +19,11 @@ package net.patchworkmc.mixin.extensions.block.blockentity; +import javax.annotation.Nullable; + +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Constant; @@ -36,6 +40,7 @@ import net.minecraft.block.entity.BlockEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.BlockView; +import net.minecraft.world.World; import net.minecraft.world.chunk.ChunkSection; import net.minecraft.world.chunk.WorldChunk; @@ -44,6 +49,17 @@ @Mixin(WorldChunk.class) public abstract class MixinWorldChunk { + @Shadow + @Nullable + public abstract BlockEntity getBlockEntity(BlockPos pos, WorldChunk.CreationType creationType); + + @Shadow + @Final + private World world; + + @Shadow + private volatile boolean shouldSave; + /** * @param blockState * @return the blockEntity created by IForgeBlock.createTileEntity(BlockState, World) @@ -155,4 +171,21 @@ private BlockEntity patchwork_setBlockState_createBlockEntity(BlockEntityProvide private Block patchwork_setBlockEntity_getBlock(BlockState blockState) { return BlockContext.hasBlockEntityBlockMarker(blockState); } + + // Workaround in setBlockState for Forge Blocks with BEs that don't extend BlockEntityProvider + @Inject(method = "setBlockState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/WorldChunk;getBlockEntity(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/chunk/WorldChunk$CreationType;)Lnet/minecraft/block/entity/BlockEntity;", ordinal = 1, shift = At.Shift.AFTER), cancellable = true) + private void postGetBlockEntity(BlockPos pos, BlockState state, boolean bl, CallbackInfoReturnable cir) { + if (!(state.getBlock() instanceof BlockEntityProvider)) { + BlockEntity blockEntity = getBlockEntity(pos, WorldChunk.CreationType.CHECK); + + if (blockEntity == null) { + IForgeBlockState forgeBlockState = (IForgeBlockState) state; + BlockEntity tileEntity = forgeBlockState.createTileEntity(world); + world.setBlockEntity(pos, tileEntity); + + this.shouldSave = true; + cir.setReturnValue(state); + } + } + } } diff --git a/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/flammable/MixinWorld.java b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/flammable/MixinWorld.java new file mode 100644 index 00000000..5399b00a --- /dev/null +++ b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/flammable/MixinWorld.java @@ -0,0 +1,49 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.block.flammable; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import net.minecraftforge.common.extensions.IForgeBlockState; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import net.patchworkmc.impl.extensions.block.Signatures; + +@Mixin(World.class) +public abstract class MixinWorld { + //////////////////////////////////////////////////////// + /// doesAreaContainFireSource - IForgeBlock.isBurning + /// In 1.16.1, this patch is moved to Entity. + //////////////////////////////////////////////////////// + // This really should be included in the Fabric API! + // Block block = this.getBlockState(pooledMutable.set(o, p, q)).getBlock(); + // if (block == Blocks.FIRE || block == Blocks.LAVA) { + @Redirect(method = "doesAreaContainFireSource", at = @At(value = "INVOKE", target = Signatures.World_getBlockState, ordinal = 0)) + private BlockState patchwork_doesAreaContainFireSource_getBlockState(World world, BlockPos blockPos) { + BlockState blockState = world.getBlockState(blockPos); + boolean isBurning = ((IForgeBlockState) blockState).isBurning(world, blockPos); + return isBurning ? Blocks.FIRE.getDefaultState() : Blocks.WATER.getDefaultState(); + } +} diff --git a/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinOreBlock.java b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinOreBlock.java new file mode 100644 index 00000000..92640771 --- /dev/null +++ b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinOreBlock.java @@ -0,0 +1,42 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.block.harvest; + +import java.util.Random; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import net.minecraftforge.common.extensions.IForgeBlock; + +import net.minecraft.block.BlockState; +import net.minecraft.block.OreBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.CollisionView; + +@Mixin(OreBlock.class) +public abstract class MixinOreBlock implements IForgeBlock { + @Shadow + protected abstract int getExperienceWhenMined(Random random); + + @Override + public int getExpDrop(BlockState state, CollisionView world, BlockPos pos, int fortune, int silktouch) { + return silktouch == 0 ? getExperienceWhenMined(getRandom()) : 0; + } +} diff --git a/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinRedstoneOreBlock.java b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinRedstoneOreBlock.java new file mode 100644 index 00000000..fcaaab76 --- /dev/null +++ b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinRedstoneOreBlock.java @@ -0,0 +1,36 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.block.harvest; + +import org.spongepowered.asm.mixin.Mixin; +import net.minecraftforge.common.extensions.IForgeBlock; + +import net.minecraft.block.BlockState; +import net.minecraft.block.RedstoneOreBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.CollisionView; + +@Mixin(RedstoneOreBlock.class) +public abstract class MixinRedstoneOreBlock implements IForgeBlock { + @Override + public int getExpDrop(BlockState state, CollisionView world, BlockPos pos, int fortune, int silktouch) { + return silktouch == 0 ? 1 + getRandom().nextInt(5) : 0; + } +} diff --git a/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinServerPlayerInteractionManager.java b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinServerPlayerInteractionManager.java new file mode 100644 index 00000000..b3a3c19c --- /dev/null +++ b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinServerPlayerInteractionManager.java @@ -0,0 +1,149 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.block.harvest; + +import javax.annotation.Nullable; + +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.minecraftforge.common.extensions.IForgeBlockState; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.network.ServerPlayerInteractionManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.GameMode; +import net.minecraft.world.IWorld; +import net.minecraft.world.World; + +import net.patchworkmc.impl.extensions.block.BlockContext; +import net.patchworkmc.impl.extensions.block.BlockHarvestManager; +import net.patchworkmc.impl.extensions.block.Signatures; + +@Mixin(ServerPlayerInteractionManager.class) +public abstract class MixinServerPlayerInteractionManager { + // removedByPlayer, canHarvestBlock + + @Unique + private boolean patchwork$removeBlock(BlockPos pos, boolean canHarvest) { + ServerPlayerInteractionManager me = (ServerPlayerInteractionManager) (Object) this; + BlockState state = me.world.getBlockState(pos); + boolean removed = ((IForgeBlockState) state).removedByPlayer(me.world, pos, me.player, canHarvest, me.world.getFluidState(pos)); + + if (removed) { + state.getBlock().onBroken(me.world, pos, state); + } + + return removed; + } + + ///////////////////////////////////// + /// Fire BlockEvent.BreakEvent, + /// get the amount of exp to drop. + ///////////////////////////////////// + @Shadow + public ServerWorld world; + @Shadow + public ServerPlayerEntity player; + @Shadow + private GameMode gameMode; + + @Inject(method = "tryBreakBlock", at = @At("HEAD"), cancellable = true) + private void hookBreakBlock(BlockPos pos, CallbackInfoReturnable callback) { + int exp = BlockHarvestManager.onBlockBreakEvent(world, gameMode, player, pos); + + if (exp < 0) { + callback.setReturnValue(false); + } else { + BlockHarvestManager.pushExpDropStack(exp); + } + } + + ///////////////////////////////////// + @Redirect(method = "tryBreakBlock", at = @At(value = "INVOKE", target = Signatures.Block_onBreak, ordinal = 0)) + private void patchwork$tryBreakBlock_onBreak(Block block, World world, BlockPos pos, BlockState state, PlayerEntity player) { + // Suppress this call + } + + @Redirect(method = "tryBreakBlock", at = @At(value = "INVOKE", target = Signatures.ServerWorld_removeBlock, ordinal = 0)) + private boolean patchwork$tryBreakBlock_removeBlock(ServerWorld world, BlockPos pos, boolean bool) { + return true; // bypass if (bl && bl2) { + } + + @Redirect(method = "tryBreakBlock", at = @At(value = "INVOKE", target = Signatures.Block_onBroken, ordinal = 0)) + private void patchwork$tryBreakBlock_onBroken(Block block, IWorld world, BlockPos pos, BlockState state) { + // Suppress this call + } + + @Inject(method = "tryBreakBlock", at = @At(value = "JUMP", ordinal = 0, opcode = Opcodes.IFEQ, shift = Shift.AFTER), + slice = @Slice(from = @At(value = "INVOKE", target = Signatures.ServerPlayerInteractionManager_isCreative))) + private void patchwork$tryBreakBlock_isCreative(BlockPos pos, CallbackInfoReturnable cir) { + patchwork$removeBlock(pos, false); + } + + @Unique + private static final ThreadLocal tryBreakBlock_canHarvest = BlockContext.createContext(); // flag1 + @Redirect(method = "tryBreakBlock", at = @At(value = "INVOKE", target = Signatures.ServerPlayerEntity_isUsingEffectiveTool, ordinal = 0)) + private boolean patchwork$tryBreakBlock_isUsingEffectiveTool(ServerPlayerEntity player, BlockState blockState, BlockPos pos) { + ServerPlayerInteractionManager me = (ServerPlayerInteractionManager) (Object) this; + boolean canHarvest = ((IForgeBlockState) blockState).canHarvestBlock(me.world, pos, player); + BlockContext.setContext(tryBreakBlock_canHarvest, canHarvest); + return true; // bypass if (bl && bl2) { + } + + @Redirect(method = "tryBreakBlock", at = @At(value = "INVOKE", target = Signatures.Block_afterBreak, ordinal = 0)) + private void patchwork$tryBreakBlock_afterBreak(Block block, World world, PlayerEntity player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack stack) { + boolean canHarvest = BlockContext.releaseContext(tryBreakBlock_canHarvest); + boolean removed = patchwork$removeBlock(pos, canHarvest); + + if (removed && canHarvest) { + block.afterBreak(world, player, pos, state, blockEntity, stack); + } + + int exp = BlockHarvestManager.getLastExpDrop(); + + // isVanillaBlock exp Action + // Vanilla true xxx Let the vanilla method do the orb drop + // Fabric mod false == 0 Let the vanilla method do the orb drop + // Forge mod false >0 Do orb drop here + // Forge mod false <=0 Do nothing + if (removed && exp > 0 && !BlockHarvestManager.isVanillaBlock(block)) { + state.getBlock().dropExperience(world, pos, exp); + } + } + + @Inject(method = "tryBreakBlock", at = @At("RETURN")) + private void tryBreakBlock_return(BlockPos pos, CallbackInfoReturnable callback) { + BlockHarvestManager.popExpDropStack(); // Pop the expDrop stack at ALL return paths. + } +} diff --git a/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinSpawnerBlock.java b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinSpawnerBlock.java new file mode 100644 index 00000000..40100d06 --- /dev/null +++ b/patchwork-extensions-block/src/main/java/net/patchworkmc/mixin/extensions/block/harvest/MixinSpawnerBlock.java @@ -0,0 +1,36 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.block.harvest; + +import org.spongepowered.asm.mixin.Mixin; +import net.minecraftforge.common.extensions.IForgeBlock; + +import net.minecraft.block.BlockState; +import net.minecraft.block.SpawnerBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.CollisionView; + +@Mixin(SpawnerBlock.class) +public abstract class MixinSpawnerBlock implements IForgeBlock { + @Override + public int getExpDrop(BlockState state, CollisionView world, BlockPos pos, int fortune, int silktouch) { + return 15 + getRandom().nextInt(15) + getRandom().nextInt(15); + } +} diff --git a/patchwork-extensions-block/src/main/resources/fabric.mod.json b/patchwork-extensions-block/src/main/resources/fabric.mod.json index 3e2c4673..f1f22b18 100644 --- a/patchwork-extensions-block/src/main/resources/fabric.mod.json +++ b/patchwork-extensions-block/src/main/resources/fabric.mod.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "patchwork-extensions-block", "version": "${version}", - "icon": "assets/patchwork-enum-hacks/icon.png", + "icon": "assets/patchwork-extensions-block/icon.png", "name": "Patchwork Extensions Block", "description": "Implementation of Forge's block extensions", "authors": [ @@ -11,11 +11,16 @@ "license": "LGPL-2.1-only", "environment": "*", "depends": { - "fabricloader": ">=0.8.4" + "patchwork-api-base": "*", + "patchwork-enum-hacks": "*", + "patchwork-extensions-item": "*", + "patchwork-tooltype": "*" }, "mixins": [ "patchwork-extensions-block.mixins.json", - "patchwork-extensions-block-blockentity.mixins.json" + "patchwork-extensions-block-blockentity.mixins.json", + "patchwork-extensions-block-flammable.mixins.json", + "patchwork-extensions-block-harvest.mixins.json" ], "accessWidener": "patchwork-extensions-block.accesswidener", "custom": { diff --git a/patchwork-extensions-block/src/main/resources/patchwork-extensions-block-flammable.mixins.json b/patchwork-extensions-block/src/main/resources/patchwork-extensions-block-flammable.mixins.json new file mode 100644 index 00000000..973ae16c --- /dev/null +++ b/patchwork-extensions-block/src/main/resources/patchwork-extensions-block-flammable.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin.extensions.block.flammable", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MixinWorld" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/patchwork-extensions-block/src/main/resources/patchwork-extensions-block-harvest.mixins.json b/patchwork-extensions-block/src/main/resources/patchwork-extensions-block-harvest.mixins.json new file mode 100644 index 00000000..ee5c9c9c --- /dev/null +++ b/patchwork-extensions-block/src/main/resources/patchwork-extensions-block-harvest.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin.extensions.block.harvest", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MixinOreBlock", + "MixinRedstoneOreBlock", + "MixinServerPlayerInteractionManager", + "MixinSpawnerBlock" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/patchwork-extensions-block/src/main/resources/patchwork-extensions-block.accesswidener b/patchwork-extensions-block/src/main/resources/patchwork-extensions-block.accesswidener index 2188fab7..4b314a92 100644 --- a/patchwork-extensions-block/src/main/resources/patchwork-extensions-block.accesswidener +++ b/patchwork-extensions-block/src/main/resources/patchwork-extensions-block.accesswidener @@ -9,3 +9,5 @@ accessible field net/minecraft/item/AxeItem STRIPPED_BLOCKS Ljava/util/Map; # accessible method net/minecraft/block/PressurePlateBlock (Lnet/minecraft/block/PressurePlateBlock$ActivationRule;Lnet/minecraft/Block$Settings;)V accessible method net/minecraft/class_2440 (Lnet/minecraft/class_2440$class_2441;Lnet/minecraft/class_2248$class_2251;)V + +accessible method net/minecraft/block/Block dropExperience (Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;I)V diff --git a/patchwork-extensions-blockentity/build.gradle b/patchwork-extensions-blockentity/build.gradle new file mode 100644 index 00000000..461a47ad --- /dev/null +++ b/patchwork-extensions-blockentity/build.gradle @@ -0,0 +1,7 @@ +archivesBaseName = "patchwork-extensions-blockentity" +version = getSubprojectVersion(project, "0.1.1") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-capabilities', configuration: 'dev') +} diff --git a/patchwork-extensions-blockentity/readme.md b/patchwork-extensions-blockentity/readme.md new file mode 100644 index 00000000..0db3d657 --- /dev/null +++ b/patchwork-extensions-blockentity/readme.md @@ -0,0 +1,4 @@ +This module implements: +1. Everything in `IForgeTileEntity` +2. Hooks for BlockEntityRenderer registration. (We cannot reuse Fabric hooks for this, the Fabric registration event is fired much earlier than Forge's) +3. FastTESR in 1.14.4, will be removed and replaced with vanilla BlockEntityRenderer in 1.15 and above. \ No newline at end of file diff --git a/patchwork-extensions-blockentity/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererFast.java b/patchwork-extensions-blockentity/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererFast.java new file mode 100644 index 00000000..f68041d9 --- /dev/null +++ b/patchwork-extensions-blockentity/src/main/java/net/minecraftforge/client/model/animation/TileEntityRendererFast.java @@ -0,0 +1,96 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.model.animation; + +import com.mojang.blaze3d.platform.GlStateManager; +import org.lwjgl.opengl.GL11; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.DiffuseLighting; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.client.render.block.entity.BlockEntityRenderer; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.block.entity.BlockEntity; + +import net.patchworkmc.impl.extensions.blockentity.ForgeBlockEntityRenderer; + +/** + * TODO: The FastTESR is removed in 1.15. + * The new vanilla BlockEntityRender is identical to Forge's FastTESR, see the "Disadvantages" section. + * Patchwork API will not implement proper batch rendering for FastTESR, + * however, FastTESRs can still be displayed. + * + *

A special case which can be batched with other + * renderers that are also instances of this class. + * + *

Advantages: + *

    + *
  • All batched renderers are drawn with a single draw call
  • + *
  • Renderers have their vertices depth sorted for better translucency + * support
  • + *
+ * + *

Disadvantages: + *

    + *
  • OpenGL operations are not permitted
  • + *
  • All renderers must use the same {@link VertexFormat} + * ({@link VertexFormats#POSITION_COLOR_TEXTURE_LIGHT_NORMAL})
  • + *
+ * + * @param The type of {@link BlockEntity} being rendered. + */ +@Deprecated +public abstract class TileEntityRendererFast extends BlockEntityRenderer implements ForgeBlockEntityRenderer { + @Override + public final void render(T te, double x, double y, double z, float partialTicks, int destroyStage) { + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + this.bindTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEX); + DiffuseLighting.disable(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GlStateManager.enableBlend(); + GlStateManager.disableCull(); + + if (MinecraftClient.isAmbientOcclusionEnabled()) { + GlStateManager.shadeModel(GL11.GL_SMOOTH); + } else { + GlStateManager.shadeModel(GL11.GL_FLAT); + } + + buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_COLOR_UV_LMAP); + + renderTileEntityFast(te, x, y, z, partialTicks, destroyStage, buffer); + buffer.setOffset(0, 0, 0); + + tessellator.draw(); + + DiffuseLighting.enable(); + } + + /** + * Draw this renderer to the passed {@link BufferBuilder}. DO + * NOT draw to any buffers other than the one passed, or use any OpenGL + * operations as they will not be applied when this renderer is batched. + */ + @Override + public abstract void renderTileEntityFast(T te, double x, double y, double z, float partialTicks, int destroyStage, BufferBuilder buffer); +} diff --git a/patchwork-extensions-blockentity/src/main/java/net/minecraftforge/common/extensions/IForgeTileEntity.java b/patchwork-extensions-blockentity/src/main/java/net/minecraftforge/common/extensions/IForgeTileEntity.java new file mode 100644 index 00000000..206cd314 --- /dev/null +++ b/patchwork-extensions-blockentity/src/main/java/net/minecraftforge/common/extensions/IForgeTileEntity.java @@ -0,0 +1,200 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.extensions; + +import net.minecraftforge.common.capabilities.ICapabilitySerializable; + +import net.minecraft.block.AbstractSignBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.ChestBlock; +import net.minecraft.block.EnderChestBlock; +import net.minecraft.block.SkullBlock; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.ClientConnection; +import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +public interface IForgeTileEntity extends ICapabilitySerializable { + /** + * Sometimes default render bounding box: infinite in scope. Used to control rendering on {@link TileEntitySpecialRenderer}. + */ + Box INFINITE_EXTENT_AABB = new Box(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); + + default BlockEntity getTileEntity() { + return (BlockEntity) this; + } + + @Override + default void deserializeNBT(CompoundTag nbt) { + getTileEntity().fromTag(nbt); + } + + @Override + default CompoundTag serializeNBT() { + CompoundTag ret = new CompoundTag(); + getTileEntity().toTag(ret); + return ret; + } + + /** + * Called when you receive a TileEntityData packet for the location this + * TileEntity is currently in. On the client, the NetworkManager will always + * be the remote server. On the server, it will be whomever is responsible for + * sending the packet. + * + * @param net The NetworkManager the packet originated from + * @param pkt The data packet + */ + default void onDataPacket(ClientConnection net, BlockEntityUpdateS2CPacket pkt) { + } + + /** + * Called when the chunk's TE update tag, gotten from {@link #getUpdateTag()}, is received on the client. + * + *

Used to handle this tag in a special way. By default this simply calls {@link #readFromNBT(NBTTagCompound)}. + * + * @param tag The {@link NBTTagCompound} sent from {@link #getUpdateTag()} + */ + default void handleUpdateTag(CompoundTag tag) { + getTileEntity().fromTag(tag); + } + + /** + * Gets a {@link NBTTagCompound} that can be used to store custom data for this tile entity. + * It will be written, and read from disc, so it persists over world saves. + * + * @return A compound tag for custom data + */ + CompoundTag getTileData(); + + default void onChunkUnloaded() { + } + + /** + * Called when this is first added to the world (by {@link World#addTileEntity(TileEntity)}). + * Override instead of adding {@code if (firstTick)} stuff in update. + */ + default void onLoad() { + requestModelDataUpdate(); + } + + /** + * Return an {@link AxisAlignedBB} that controls the visible scope of a {@link TileEntitySpecialRenderer} associated with this {@link TileEntity} + * Defaults to the collision bounding box {@link Block#getCollisionBoundingBoxFromPool(World, int, int, int)} associated with the block + * at this location. + * + * @return an appropriately size {@link AxisAlignedBB} for the {@link TileEntity} + */ + @Environment(EnvType.CLIENT) + default Box getRenderBoundingBox() { + Box bb = INFINITE_EXTENT_AABB; + BlockState state = getTileEntity().getCachedState(); + Block block = state.getBlock(); + BlockPos pos = getTileEntity().getPos(); + + if (block == Blocks.ENCHANTING_TABLE) { + bb = new Box(pos, pos.add(1, 1, 1)); + } else if (block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST) { + bb = new Box(pos.add(-1, 0, -1), pos.add(2, 2, 2)); + } else if (block == Blocks.STRUCTURE_BLOCK) { + bb = INFINITE_EXTENT_AABB; + } else if (block != null && block != Blocks.BEACON) { + Box cbb = null; + + try { + cbb = state.getCollisionShape(getTileEntity().getWorld(), pos).getBoundingBox().offset(pos); + } catch (Exception e) { + // We have to capture any exceptions that may occur here because BUKKIT servers like to send + // the tile entity data BEFORE the chunk data, you know, the OPPOSITE of what vanilla does! + // So we can not GUARANTEE that the world state is the real state for the block... + // So, once again in the long line of US having to accommodate BUKKIT breaking things, + // here it is, assume that the TE is only 1 cubic block. Problem with this is that it may + // cause the TileEntity renderer to error further down the line! But alas, nothing we can do. + cbb = new Box(pos.add(-1, 0, -1), pos.add(1, 1, 1)); + } + + if (cbb != null) { + bb = cbb; + } + } + + return bb; + } + + /** + * Checks if this tile entity knows how to render its 'breaking' overlay effect. + * If this returns true, The TileEntitySpecialRenderer will be called again with break progress set. + * + * @return True to re-render tile with breaking effect. + */ + default boolean canRenderBreaking() { + Block block = getTileEntity().getCachedState().getBlock(); + return (block instanceof ChestBlock + || block instanceof EnderChestBlock + || block instanceof AbstractSignBlock + || block instanceof SkullBlock); + } + + /** + * TODO: Deprecated, in Patchwork API 1.14.4, vanilla 1.15 and above, + * this is never called. See {@link net.minecraftforge.client.model.animation.TileEntityRendererFast}. + * + *

If the TileEntitySpecialRenderer associated with this TileEntity can be batched in with another renderers, and won't access the GL state. + * If TileEntity returns true, then TESR should have the same functionality as (and probably extend) the FastTESR class. + */ + @Deprecated + default boolean hasFastRenderer() { + return false; + } + + /** + * Requests a refresh for the model data of your TE + * Call this every time your {@link #getModelData()} changes. + */ + default void requestModelDataUpdate() { + BlockEntity te = getTileEntity(); + World world = te.getWorld(); + + if (world != null && world.isClient) { + // ModelDataManager.requestModelDataRefresh(te); + } + } + + /** + * Allows you to return additional model data. + * This data can be used to provide additional functionality in your {@link net.minecraft.client.renderer.model.IBakedModel} + * You need to schedule a refresh of you model data via {@link #requestModelDataUpdate()} if the result of this function changes. + * Note that this method may be called on a chunk render thread instead of the main client thread + * + * @return Your model data + */ + //@Nonnull + //default IModelData getModelData() { + // return EmptyModelData.INSTANCE; + //} +} diff --git a/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/impl/extensions/blockentity/ForgeBlockEntityRenderer.java b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/impl/extensions/blockentity/ForgeBlockEntityRenderer.java new file mode 100644 index 00000000..aef2209b --- /dev/null +++ b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/impl/extensions/blockentity/ForgeBlockEntityRenderer.java @@ -0,0 +1,29 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.extensions.blockentity; + +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.client.render.BufferBuilder; + +@Deprecated +public interface ForgeBlockEntityRenderer { + default void renderTileEntityFast(T te, double x, double y, double z, float partialTicks, int destroyStage, BufferBuilder buffer) { + } +} diff --git a/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinBlockEntity.java b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinBlockEntity.java new file mode 100644 index 00000000..6d642335 --- /dev/null +++ b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinBlockEntity.java @@ -0,0 +1,65 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.blockentity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.minecraftforge.common.extensions.IForgeTileEntity; + +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.CompoundTag; + +@Mixin(BlockEntity.class) +public abstract class MixinBlockEntity implements IForgeTileEntity { + @Unique + private CompoundTag customTileData; + + @Inject(method = "fromTag", at = @At("RETURN")) + private void readForgeData(CompoundTag tag, CallbackInfo ci) { + if (tag.contains("ForgeData")) { + customTileData = tag.getCompound("ForgeData"); + } + } + + @Inject(method = "writeIdentifyingData", at = @At("RETURN")) + private void saveForgeData(CompoundTag tag, CallbackInfoReturnable ci) { + if (customTileData != null) { + tag.put("ForgeData", customTileData); + } + } + + @Inject(method = "markRemoved", at = @At("RETURN")) + private void markRemoved(CallbackInfo ci) { + requestModelDataUpdate(); + } + + @Override + public CompoundTag getTileData() { + if (customTileData == null) { + customTileData = new CompoundTag(); + } + + return customTileData; + } +} diff --git a/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinBlockEntityRenderer.java b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinBlockEntityRenderer.java new file mode 100644 index 00000000..5a76e8ef --- /dev/null +++ b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinBlockEntityRenderer.java @@ -0,0 +1,32 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.blockentity; + +import org.spongepowered.asm.mixin.Mixin; + +import net.minecraft.client.render.block.entity.BlockEntityRenderer; + +import net.patchworkmc.impl.extensions.blockentity.ForgeBlockEntityRenderer; + +@SuppressWarnings("rawtypes") +@Mixin(BlockEntityRenderer.class) +@Deprecated // No longer required in 1.15 and above +public abstract class MixinBlockEntityRenderer implements ForgeBlockEntityRenderer { +} diff --git a/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinClientPlayNetworkHandler.java b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinClientPlayNetworkHandler.java new file mode 100644 index 00000000..f1e6be22 --- /dev/null +++ b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinClientPlayNetworkHandler.java @@ -0,0 +1,121 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.blockentity; + +import java.util.Iterator; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import net.minecraftforge.common.extensions.IForgeTileEntity; + +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; +import net.minecraft.network.packet.s2c.play.ChunkDataS2CPacket; +import net.minecraft.util.math.BlockPos; + +import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable; + +/** + * TODO: This Mixin can be less hacky if we can talk to the Fabric API team. + * + *

This Mixin implements {@link IForgeTileEntity#handleUpdateTag(CompoundTag)} + * and {@link IForgeTileEntity#onDataPacket(net.minecraft.network.ClientConnection, BlockEntityUpdateS2CPacket)}. + */ +@Mixin(ClientPlayNetworkHandler.class) +public abstract class MixinClientPlayNetworkHandler { + /////////////////////////////////////////////////////////////// + /// onChunkData -> IForgeTileEntity.handleUpdateTag + /////////////////////////////////////////////////////////////// + @Unique + private static final ThreadLocal onChunkData_BETag = ThreadLocal.withInitial(() -> null); + @Unique + private static final String ClientWorld_getBlockEntity = "net/minecraft/client/world/ClientWorld.getBlockEntity(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/entity/BlockEntity;"; + + @Inject(method = "onChunkData", locals = LocalCapture.CAPTURE_FAILHARD, + at = @At(value = "INVOKE", shift = Shift.BEFORE, target = ClientWorld_getBlockEntity)) + private void onChunkData_CollectBETag(ChunkDataS2CPacket packet, CallbackInfo ci, + @SuppressWarnings("rawtypes") Iterator blockEntityTagListIterator, CompoundTag blockEntityTag, BlockPos blockPos) { + if (onBlockEntityUpdate_BlockEntity.get() != null) { + throw new IllegalStateException("State of ClientPlayNetworkHandler.onChunkData() is not clean, incompatible Mixins might be the cause!"); + } + + onChunkData_BETag.set(blockEntityTag); + } + + @Redirect(method = "onChunkData", at = @At(value = "INVOKE", target = ClientWorld_getBlockEntity)) + private BlockEntity onChunkData_world_getBlockEntity(ClientWorld clientWorld, BlockPos blockPos) { + CompoundTag blockEntityTag = onChunkData_BETag.get(); + onChunkData_BETag.remove(); + + ClientPlayNetworkHandler me = (ClientPlayNetworkHandler) (Object) this; + BlockEntity blockEntity = me.getWorld().getBlockEntity(blockPos); + + if (blockEntity != null && !(blockEntity instanceof BlockEntityClientSerializable)) { + // Non-Fabric BlockEntity, redirect to IForgeTileEntity.handleUpdateTag, + // which then calls the vanilla BlockEntity.fromTag method. + ((IForgeTileEntity) blockEntity).handleUpdateTag(blockEntityTag); + return null; // Skip Fabric's patch + } else { + return blockEntity; // Fabric BlockEntity, let Fabric API process it. + } + } + + /////////////////////////////////////////////////////////////// + /// onBlockEntityUpdate -> IForgeTileEntity.onDataPacket + /////////////////////////////////////////////////////////////// + @Unique + private static final ThreadLocal onBlockEntityUpdate_BlockEntity = ThreadLocal.withInitial(() -> null); + + @Inject(method = "onBlockEntityUpdate", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", ordinal = 0, shift = Shift.AFTER, + target = "net/minecraft/network/packet/s2c/play/BlockEntityUpdateS2CPacket.getBlockEntityType()I")) + private void onBlockEntityUpdate_getBlockEntityType(BlockEntityUpdateS2CPacket packet, CallbackInfo ci, BlockEntity blockEntity) { + if (onBlockEntityUpdate_BlockEntity.get() != null) { + throw new IllegalStateException("State of ClientPlayNetworkHandler.onBlockEntityUpdate() is not clean, incompatible Mixins might be the cause!"); + } + + onBlockEntityUpdate_BlockEntity.set(blockEntity); + } + + @Inject(method = "onBlockEntityUpdate", at = @At(value = "INVOKE", ordinal = 0, shift = Shift.BEFORE, + target = "net/minecraft/block/entity/BlockEntity.fromTag(Lnet/minecraft/nbt/CompoundTag;)V")) + private void onBlockEntityUpdate_fromTag(CallbackInfo ci) { + onBlockEntityUpdate_BlockEntity.remove(); + } + + @Inject(method = "onBlockEntityUpdate", at = @At(value = "RETURN")) + private void onBlockEntityUpdate_Return(BlockEntityUpdateS2CPacket packet, CallbackInfo ci) { + BlockEntity blockEntity = onBlockEntityUpdate_BlockEntity.get(); + + if (blockEntity != null && !(blockEntity instanceof BlockEntityClientSerializable)) { + onBlockEntityUpdate_BlockEntity.remove(); + ClientPlayNetworkHandler me = (ClientPlayNetworkHandler) (Object) this; + ((IForgeTileEntity) blockEntity).onDataPacket(me.getConnection(), packet); + } + } +} diff --git a/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinWorld.java b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinWorld.java new file mode 100644 index 00000000..0fb2ad3c --- /dev/null +++ b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinWorld.java @@ -0,0 +1,178 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.blockentity; + +import java.util.Iterator; +import java.util.List; + +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import net.minecraftforge.common.extensions.IForgeTileEntity; + +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.Tickable; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.profiler.Profiler; +import net.minecraft.world.World; +import net.minecraft.world.chunk.WorldChunk; + +/** + * This Mixin implements {@link IForgeTileEntity#onLoad()} and {@link IForgeTileEntity#onChunkUnloaded()}. + */ +@Mixin(World.class) +public class MixinWorld { + ///////////////////////////////////// + /// addBlockEntity() + ///////////////////////////////////// + @Inject(method = "addBlockEntity", at = @At("HEAD")) + private void onAddBlockEntity(BlockEntity blockEntity, CallbackInfoReturnable ci) { + World me = (World) (Object) this; + + if (blockEntity.getWorld() != me) { + // Forge - set the world early as vanilla doesn't set it until next tick + blockEntity.setWorld(me); + } + } + + @Shadow + @Final + protected List pendingBlockEntities; + + @Inject(method = "addBlockEntity", cancellable = true, + at = @At(value = "INVOKE", ordinal = 0, shift = Shift.AFTER, remap = false, + target = "org/apache/logging/log4j/Logger.error(Ljava/lang/String;[Lorg/apache/logging/log4j/util/Supplier;)V")) + private void onBlockEntityAdding(BlockEntity blockEntity, CallbackInfoReturnable cir) { + // Forge: wait to add new TE if we're currently processing existing ones + cir.setReturnValue(pendingBlockEntities.add(blockEntity)); + } + + @Inject(method = "addBlockEntity", at = @At(value = "FIELD", ordinal = 0, shift = Shift.BEFORE, + target = "net/minecraft/world/World.isClient:Z", opcode = Opcodes.GETFIELD)) + private void onBlockEntityAdded(BlockEntity blockEntity, CallbackInfoReturnable cir) { + // We cannot use Fabric events because Forge's onLoad() is also called on the client side. + ((IForgeTileEntity) blockEntity).onLoad(); + } + + ///////////////////////////////////// + /// tickBlockEntities() + ///////////////////////////////////// + @Shadow + protected boolean iteratingTickingBlockEntities; + + @Inject(method = "tickBlockEntities", at = @At(value = "INVOKE", ordinal = 0, shift = Shift.AFTER, + target = "net/minecraft/util/profiler/Profiler.push(Ljava/lang/String;)V")) + private void onTickBlockEntitiesStart(CallbackInfo ci) { + // Forge: Move above remove to prevent CocurrentModificationException + iteratingTickingBlockEntities = true; + } + + @Shadow + @Final + protected List unloadedBlockEntities; + + @Inject(method = "tickBlockEntities", at = @At(value = "INVOKE", ordinal = 0, shift = Shift.BEFORE, + target = "java/util/List.removeAll(Ljava/util/Collection;)Z")) + private void onBlockEntitiesRemoved(CallbackInfo ci) { + for (BlockEntity blockEntity: unloadedBlockEntities) { + ((IForgeTileEntity) blockEntity).onChunkUnloaded(); + } + } + + @Unique + private static final ThreadLocal onBlockEntitiesRemoved_BlockEntity = ThreadLocal.withInitial(() -> null); + @Unique + private static final String WorldChunk_removeBlockEntity = "net/minecraft/world/chunk/WorldChunk.removeBlockEntity(Lnet/minecraft/util/math/BlockPos;)V"; + + @Inject(method = "tickBlockEntities", locals = LocalCapture.CAPTURE_FAILHARD, + at = @At(value = "INVOKE", ordinal = 0, shift = Shift.BEFORE, target = WorldChunk_removeBlockEntity)) + private void onBlockEntitiesRemoved_CaptureVars(CallbackInfo ci, + Profiler profiler, @SuppressWarnings("rawtypes") Iterator iterator, BlockEntity blockEntity) { + if (onBlockEntitiesRemoved_BlockEntity.get() != null) { + throw new IllegalStateException("State of World.onBlockEntitiesRemoved() is not clean, incompatible Mixins might be the cause!"); + } + + onBlockEntitiesRemoved_BlockEntity.set(blockEntity); + } + + @Redirect(method = "tickBlockEntities", at = @At(value = "INVOKE", ordinal = 0, target = WorldChunk_removeBlockEntity)) + private void onBlockEntitiesRemoved(WorldChunk chunk, BlockPos pos) { + BlockEntity blockEntity = onBlockEntitiesRemoved_BlockEntity.get(); + onBlockEntitiesRemoved_BlockEntity.remove(); + + //Forge: Bugfix: If we set the tile entity it immediately sets it in the chunk, so we could be desyned + if (chunk.getBlockEntity(pos, WorldChunk.CreationType.CHECK) == blockEntity) { + chunk.removeBlockEntity(pos); + } + } + + ///////////////////////////////////// + /// setBlockEntity() + ///////////////////////////////////// + @ModifyVariable(method = "setBlockEntity", at = @At("HEAD")) + private BlockPos makeBlockPosImmutable(BlockPos pos) { + // Forge - prevent mutable BlockPos leaks + return pos.toImmutable(); + } + + @Inject(method = "setBlockEntity", at = @At(value = "INVOKE", ordinal = 0, shift = Shift.BEFORE, + target = "java/util/List.iterator()Ljava/util/Iterator;")) + private void setWorldEarly(BlockPos pos, BlockEntity blockEntity, CallbackInfo ci) { + World me = (World) (Object) this; + + if (blockEntity.getWorld() != me) { + // Forge - set the world early as vanilla doesn't set it until next tick + blockEntity.setWorld(me); + } + } + + @Redirect(method = "setBlockEntity", at = @At(value = "INVOKE", ordinal = 0, + target = "net/minecraft/world/chunk/WorldChunk.setBlockEntity(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/entity/BlockEntity;)V")) + private void checkChunkAndSetBlockEntity(WorldChunk chunk, BlockPos pos, BlockEntity blockEntity) { + // Forge adds this to prevent a null-pointer exception + if (chunk != null) { + chunk.setBlockEntity(pos, blockEntity); + } + } + + ///////////////////////////////////// + /// removeBlockEntity() + ///////////////////////////////////// + @Inject(method = "removeBlockEntity", locals = LocalCapture.CAPTURE_FAILHARD, + at = @At(value = "INVOKE", ordinal = 0, shift = Shift.AFTER, + target = "java/util/List.remove(Ljava/lang/Object;)Z")) + private void removeTickableBlockEntity(BlockPos pos, CallbackInfo ci, BlockEntity blockEntity) { + if (!(blockEntity instanceof Tickable)) { + //Forge: If they are not tickable they wont be removed in the update loop. + World me = (World) (Object) this; + me.blockEntities.remove(blockEntity); + } + } +} diff --git a/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinWorldRenderer.java b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinWorldRenderer.java new file mode 100644 index 00000000..5629a61d --- /dev/null +++ b/patchwork-extensions-blockentity/src/main/java/net/patchworkmc/mixin/extensions/blockentity/MixinWorldRenderer.java @@ -0,0 +1,99 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.blockentity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; +import org.spongepowered.asm.mixin.injection.Redirect; +import net.minecraftforge.common.extensions.IForgeTileEntity; + +import net.minecraft.block.ChestBlock; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.client.render.BlockBreakingInfo; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.VisibleRegion; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.util.math.BlockPos; + +/** + * Implements {@link IForgeTileEntity#getRenderBoundingBox()} and + * {@link IForgeTileEntity#canRenderBreaking()}. + */ +@Mixin(WorldRenderer.class) +public class MixinWorldRenderer { + ///////////////////////////////////// + /// renderEntities() + ///////////////////////////////////// + // Redirect two calls. + @Redirect(method = "renderEntities", at = @At(value = "INVOKE", + target = "net/minecraft/client/render/block/entity/BlockEntityRenderDispatcher.render(Lnet/minecraft/block/entity/BlockEntity;FI)V")) + private void renderBlockEntityIfVisible(BlockEntityRenderDispatcher dispatcher, BlockEntity blockEntity, float tickDelta, int blockBreakStage, + Camera camera, VisibleRegion visibleRegion, float tickDeltaParam) { + IForgeTileEntity te = (IForgeTileEntity) blockEntity; + + if (visibleRegion.intersects(te.getRenderBoundingBox())) { + dispatcher.render(blockEntity, tickDeltaParam, blockBreakStage); + } + } + + ///////////////////////////////////// + /// renderPartiallyBrokenBlocks() + ///////////////////////////////////// + @Shadow + private ClientWorld world; + @Unique + private static final ThreadLocal blockPosParam = ThreadLocal.withInitial(() -> null); + + @Redirect(method = "renderPartiallyBrokenBlocks", at = @At(value = "INVOKE", ordinal = 0, + target = "net/minecraft/client/render/BlockBreakingInfo.getPos()Lnet/minecraft/util/math/BlockPos;")) + private BlockPos captureBlockPos(BlockBreakingInfo bbi) { + if (blockPosParam.get() != null) { + throw new IllegalStateException("State of WorldRenderer.renderPartiallyBrokenBlocks() is not clean, incompatible Mixins might be the cause!"); + } + + BlockPos pos = bbi.getPos(); + blockPosParam.set(pos); + return pos; + } + + @ModifyConstant(method = "renderPartiallyBrokenBlocks", constant = @Constant(classValue = ChestBlock.class)) + private boolean skipNormalPartialDamageRendering(Object objIn, Class clsChestBlock) { + BlockPos pos = blockPosParam.get(); + blockPosParam.remove(); + + if (clsChestBlock.isAssignableFrom(objIn.getClass())) { + return true; // objIn instanceof clsChestBlock + } + + BlockEntity blockEntity = world.getBlockEntity(pos); + + if (blockEntity == null) { + return false; // Render partial damage for normal blocks + } + + return ((IForgeTileEntity) blockEntity).canRenderBreaking(); + } +} diff --git a/patchwork-extensions-blockentity/src/main/resources/assets/patchwork-extensions-block/icon.png b/patchwork-extensions-blockentity/src/main/resources/assets/patchwork-extensions-block/icon.png new file mode 100644 index 00000000..de75d2fb Binary files /dev/null and b/patchwork-extensions-blockentity/src/main/resources/assets/patchwork-extensions-block/icon.png differ diff --git a/patchwork-extensions-blockentity/src/main/resources/fabric.mod.json b/patchwork-extensions-blockentity/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..9547905f --- /dev/null +++ b/patchwork-extensions-blockentity/src/main/resources/fabric.mod.json @@ -0,0 +1,24 @@ +{ + "schemaVersion": 1, + "id": "patchwork-extensions-blockentity", + "version": "${version}", + "icon": "assets/patchwork-enum-hacks/icon.png", + "name": "Patchwork Extensions Block", + "description": "Implementation of Forge's blockentity extensions (IForgeTileEntity)", + "authors": [ + "PatchworkMC" + ], + "license": "LGPL-2.1-only", + "environment": "*", + "depends": { + "patchwork-api-base": "*", + "patchwork-capabilities": "*" + }, + "mixins": [ + "patchwork-extensions-blockentity.mixins.json" + ], + "custom": { + "modmenu:api": true, + "modmenu:parent": "patchwork" + } +} diff --git a/patchwork-extensions-blockentity/src/main/resources/patchwork-extensions-blockentity.mixins.json b/patchwork-extensions-blockentity/src/main/resources/patchwork-extensions-blockentity.mixins.json new file mode 100644 index 00000000..ec5fd316 --- /dev/null +++ b/patchwork-extensions-blockentity/src/main/resources/patchwork-extensions-blockentity.mixins.json @@ -0,0 +1,17 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin.extensions.blockentity", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MixinBlockEntity", + "MixinWorld" + ], + "client": [ + "MixinClientPlayNetworkHandler", + "MixinBlockEntityRenderer", + "MixinWorldRenderer" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/patchwork-extensions-item/build.gradle b/patchwork-extensions-item/build.gradle index 1bb40bab..8dbba5a0 100644 --- a/patchwork-extensions-item/build.gradle +++ b/patchwork-extensions-item/build.gradle @@ -1,6 +1,7 @@ archivesBaseName = "patchwork-extensions-item" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.4.0") dependencies { - compile project(path: ':patchwork-tooltype', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-tooltype', configuration: 'dev') } diff --git a/patchwork-extensions-item/src/main/java/net/patchworkmc/impl/extensions/item/PatchworkArmorItemHandler.java b/patchwork-extensions-item/src/main/java/net/patchworkmc/impl/extensions/item/PatchworkArmorItemHandler.java new file mode 100644 index 00000000..1f8ab4dc --- /dev/null +++ b/patchwork-extensions-item/src/main/java/net/patchworkmc/impl/extensions/item/PatchworkArmorItemHandler.java @@ -0,0 +1,63 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.extensions.item; + +import javax.annotation.Nullable; + +import net.minecraftforge.common.extensions.IForgeItem; + +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.world.World; + +public interface PatchworkArmorItemHandler { + @SuppressWarnings("rawtypes") + BipedEntityModel getArmorModelHook(LivingEntity entity, ItemStack itemStack, EquipmentSlot slot, BipedEntityModel model); + + Identifier getArmorResource(Entity entity, ItemStack stack, EquipmentSlot slot, @Nullable String type); + + /** + * Called by mixins(MixinArmorFeatureRenderer) and ForgeHooksClient. + */ + static String patchwork$getArmorTexture(Entity entity, ItemStack itemStack, String defaultTexture, EquipmentSlot slot, String type) { + IForgeItem forgeItem = (IForgeItem) itemStack.getItem(); + String result = forgeItem.getArmorTexture(itemStack, entity, slot, type); + return result != null ? result : defaultTexture; + } + + static > A patchwork$getArmorModel(LivingEntity entityLiving, ItemStack itemStack, EquipmentSlot slot, A _default) { + IForgeItem forgeItem = (IForgeItem) itemStack.getItem(); + A model = forgeItem.getArmorModel(entityLiving, itemStack, slot, _default); + return model == null ? _default : model; + } + + /** + * Called by mixins(MixinPlayerInventory) and IForgeItemStack. + */ + static void patchwork$fireArmorTick(ItemStack itemStack, World world, PlayerEntity player) { + IForgeItem item = (IForgeItem) itemStack.getItem(); + item.onArmorTick(itemStack, world, player); + } +} diff --git a/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/ItemTagsAccessor.java b/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/ItemTagsAccessor.java index 6f42875e..f7fe918f 100644 --- a/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/ItemTagsAccessor.java +++ b/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/ItemTagsAccessor.java @@ -25,9 +25,9 @@ import net.minecraft.tag.ItemTags; @Mixin(ItemTags.class) -public class ItemTagsAccessor { +public interface ItemTagsAccessor { @Accessor - public static int getLatestVersion() { + static int getLatestVersion() { throw new UnsupportedOperationException(); } } diff --git a/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/MixinPlayerInventory.java b/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/MixinPlayerInventory.java new file mode 100644 index 00000000..52ddd1e3 --- /dev/null +++ b/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/MixinPlayerInventory.java @@ -0,0 +1,42 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.item; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.world.World; + +import net.patchworkmc.impl.extensions.item.PatchworkArmorItemHandler; + +@Mixin(PlayerInventory.class) +public abstract class MixinPlayerInventory { + @Inject(method = "updateItems", at = @At("RETURN")) + private void fireArmorTick(CallbackInfo ci) { + final PlayerInventory me = (PlayerInventory) (Object) this; + final PlayerEntity player = me.player; + final World world = player.world; + me.armor.forEach(itemStack -> PatchworkArmorItemHandler.patchwork$fireArmorTick(itemStack, world, player)); + } +} diff --git a/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/client/MixinArmorBipedFeatureRenderer.java b/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/client/MixinArmorBipedFeatureRenderer.java new file mode 100644 index 00000000..9918befb --- /dev/null +++ b/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/client/MixinArmorBipedFeatureRenderer.java @@ -0,0 +1,39 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.item.client; + +import org.spongepowered.asm.mixin.Mixin; + +import net.minecraft.client.render.entity.feature.ArmorBipedFeatureRenderer; +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; + +import net.patchworkmc.impl.extensions.item.PatchworkArmorItemHandler; + +@Mixin(ArmorBipedFeatureRenderer.class) +public abstract class MixinArmorBipedFeatureRenderer implements PatchworkArmorItemHandler { + @SuppressWarnings("rawtypes") + @Override + public BipedEntityModel getArmorModelHook(LivingEntity entity, ItemStack itemStack, EquipmentSlot slot, BipedEntityModel defaultModel) { + return PatchworkArmorItemHandler.patchwork$getArmorModel(entity, itemStack, slot, defaultModel); + } +} diff --git a/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/client/MixinArmorFeatureRenderer.java b/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/client/MixinArmorFeatureRenderer.java new file mode 100644 index 00000000..fae66d0a --- /dev/null +++ b/patchwork-extensions-item/src/main/java/net/patchworkmc/mixin/extensions/item/client/MixinArmorFeatureRenderer.java @@ -0,0 +1,185 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.extensions.item.client; + +import java.util.Map; + +import javax.annotation.Nullable; + +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; + +import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ArmorItem; +import net.minecraft.item.DyeableArmorItem; +import net.minecraft.item.DyeableItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.Identifier; + +import net.patchworkmc.impl.extensions.item.PatchworkArmorItemHandler; + +/** + * TODO: Check if any Fabric mods calls getArmorTexture and method_4174 directly, + * I don't think so because both are private. + */ +@Mixin(ArmorFeatureRenderer.class) +public abstract class MixinArmorFeatureRenderer implements PatchworkArmorItemHandler { + @Shadow + @Final + private static Map ARMOR_TEXTURE_CACHE; + + @Shadow + private boolean isLegs(EquipmentSlot equipmentSlot) { + return false; + } + + @SuppressWarnings("rawtypes") + @Redirect(method = "renderArmor", at = @At(value = "INVOKE", ordinal = 0, + target = "net/minecraft/client/render/entity/feature/ArmorFeatureRenderer.getArmor(Lnet/minecraft/entity/EquipmentSlot;)Lnet/minecraft/client/render/entity/model/BipedEntityModel;")) + private BipedEntityModel getArmorModel(ArmorFeatureRenderer me, EquipmentSlot equipmentSlot, + LivingEntity livingEntity, float f, float g, float h, float i, float j, float k, float l, EquipmentSlot equipmentSlot2) { + BipedEntityModel defaultModel = me.getArmor(equipmentSlot); + ItemStack itemStack = livingEntity.getEquippedStack(equipmentSlot); + return this.getArmorModelHook(livingEntity, itemStack, equipmentSlot, defaultModel); + } + + // In 1.15 and above, getArmorTexture(ArmorItem, boolean) is removed. + @SuppressWarnings("rawtypes") + @Redirect(method = "renderArmor", at = @At(value = "INVOKE", ordinal = 0, + target = "net/minecraft/client/render/entity/feature/ArmorFeatureRenderer.getArmorTexture(Lnet/minecraft/item/ArmorItem;Z)Lnet/minecraft/util/Identifier;")) + private Identifier getArmorTexture(ArmorFeatureRenderer me, ArmorItem armor, boolean bl, + LivingEntity livingEntity, float f, float g, float h, float i, float j, float k, float l, EquipmentSlot equipmentSlot) { + ItemStack itemStack = livingEntity.getEquippedStack(equipmentSlot); + return this.getArmorResource(livingEntity, itemStack, equipmentSlot, null); + } + + // In 1.15 and above, method_4174 is renamed to getArmorTexture. + @SuppressWarnings("rawtypes") + @Redirect(method = "renderArmor", at = @At(value = "INVOKE", ordinal = 0, + target = "net/minecraft/client/render/entity/feature/ArmorFeatureRenderer.method_4174(Lnet/minecraft/item/ArmorItem;ZLjava/lang/String;)Lnet/minecraft/util/Identifier;")) + private Identifier getArmorTexture(ArmorFeatureRenderer me, ArmorItem armor, boolean bl, String overlay, + LivingEntity livingEntity, float f, float g, float h, float i, float j, float k, float l, EquipmentSlot equipmentSlot) { + ItemStack itemStack = livingEntity.getEquippedStack(equipmentSlot); + return this.getArmorResource(livingEntity, itemStack, equipmentSlot, overlay); + } + + /* + * this.bindTexture(xxxxxxxx); // The first bindTexture() within renderArmor(). + * - if (armorItem instanceof DyeableArmorItem) { + * - int m = ((DyeableArmorItem)armorItem).getColor(itemStack); + * + if (armorItem instanceof DyeableItem) { + * + armorItem = hookBeforeTypeCast(armorItem); + * + int m = ((DyeableItem) armorItem).hookGetColor(armorItem, itemStack); + * + armorItem = restoreVar(xxxx); + * float n = (float) (m >> 16 & 255) / 255.0F; + */ + @SuppressWarnings("rawtypes") + @ModifyConstant(method = "renderArmor", constant = @Constant(classValue = DyeableArmorItem.class, ordinal = 0)) + private boolean isDyeableItem(Object obj, Class cls) { + return obj instanceof DyeableItem; // Allow this for anything, not only cloth + } + + @Unique + private static final String FeatureRenderer_bindTexture = "net/minecraft/client/render/entity/feature/FeatureRenderer.bindTexture(Lnet/minecraft/util/Identifier;)V"; + @Unique + private static final String DyeableArmorItem_getColor = "net/minecraft/item/DyeableArmorItem.getColor(Lnet/minecraft/item/ItemStack;)I"; + + @ModifyVariable(method = "renderArmor", ordinal = 0, at = @At(value = "JUMP", ordinal = 0, opcode = Opcodes.IFEQ, shift = Shift.AFTER), + slice = @Slice( + from = @At(value = "INVOKE", ordinal = 0, target = FeatureRenderer_bindTexture), + to = @At(value = "INVOKE", ordinal = 0, target = DyeableArmorItem_getColor) + )) + private ArmorItem hookBeforeTypeCast(ArmorItem armorItem) { + return (DyeableArmorItem) Items.LEATHER_HELMET; // Bypass the checkcast + } + + @Redirect(method = "renderArmor", at = @At(value = "INVOKE", ordinal = 0, target = DyeableArmorItem_getColor)) + private int hookGetColor(DyeableArmorItem dummy, ItemStack itemStack) { + return ((DyeableItem) itemStack.getItem()).getColor(itemStack); + } + + @ModifyVariable(method = "renderArmor", ordinal = 0, at = @At(value = "INVOKE", ordinal = 0, shift = Shift.AFTER, target = DyeableArmorItem_getColor)) + private ArmorItem restoreVar(ArmorItem armorItem, + LivingEntity livingEntity, float f, float g, float h, float i, float j, float k, float l, EquipmentSlot equipmentSlot) { + ItemStack itemStack = livingEntity.getEquippedStack(equipmentSlot); + return (ArmorItem) itemStack.getItem(); + } + + /*=================================== FORGE START =========================================*/ + /** + * Hook to allow item-sensitive armor model. for LayerBipedArmor. In Forge, this is protected. + */ + @SuppressWarnings("rawtypes") + @Override + public BipedEntityModel getArmorModelHook(LivingEntity entity, ItemStack itemStack, EquipmentSlot slot, BipedEntityModel model) { + return model; + } + + /** + * More generic ForgeHook version of the above function, it allows for Items to + * have more control over what texture they provide. + * + * @param entity Entity wearing the armor + * @param stack ItemStack for the armor + * @param slot Slot ID that the item is in + * @param type Subtype, can be null or "overlay" + * @return ResourceLocation pointing at the armor's texture + */ + @Override + public Identifier getArmorResource(Entity entity, ItemStack stack, EquipmentSlot slot, @Nullable String type) { + ArmorItem item = (ArmorItem) stack.getItem(); + String texture = item.getMaterial().getName(); + String domain = "minecraft"; + int idx = texture.indexOf(':'); + + if (idx != -1) { + domain = texture.substring(0, idx); + texture = texture.substring(idx + 1); + } + + String s1 = String.format("%s:textures/models/armor/%s_layer_%d%s.png", domain, texture, (isLegs(slot) ? 2 : 1), + type == null ? "" : String.format("_%s", type)); + + s1 = PatchworkArmorItemHandler.patchwork$getArmorTexture(entity, stack, s1, slot, type); + Identifier armorTextureIdentifier = (Identifier) ARMOR_TEXTURE_CACHE.get(s1); + + if (armorTextureIdentifier == null) { + armorTextureIdentifier = new Identifier(s1); + ARMOR_TEXTURE_CACHE.put(s1, armorTextureIdentifier); + } + + return armorTextureIdentifier; + } +} diff --git a/patchwork-extensions-item/src/main/resources/fabric.mod.json b/patchwork-extensions-item/src/main/resources/fabric.mod.json index 154a6edb..a538560f 100644 --- a/patchwork-extensions-item/src/main/resources/fabric.mod.json +++ b/patchwork-extensions-item/src/main/resources/fabric.mod.json @@ -15,8 +15,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", - "fabric": "*", + "patchwork-api-base": "*", "patchwork-tooltype": "*" }, "mixins": [ diff --git a/patchwork-extensions-item/src/main/resources/patchwork-extensions-item.mixins.json b/patchwork-extensions-item/src/main/resources/patchwork-extensions-item.mixins.json index 1de9eaae..055b178b 100644 --- a/patchwork-extensions-item/src/main/resources/patchwork-extensions-item.mixins.json +++ b/patchwork-extensions-item/src/main/resources/patchwork-extensions-item.mixins.json @@ -8,10 +8,13 @@ "MixinEnchantedBookItem", "MixinItem", "MixinItemSettings", + "MixinPlayerInventory", "MixinPotionItems", "MixinSpawnEggItem" ], "client": [ + "client.MixinArmorBipedFeatureRenderer", + "client.MixinArmorFeatureRenderer", "client.MixinItem", "client.MixinItemSettings" ], diff --git a/patchwork-extensions-shearing/build.gradle b/patchwork-extensions-shearing/build.gradle index 70518bac..d5a35e96 100644 --- a/patchwork-extensions-shearing/build.gradle +++ b/patchwork-extensions-shearing/build.gradle @@ -1,6 +1,6 @@ archivesBaseName = "patchwork-extensions-shearing" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') } diff --git a/patchwork-extensions-shearing/src/main/resources/fabric.mod.json b/patchwork-extensions-shearing/src/main/resources/fabric.mod.json index a59f138b..ba76f678 100644 --- a/patchwork-extensions-shearing/src/main/resources/fabric.mod.json +++ b/patchwork-extensions-shearing/src/main/resources/fabric.mod.json @@ -13,8 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", - "patchwork-fml": "*" + "patchwork-api-base": "*" }, "mixins": [ "patchwork-extensions-shearing.mixins.json" diff --git a/patchwork-extensions/build.gradle b/patchwork-extensions/build.gradle index 7f1d5633..1abcf4b6 100644 --- a/patchwork-extensions/build.gradle +++ b/patchwork-extensions/build.gradle @@ -1,6 +1,7 @@ archivesBaseName = "patchwork-extensions" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") dependencies { - compile project(path: ':patchwork-extensions-block', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-extensions-block', configuration: 'dev') } diff --git a/patchwork-extensions/src/main/resources/fabric.mod.json b/patchwork-extensions/src/main/resources/fabric.mod.json index 89c4cb58..ec0a3d92 100644 --- a/patchwork-extensions/src/main/resources/fabric.mod.json +++ b/patchwork-extensions/src/main/resources/fabric.mod.json @@ -11,7 +11,7 @@ "icon": "assets/patchwork-extensions/icon.png", "environment": "*", "depends": { - "fabricloader": ">=0.8.4", + "patchwork-api-base": "*", "fabric": "*" }, "mixins": [ diff --git a/patchwork-fake-players/build.gradle b/patchwork-fake-players/build.gradle new file mode 100644 index 00000000..e18ff318 --- /dev/null +++ b/patchwork-fake-players/build.gradle @@ -0,0 +1,6 @@ +archivesBaseName = "patchwork-fake-players" +version = getSubprojectVersion(project, "0.1.0") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') +} diff --git a/patchwork-fake-players/src/main/java/net/minecraftforge/common/util/FakePlayer.java b/patchwork-fake-players/src/main/java/net/minecraftforge/common/util/FakePlayer.java new file mode 100644 index 00000000..2408f660 --- /dev/null +++ b/patchwork-fake-players/src/main/java/net/minecraftforge/common/util/FakePlayer.java @@ -0,0 +1,81 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.util; + +import com.mojang.authlib.GameProfile; + +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.packet.c2s.play.ClientSettingsC2SPacket; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.network.ServerPlayerInteractionManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.stat.Stat; +import net.minecraft.text.Text; +import net.minecraft.util.math.Vec3d; + +// Preliminary, simple Fake Player class +public class FakePlayer extends ServerPlayerEntity { + public FakePlayer(ServerWorld world, GameProfile profile) { + super(world.getServer(), world, profile, new ServerPlayerInteractionManager(world)); + } + + @Override + public Vec3d getPosVector() { + return new Vec3d(0, 0, 0); + } + + @Override + public void addChatMessage(Text text, boolean actionBar) { + } + + @Override + public void sendMessage(Text component) { + } + + @Override + public void increaseStat(Stat stat, int num) { + } + + @Override + public boolean isInvulnerableTo(DamageSource source) { + return true; + } + + @Override + public boolean shouldDamagePlayer(PlayerEntity player) { + return false; + } + + @Override + public void onDeath(DamageSource source) { + } + + @Override + public void tick() { + } + + @Override + public void setClientSettings(ClientSettingsC2SPacket packet) { + } + + // Forge also has this here: + // @Override @Nullable public MinecraftServer getServer() { return ServerLifecycleHooks.getCurrentServer(); } +} diff --git a/patchwork-fake-players/src/main/java/net/minecraftforge/common/util/FakePlayerFactory.java b/patchwork-fake-players/src/main/java/net/minecraftforge/common/util/FakePlayerFactory.java new file mode 100644 index 00000000..2403c7c2 --- /dev/null +++ b/patchwork-fake-players/src/main/java/net/minecraftforge/common/util/FakePlayerFactory.java @@ -0,0 +1,70 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common.util; + +import java.lang.ref.WeakReference; +import java.util.Map; +import java.util.UUID; + +import com.google.common.collect.Maps; +import com.mojang.authlib.GameProfile; + +import net.minecraft.server.world.ServerWorld; + +public class FakePlayerFactory { + private static final GameProfile MINECRAFT = new GameProfile(UUID.fromString("41C82C87-7AfB-4024-BA57-13D2C99CAE77"), "[Minecraft]"); + // Map of all active fake player profiles to their entities + private static final Map fakePlayers = Maps.newHashMap(); + private static WeakReference MINECRAFT_PLAYER = null; + + public static FakePlayer getMinecraft(ServerWorld world) { + FakePlayer ret = MINECRAFT_PLAYER != null ? MINECRAFT_PLAYER.get() : null; + + if (ret == null) { + ret = FakePlayerFactory.get(world, MINECRAFT); + MINECRAFT_PLAYER = new WeakReference<>(ret); + } + + return ret; + } + + /** + * Get a fake player with a given profile. + * Mods should either hold weak references to the return value, or listen for a + * WorldEvent.Unload and kill all references to prevent worlds staying in memory. + */ + public static FakePlayer get(ServerWorld world, GameProfile profile) { + return fakePlayers.computeIfAbsent(profile, it -> new FakePlayer(world, it)); + } + + /** + * Used internally to clean up fake players when worlds are unloaded on server stop. + */ + public static void unloadWorld(ServerWorld world) { + fakePlayers.entrySet().removeIf(entry -> entry.getValue().world == world); + + // This shouldn't be strictly necessary, but let's be aggressive. + FakePlayer mc = MINECRAFT_PLAYER != null ? MINECRAFT_PLAYER.get() : null; + + if (mc != null && mc.world == world) { + MINECRAFT_PLAYER = null; + } + } +} diff --git a/patchwork-fake-players/src/main/java/net/patchworkmc/impl/fakeplayers/PatchworkFakePlayers.java b/patchwork-fake-players/src/main/java/net/patchworkmc/impl/fakeplayers/PatchworkFakePlayers.java new file mode 100644 index 00000000..1e9db443 --- /dev/null +++ b/patchwork-fake-players/src/main/java/net/patchworkmc/impl/fakeplayers/PatchworkFakePlayers.java @@ -0,0 +1,38 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.fakeplayers; + +import net.minecraftforge.common.util.FakePlayerFactory; + +import net.minecraft.server.world.ServerWorld; + +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.api.ModInitializer; + +public class PatchworkFakePlayers implements ModInitializer { + @Override + public void onInitialize() { + ServerLifecycleEvents.SERVER_STOPPED.register(server -> { + for (ServerWorld world : server.getWorlds()) { + FakePlayerFactory.unloadWorld(world); + } + }); + } +} diff --git a/patchwork-fake-players/src/main/java/net/patchworkmc/mixin/fakeplayers/MixinPlayerAdvancementTracker.java b/patchwork-fake-players/src/main/java/net/patchworkmc/mixin/fakeplayers/MixinPlayerAdvancementTracker.java new file mode 100644 index 00000000..3a101a98 --- /dev/null +++ b/patchwork-fake-players/src/main/java/net/patchworkmc/mixin/fakeplayers/MixinPlayerAdvancementTracker.java @@ -0,0 +1,44 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.fakeplayers; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.minecraftforge.common.util.FakePlayer; + +import net.minecraft.advancement.Advancement; +import net.minecraft.advancement.PlayerAdvancementTracker; +import net.minecraft.server.network.ServerPlayerEntity; + +@Mixin(PlayerAdvancementTracker.class) +public class MixinPlayerAdvancementTracker { + @Shadow + private ServerPlayerEntity owner; + + @Inject(method = "grantCriterion", at = @At("HEAD"), cancellable = true) + private void onGrantCriterion(Advancement advancement, String criterion, CallbackInfoReturnable cir) { + if (owner instanceof FakePlayer) { + cir.setReturnValue(false); + } + } +} diff --git a/patchwork-fake-players/src/main/resources/assets/patchwork-extensions/icon.png b/patchwork-fake-players/src/main/resources/assets/patchwork-extensions/icon.png new file mode 100644 index 00000000..de75d2fb Binary files /dev/null and b/patchwork-fake-players/src/main/resources/assets/patchwork-extensions/icon.png differ diff --git a/patchwork-fake-players/src/main/resources/fabric.mod.json b/patchwork-fake-players/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..a544c018 --- /dev/null +++ b/patchwork-fake-players/src/main/resources/fabric.mod.json @@ -0,0 +1,29 @@ +{ + "schemaVersion": 1, + "id": "patchwork-fake-players", + "version": "${version}", + "name": "Patchwork Fake Players", + "description": "Implements the Minecraft Forge fake players API", + "authors": [ + "PatchworkMC" + ], + "license": "LGPL-2.1-only", + "icon": "assets/patchwork-fake-players/icon.png", + "environment": "*", + "depends": { + "patchwork-api-base": "*", + "fabric": "*" + }, + "mixins": [ + "patchwork-fake-players.mixins.json" + ], + "entrypoints": { + "main": [ + "net.patchworkmc.impl.fakeplayers.PatchworkFakePlayers" + ] + }, + "custom": { + "modmenu:api": true, + "modmenu:parent": "patchwork" + } +} diff --git a/patchwork-fake-players/src/main/resources/patchwork-fake-players.mixins.json b/patchwork-fake-players/src/main/resources/patchwork-fake-players.mixins.json new file mode 100644 index 00000000..c6b3a60c --- /dev/null +++ b/patchwork-fake-players/src/main/resources/patchwork-fake-players.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin.fakeplayers", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MixinPlayerAdvancementTracker" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/patchwork-fml/build.gradle b/patchwork-fml/build.gradle index 4880cf10..a1c8ee20 100644 --- a/patchwork-fml/build.gradle +++ b/patchwork-fml/build.gradle @@ -1,6 +1,12 @@ archivesBaseName = "patchwork-fml" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.4.0") + +loom { + accessWidener "src/main/resources/patchwork-fml.accesswidener" +} dependencies { implementation 'com.electronwill.night-config:toml:3.6.2' + + implementation project(path: ':patchwork-api-base', configuration: 'dev') } diff --git a/patchwork-fml/src/main/java/net/minecraftforge/fml/ModContainer.java b/patchwork-fml/src/main/java/net/minecraftforge/fml/ModContainer.java index 2a4ad48f..447aed30 100644 --- a/patchwork-fml/src/main/java/net/minecraftforge/fml/ModContainer.java +++ b/patchwork-fml/src/main/java/net/minecraftforge/fml/ModContainer.java @@ -85,4 +85,8 @@ public final net.fabricmc.loader.api.ModContainer getParent() { protected void acceptEvent(Event e) { } + + public final void patchwork$acceptEvent(Event e) { + this.acceptEvent(e); + } } diff --git a/patchwork-fml/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModAnnotation.java b/patchwork-fml/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModAnnotation.java new file mode 100644 index 00000000..ce6fb30a --- /dev/null +++ b/patchwork-fml/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModAnnotation.java @@ -0,0 +1,123 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fml.loading.moddiscovery; + +import java.lang.annotation.ElementType; +import java.util.ArrayList; +import java.util.Map; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import net.minecraftforge.forgespi.language.ModFileScanData; +import org.objectweb.asm.Type; + +public class ModAnnotation { + private final ElementType type; + private final Type asmType; + private final String member; + private final Map elements; + private ArrayList arrayList; + private String arrayName; + + public static ModFileScanData.AnnotationData fromModAnnotation(Type clazz, ModAnnotation annotation) { + return new ModFileScanData.AnnotationData( + annotation.asmType, annotation.type, clazz, annotation.member, annotation.elements + ); + } + + public ModAnnotation(ElementType type, Type asmType, String member) { + this.elements = Maps.newHashMap(); + this.type = type; + this.asmType = asmType; + this.member = member; + } + + public ModAnnotation(Type asmType, ModAnnotation parent) { + this.elements = Maps.newHashMap(); + this.type = parent.type; + this.asmType = asmType; + this.member = parent.member; + } + + public String toString() { + return MoreObjects.toStringHelper("Annotation") + .add("type", this.type).add("name", this.asmType.getClassName()) + .add("member", this.member).add("values", this.elements).toString(); + } + + public ElementType getType() { + return this.type; + } + + public String getMember() { + return this.member; + } + + public Map getValues() { + return this.elements; + } + + public void addArray(String name) { + this.arrayList = Lists.newArrayList(); + this.arrayName = name; + } + + public void addProperty(String key, Object value) { + if (this.arrayList != null) { + this.arrayList.add(value); + } else { + this.elements.put(key, value); + } + } + + public void addEnumProperty(String key, String enumName, String value) { + this.addProperty(key, new ModAnnotation.EnumHolder(enumName, value)); + } + + public void endArray() { + this.elements.put(this.arrayName, this.arrayList); + this.arrayList = null; + } + + public ModAnnotation addChildAnnotation(String name, String desc) { + ModAnnotation child = new ModAnnotation(Type.getType(desc), this); + this.addProperty(name, child.getValues()); + return child; + } + + public static class EnumHolder { + private final String desc; + private final String value; + + public EnumHolder(String desc, String value) { + this.desc = desc; + this.value = value; + } + + public String getDesc() { + return this.desc; + } + + public String getValue() { + return this.value; + } + } +} diff --git a/patchwork-fml/src/main/java/net/minecraftforge/forgespi/language/ModFileScanData.java b/patchwork-fml/src/main/java/net/minecraftforge/forgespi/language/ModFileScanData.java index b066770f..284e7aaf 100644 --- a/patchwork-fml/src/main/java/net/minecraftforge/forgespi/language/ModFileScanData.java +++ b/patchwork-fml/src/main/java/net/minecraftforge/forgespi/language/ModFileScanData.java @@ -35,6 +35,7 @@ import java.util.stream.Collectors; import com.google.gson.Gson; +import net.minecraftforge.fml.loading.moddiscovery.ModAnnotation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.objectweb.asm.Type; @@ -110,8 +111,7 @@ public static class AnnotationData { private final Type clazz; private final String memberName; - //lazy evaluated - private Map annotationData; + private Map elements; public AnnotationData( final Type annotationType, final ElementType targetType, @@ -123,6 +123,17 @@ public AnnotationData( this.memberName = memberName; } + public AnnotationData( + Type annotationType, ElementType targetType, Type clazz, + String memberName, Map elements + ) { + this.annotationType = annotationType; + this.targetType = targetType; + this.clazz = clazz; + this.memberName = memberName; + this.elements = elements; + } + public Type getAnnotationType() { return annotationType; } @@ -140,15 +151,15 @@ public String getMemberName() { } public Map getAnnotationData() { - if (annotationData == null) { + if (elements == null) { initAnnotationData(); } - return annotationData; + return elements; } private void initAnnotationData() { - annotationData = new HashMap<>(); + elements = new HashMap<>(); try { // TODO: This *may* load classes in the wrong order, but it shouldn't be an issue @@ -163,14 +174,13 @@ private void initAnnotationData() { return; } - Method[] argMethods = annotationObject.getClass().getDeclaredMethods(); + Method[] elementGetters = annotationObject.getClass().getDeclaredMethods(); - for (Method argMethod : argMethods) { - if (isArgumentMethod(argMethod)) { - annotationData.put( - argMethod.getName(), - argMethod.invoke(annotationObject) - ); + for (Method elementGetter : elementGetters) { + if (isElementGetter(elementGetter)) { + Object value = elementGetter.invoke(annotationObject); + + elements.put(elementGetter.getName(), processElementObject(value)); } } } catch (Throwable e) { @@ -178,7 +188,7 @@ private void initAnnotationData() { } } - private static boolean isArgumentMethod(Method method) { + private static boolean isElementGetter(Method method) { String name = method.getName(); if (name.equals("toString")) return false; if (name.equals("hashCode")) return false; @@ -199,15 +209,14 @@ private Annotation getAnnotationObject(Class clazzObj, Class annotationType) case METHOD: String methodName = memberName.substring(0, memberName.indexOf('(')); Method[] methods = Arrays.stream(clazzObj.getDeclaredMethods()) - .filter(method -> method.getName().equals(methodName)) - .toArray(Method[]::new); + .filter(method -> method.getName().equals(methodName)) + .toArray(Method[]::new); if (methods.length == 0) { throw new RuntimeException("Cannot find method " + methodName); } if (methods.length > 1) { //TODO handle overloaded methods - throw new RuntimeException("Currently Cannot Handle Overloaded Methods"); } @@ -235,4 +244,23 @@ public int hashCode() { return Objects.hash(annotationType, targetType, clazz, memberName); } } + + private static Object processElementObject(Object object) { + if (object instanceof Object[]) { + return Arrays.stream((Object[]) object) + .map(ModFileScanData::processElementObject) + .collect(Collectors.toList()); + } + + if (object instanceof Enum) { + Enum enumObject = (Enum) object; + String className = enumObject.getDeclaringClass().getName(); + return new ModAnnotation.EnumHolder( + className.replace('.', '/'), + enumObject.toString() + ); + } + + return object; + } } diff --git a/patchwork-fml/src/main/resources/fabric.mod.json b/patchwork-fml/src/main/resources/fabric.mod.json index 8e0790fe..8e88f088 100644 --- a/patchwork-fml/src/main/resources/fabric.mod.json +++ b/patchwork-fml/src/main/resources/fabric.mod.json @@ -13,7 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4" + "patchwork-api-base": "*" }, "entrypoints": { "main": [ @@ -24,5 +24,6 @@ "custom": { "modmenu:api": true, "modmenu:parent": "patchwork" - } + }, + "accessWidener": "patchwork-fml.accesswidener" } diff --git a/patchwork-fml/src/main/resources/patchwork-fml.accesswidener b/patchwork-fml/src/main/resources/patchwork-fml.accesswidener new file mode 100644 index 00000000..8c0669e1 --- /dev/null +++ b/patchwork-fml/src/main/resources/patchwork-fml.accesswidener @@ -0,0 +1,2 @@ +accessWidener v1 named +accessible field net/minecraft/client/MinecraftClient textureManager Lnet/minecraft/client/texture/TextureManager; diff --git a/patchwork-god-classes/build.gradle b/patchwork-god-classes/build.gradle index 6c9cf972..da0360f2 100644 --- a/patchwork-god-classes/build.gradle +++ b/patchwork-god-classes/build.gradle @@ -1,8 +1,17 @@ archivesBaseName = "patchwork-god-classes" -version = getSubprojectVersion(project, "0.1.0") +version = getSubprojectVersion(project, "0.3.0") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') - compile project(path: ':patchwork-events-lifecycle', configuration: 'dev') - compile project(path: ':patchwork-events-world', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-capabilities', configuration: 'dev') + implementation project(path: ':patchwork-events-entity', configuration: 'dev') + implementation project(path: ':patchwork-events-input', configuration: 'dev') + implementation project(path: ':patchwork-events-lifecycle', configuration: 'dev') + implementation project(path: ':patchwork-events-rendering', configuration: 'dev') + implementation project(path: ':patchwork-events-world', configuration: 'dev') + implementation project(path: ':patchwork-extensions-block', configuration: 'dev') + implementation project(path: ':patchwork-extensions-item', configuration: 'dev') + implementation project(path: ':patchwork-loot', configuration: 'dev') + implementation project(path: ':patchwork-networking', configuration: 'dev') } diff --git a/patchwork-god-classes/src/main/java/net/minecraftforge/client/ForgeHooksClient.java b/patchwork-god-classes/src/main/java/net/minecraftforge/client/ForgeHooksClient.java index ef6b02e5..213953b4 100644 --- a/patchwork-god-classes/src/main/java/net/minecraftforge/client/ForgeHooksClient.java +++ b/patchwork-god-classes/src/main/java/net/minecraftforge/client/ForgeHooksClient.java @@ -19,9 +19,65 @@ package net.minecraftforge.client; +import java.util.Set; + +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.color.item.ItemColors; +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import net.minecraft.client.Mouse; + +import net.patchworkmc.impl.event.input.InputEvents; +import net.patchworkmc.impl.event.render.RenderEvents; +import net.patchworkmc.impl.extensions.item.PatchworkArmorItemHandler; + /* * Note: this class is intended for mod use only, to dispatch to the implementations kept in their own modules. * Do not keep implementation details here, methods should be thin wrappers around methods in other modules. */ public class ForgeHooksClient { + public static String getArmorTexture(Entity entity, ItemStack armor, String defaultTexture, EquipmentSlot slot, String type) { + return PatchworkArmorItemHandler.patchwork$getArmorTexture(entity, armor, defaultTexture, slot, type); + } + + public static > A getArmorModel(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot slot, A defaultModel) { + return PatchworkArmorItemHandler.patchwork$getArmorModel(livingEntity, itemStack, slot, defaultModel); + } + + public static void fireMouseInput(int button, int action, int mods) { + InputEvents.fireMouseInput(button, action, mods); + } + + public static void fireKeyInput(int key, int scanCode, int action, int modifiers) { + InputEvents.fireKeyInput(key, scanCode, action, modifiers); + } + + public static boolean onMouseScroll(Mouse mouseHelper, double scrollDelta) { + return InputEvents.onMouseScroll(mouseHelper, scrollDelta); + } + + public static boolean onRawMouseClicked(int button, int action, int mods) { + return InputEvents.onRawMouseClicked(button, action, mods); + } + + public static void onBlockColorsInit(BlockColors blockColors) { + RenderEvents.onBlockColorsInit(blockColors); + } + + public static void onItemColorsInit(ItemColors itemColors, BlockColors blockColors) { + RenderEvents.onItemColorsInit(itemColors, blockColors); + } + + public static void onTextureStitchedPre(SpriteAtlasTexture map, Set resourceLocations) { + RenderEvents.onTextureStitchPre(map, resourceLocations); + } + + public static void onTextureStitchedPost(SpriteAtlasTexture map) { + RenderEvents.onTextureStitchPost(map); + } } diff --git a/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java b/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java index ff7b73ca..aa4cd367 100644 --- a/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/patchwork-god-classes/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -19,21 +19,45 @@ package net.minecraftforge.common; +import java.util.Collection; import java.util.List; import javax.annotation.Nullable; -import net.minecraft.entity.EntityCategory; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.eventbus.api.Event; + +import net.minecraft.world.MobSpawnerLogic; +import net.minecraft.world.GameMode; +import net.minecraft.world.Difficulty; +import net.minecraft.world.dimension.DimensionType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.SpawnType; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; +import net.minecraft.entity.EntityCategory; +import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.Difficulty; import net.minecraft.world.IWorld; import net.minecraft.world.World; import net.minecraft.world.biome.Biome; import net.minecraft.world.level.LevelInfo; +import net.patchworkmc.impl.extensions.block.BlockHarvestManager; +import net.patchworkmc.impl.loot.LootHooks; +import net.patchworkmc.impl.event.entity.EntityEvents; import net.patchworkmc.impl.event.world.WorldEvents; /* @@ -41,6 +65,87 @@ * Do not keep implementation details here, methods should be thin wrappers around methods in other modules. */ public class ForgeHooks { + public static int canEntitySpawn(MobEntity entity, IWorld world, double x, double y, double z, MobSpawnerLogic spawner, SpawnType spawnReason) { + Event.Result res = ForgeEventFactory.canEntitySpawn(entity, world, x, y, z, null, spawnReason); + return res == Event.Result.DEFAULT ? 0 : res == Event.Result.DENY ? -1 : 1; + } + + // TODO: onInteractEntityAt + + public static ActionResult onInteractEntity(PlayerEntity player, Entity entity, Hand hand) { + return EntityEvents.onInteractEntity(player, entity, hand); + } + + public static boolean onLivingDeath(LivingEntity entity, DamageSource src) { + return EntityEvents.onLivingDeath(entity, src); + } + + public static boolean onLivingUpdate(LivingEntity entity) { + return EntityEvents.onLivingUpdateEvent(entity); + } + + // TODO: forge calls the equivilant to this in LivingEntity, but patchwork only calls the equivilant to onPlayerAttack + public static boolean onLivingAttack(LivingEntity entity, DamageSource src, float amount) { + return entity instanceof PlayerEntity || onPlayerAttack(entity, src, amount); + } + + public static boolean onPlayerAttack(LivingEntity entity, DamageSource src, float amount) { + return !EntityEvents.onLivingAttack(entity, src, amount); + } + + // optifine wants this? O.o + public static void onLivingSetAttackTarget(LivingEntity entity, LivingEntity target) { + EntityEvents.onLivingSetAttackTarget(entity, target); + } + + public static float onLivingHurt(LivingEntity entity, DamageSource src, float amount) { + return EntityEvents.onLivingHurt(entity, src, amount); + } + + @Nullable + public static float[] onLivingFall(LivingEntity entity, float distance, float damageMultiplier) { + return EntityEvents.onLivingFall(entity, distance, damageMultiplier); + } + + public static float onLivingDamage(LivingEntity entity, DamageSource src, float amount) { + return EntityEvents.onLivingDamage(entity, src, amount); + } + + public static boolean onLivingDrops(LivingEntity entity, DamageSource source, Collection drops, int lootingLevel, boolean recentlyHit) { + return EntityEvents.onLivingDrops(entity, source, drops, lootingLevel, recentlyHit); + } + + public static boolean onPlayerAttackTarget(PlayerEntity player, Entity target) { + return EntityEvents.attackEntity(player, target); + } + + public static boolean onTravelToDimension(Entity entity, DimensionType dimensionType) { + return EntityEvents.onTravelToDimension(entity, dimensionType); + } + + public static int onBlockBreakEvent(World world, GameMode gameType, ServerPlayerEntity entityPlayer, BlockPos pos) { + return BlockHarvestManager.onBlockBreakEvent(world, gameType, entityPlayer, pos); + } + + @SuppressWarnings({ "rawtypes", "unused" }) + private static ThreadLocal lootContext = LootHooks.lootContext; + + // Need to have the class here to make some mod hacks work + public static class LootTableContext extends LootHooks.LootTableContext { + private LootTableContext(Identifier name, boolean custom) { + super(name, custom); + } + } + + @Nullable + public static LootTable loadLootTable(Gson gson, Identifier name, JsonObject data, boolean custom, LootManager lootTableManager) { + return LootHooks.loadLootTable(gson, name, data, custom, lootTableManager); + } + + public static String readPoolName(JsonObject json) { + return LootHooks.readPoolName(json); + } + public static boolean onCreateWorldSpawn(World world, LevelInfo settings) { return WorldEvents.onCreateWorldSpawn(world, settings); } diff --git a/patchwork-god-classes/src/main/java/net/minecraftforge/event/ForgeEventFactory.java b/patchwork-god-classes/src/main/java/net/minecraftforge/event/ForgeEventFactory.java index f9e54a66..cca7bf8f 100644 --- a/patchwork-god-classes/src/main/java/net/minecraftforge/event/ForgeEventFactory.java +++ b/patchwork-god-classes/src/main/java/net/minecraftforge/event/ForgeEventFactory.java @@ -19,9 +19,101 @@ package net.minecraftforge.event; +import javax.annotation.Nullable; + +import net.minecraftforge.common.capabilities.CapabilityDispatcher; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.eventbus.api.Event; + +import net.minecraft.entity.SpawnType; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.projectile.ExplosiveProjectileEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.entity.thrown.ThrownEntity; +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; +import net.minecraft.util.Identifier; +import net.minecraft.util.hit.HitResult; +import net.minecraft.world.IWorld; +import net.minecraft.world.MobSpawnerLogic; +import net.minecraft.world.World; +import net.minecraft.block.BlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.DefaultedList; +import net.minecraft.util.math.BlockPos; + +import net.patchworkmc.impl.capability.CapabilityEvents; +import net.patchworkmc.impl.event.entity.EntityEvents; +import net.patchworkmc.impl.event.entity.PlayerEvents; +import net.patchworkmc.impl.event.loot.LootEvents; +import net.patchworkmc.impl.extensions.block.BlockHarvestManager; + /* * Note: this class is intended for mod use only, to dispatch to the implementations kept in their own modules. * Do not keep implementation details here, methods should be thin wrappers around methods in other modules. */ public class ForgeEventFactory { + @Nullable + public static CapabilityDispatcher gatherCapabilities(Class type, T provider) { + return gatherCapabilities(type, provider, null); + } + + @Nullable + public static CapabilityDispatcher gatherCapabilities(Class type, T provider, @Nullable ICapabilityProvider parent) { + return CapabilityEvents.gatherCapabilities(type, provider, parent); + } + + public static Event.Result canEntitySpawn(MobEntity entity, IWorld world, double x, double y, double z, MobSpawnerLogic spawner, SpawnType spawnReason) { + return EntityEvents.canEntitySpawn(entity, world, x, y, z, spawner, spawnReason); + } + + public static boolean canEntitySpawnSpawner(MobEntity entity, World world, float x, float y, float z, MobSpawnerLogic spawner) { + return EntityEvents.canEntitySpawnFromSpawner(entity, world, x, y, z, spawner); + } + + public static void onPlayerFall(PlayerEntity player, float distance, float multiplier) { + EntityEvents.onFlyablePlayerFall(player, distance, multiplier); + } + + public static boolean doSpecialSpawn(MobEntity entity, World world, float x, float y, float z, MobSpawnerLogic spawner, SpawnType spawnReason) { + return EntityEvents.doSpecialSpawn(entity, world, x, y, z, spawner, spawnReason); + } + + public static LootTable loadLootTable(Identifier name, LootTable table, LootManager lootTableManager) { + return LootEvents.loadLootTable(name, table, lootTableManager); + } + + // Forge might remove BlockEvent.HarvestDropsEvent, which is replaced by the new loot modifier. + @Deprecated + public static float fireBlockHarvesting(DefaultedList drops, World world, BlockPos pos, BlockState state, int fortune, float dropChance, boolean silkTouch, PlayerEntity player) { + return BlockHarvestManager.fireBlockHarvesting(drops, world, pos, state, fortune, dropChance, silkTouch, player); + } + + public static boolean onAnimalTame(AnimalEntity animal, PlayerEntity tamer) { + return EntityEvents.onAnimalTame(animal, tamer); + } + + public static boolean onProjectileImpact(Entity entity, HitResult ray) { + return EntityEvents.onProjectileImpact(entity, ray); + } + + public static boolean onProjectileImpact(ProjectileEntity arrow, HitResult ray) { + return EntityEvents.onProjectileImpact(arrow, ray); + } + + public static boolean onProjectileImpact(ExplosiveProjectileEntity fireball, HitResult ray) { + return EntityEvents.onProjectileImpact(fireball, ray); + } + + public static boolean onProjectileImpact(ThrownEntity throwable, HitResult ray) { + return EntityEvents.onProjectileImpact(throwable, ray); + } + + public static int onItemPickup(ItemEntity item, PlayerEntity player) { + return PlayerEvents.onItemPickup(player, item); + } } diff --git a/patchwork-god-classes/src/main/java/net/minecraftforge/fml/client/ClientHooks.java b/patchwork-god-classes/src/main/java/net/minecraftforge/fml/client/ClientHooks.java new file mode 100644 index 00000000..cb087f9c --- /dev/null +++ b/patchwork-god-classes/src/main/java/net/minecraftforge/fml/client/ClientHooks.java @@ -0,0 +1,40 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fml.client; + +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.network.ClientConnection; + +import net.patchworkmc.impl.networking.ClientNetworkingEvents; + +public class ClientHooks { + public static void firePlayerLogin(final ClientPlayerInteractionManager interactionManager, final ClientPlayerEntity player, final ClientConnection clientConnection) { + ClientNetworkingEvents.firePlayerLogin(interactionManager, player, clientConnection); + } + + public static void firePlayerLogout(final ClientPlayerInteractionManager interactionManager, final ClientPlayerEntity player) { + ClientNetworkingEvents.firePlayerLogout(interactionManager, player); + } + + public static void firePlayerRespawn(final ClientPlayerInteractionManager interactionManager, final ClientPlayerEntity oldPlayer, final ClientPlayerEntity newPlayer, final ClientConnection clientConnection) { + ClientNetworkingEvents.firePlayerRespawn(interactionManager, oldPlayer, newPlayer, clientConnection); + } +} diff --git a/patchwork-god-classes/src/main/java/net/minecraftforge/fml/hooks/BasicEventHooks.java b/patchwork-god-classes/src/main/java/net/minecraftforge/fml/hooks/BasicEventHooks.java index aaebffd3..8dce0091 100644 --- a/patchwork-god-classes/src/main/java/net/minecraftforge/fml/hooks/BasicEventHooks.java +++ b/patchwork-god-classes/src/main/java/net/minecraftforge/fml/hooks/BasicEventHooks.java @@ -21,9 +21,14 @@ import net.minecraftforge.event.TickEvent; +import net.minecraft.entity.ItemEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; +import net.patchworkmc.impl.event.entity.PlayerEvents; import net.patchworkmc.impl.event.lifecycle.LifecycleEvents; /* @@ -31,6 +36,37 @@ * Do not keep implementation details here, methods should be thin wrappers around methods in other modules. */ public class BasicEventHooks { + public static void firePlayerChangedDimensionEvent(PlayerEntity player, DimensionType fromDim, DimensionType toDim) { + PlayerEvents.firePlayerChangedDimensionEvent(player, fromDim, toDim); + } + + public static void firePlayerLoggedIn(PlayerEntity player) { + PlayerEvents.firePlayerLoggedIn(player); + } + + public static void firePlayerLoggedOut(PlayerEntity player) { + PlayerEvents.firePlayerLoggedOut(player); + } + + public static void firePlayerRespawnEvent(PlayerEntity player, boolean endConquered) { + PlayerEvents.firePlayerRespawnEvent(player, endConquered); + } + + public static void firePlayerItemPickupEvent(PlayerEntity player, ItemEntity item, ItemStack clone) { + PlayerEvents.firePlayerItemPickupEvent(player, item, clone); + } + + public static void firePlayerCraftingEvent(PlayerEntity player, ItemStack crafted, Inventory craftMatrix) { + PlayerEvents.firePlayerCraftingEvent(player, crafted, craftMatrix); + } + + public static void firePlayerSmeltedEvent(PlayerEntity player, ItemStack smelted) { + PlayerEvents.firePlayerSmeltedEvent(player, smelted); + } + + // TODO: onRenderTickStart + // TODO: onRenderTickEnd + public static void onPlayerPreTick(PlayerEntity player) { LifecycleEvents.firePlayerTickEvent(TickEvent.Phase.START, player); } @@ -54,4 +90,20 @@ public static void onPreClientTick() { public static void onPostClientTick() { LifecycleEvents.fireClientTickEvent(TickEvent.Phase.END); } + + public static void onPreServerTick() { + LifecycleEvents.fireServerTickEvent(TickEvent.Phase.START); + } + + public static void onPostServerTick() { + LifecycleEvents.fireServerTickEvent(TickEvent.Phase.END); + } + + public static void onRenderTickStart(float timer) { + LifecycleEvents.fireRenderTickEvent(TickEvent.Phase.START, timer); + } + + public static void onRenderTickEnd(float timer) { + LifecycleEvents.fireRenderTickEvent(TickEvent.Phase.END, timer); + } } diff --git a/patchwork-god-classes/src/main/resources/assets/patchwork-god-classes/icon.png b/patchwork-god-classes/src/main/resources/assets/patchwork-god-classes/icon.png new file mode 100644 index 00000000..de75d2fb Binary files /dev/null and b/patchwork-god-classes/src/main/resources/assets/patchwork-god-classes/icon.png differ diff --git a/patchwork-god-classes/src/main/resources/fabric.mod.json b/patchwork-god-classes/src/main/resources/fabric.mod.json index 4fd1b350..339e514d 100644 --- a/patchwork-god-classes/src/main/resources/fabric.mod.json +++ b/patchwork-god-classes/src/main/resources/fabric.mod.json @@ -15,10 +15,18 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", + "patchwork-api-base": "*", "patchwork-fml": "*", + "patchwork-capabilities": "*", + "patchwork-events-entity": "*", + "patchwork-events-input": "*", "patchwork-events-lifecycle": "*", - "patchwork-events-world": "*" + "patchwork-events-rendering": "*", + "patchwork-events-world": "*", + "patchwork-extensions-block": "*", + "patchwork-extensions-item": "*", + "patchwork-loot": "*", + "patchwork-networking": "*" }, "custom": { "modmenu:api": true, diff --git a/patchwork-gui/build.gradle b/patchwork-gui/build.gradle index 2aaeb8f4..be606503 100644 --- a/patchwork-gui/build.gradle +++ b/patchwork-gui/build.gradle @@ -1,6 +1,6 @@ archivesBaseName = "patchwork-gui" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') } diff --git a/patchwork-gui/src/main/java/net/patchworkmc/mixin/gui/MixinAbstractButtonWidget.java b/patchwork-gui/src/main/java/net/patchworkmc/mixin/gui/MixinAbstractButtonWidget.java new file mode 100644 index 00000000..5434e17f --- /dev/null +++ b/patchwork-gui/src/main/java/net/patchworkmc/mixin/gui/MixinAbstractButtonWidget.java @@ -0,0 +1,40 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.gui; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import net.minecraft.client.gui.DrawableHelper; +import net.minecraft.client.gui.widget.AbstractButtonWidget; + +@Mixin(AbstractButtonWidget.class) +public abstract class MixinAbstractButtonWidget extends DrawableHelper { + @Shadow + protected int height; + + public int getHeight() { + return this.height; + } + + public void setHeight(int value) { + this.height = value; + } +} diff --git a/patchwork-gui/src/main/resources/fabric.mod.json b/patchwork-gui/src/main/resources/fabric.mod.json index fc32dc9b..cea0eadf 100644 --- a/patchwork-gui/src/main/resources/fabric.mod.json +++ b/patchwork-gui/src/main/resources/fabric.mod.json @@ -13,8 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", - "patchwork-fml": "*" + "patchwork-api-base": "*" }, "mixins": [ "patchwork-gui.mixins.json" diff --git a/patchwork-gui/src/main/resources/patchwork-gui.mixins.json b/patchwork-gui/src/main/resources/patchwork-gui.mixins.json index 6bb1e5a0..4a01c95a 100644 --- a/patchwork-gui/src/main/resources/patchwork-gui.mixins.json +++ b/patchwork-gui/src/main/resources/patchwork-gui.mixins.json @@ -3,6 +3,7 @@ "package": "net.patchworkmc.mixin.gui", "compatibilityLevel": "JAVA_8", "client": [ + "MixinAbstractButtonWidget", "MixinAbstractInventoryScreen", "MixinGameRenderer", "MixinKeyboard", diff --git a/patchwork-items/build.gradle b/patchwork-items/build.gradle new file mode 100644 index 00000000..a3a59f9f --- /dev/null +++ b/patchwork-items/build.gradle @@ -0,0 +1,7 @@ +archivesBaseName = "patchwork-items" +version = getSubprojectVersion(project, "0.1.0") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-capabilities', configuration: 'dev') +} diff --git a/patchwork-items/src/main/java/net/minecraftforge/items/CapabilityItemHandler.java b/patchwork-items/src/main/java/net/minecraftforge/items/CapabilityItemHandler.java new file mode 100644 index 00000000..4d00ee3e --- /dev/null +++ b/patchwork-items/src/main/java/net/minecraftforge/items/CapabilityItemHandler.java @@ -0,0 +1,79 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.items; + +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityManager; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.util.math.Direction; + +import net.patchworkmc.api.capability.CapabilityRegisteredCallback; + +public class CapabilityItemHandler { + public static Capability ITEM_HANDLER_CAPABILITY = null; + + public static void register() { + CapabilityRegisteredCallback.event(IItemHandler.class).register(cap -> ITEM_HANDLER_CAPABILITY = cap); + + CapabilityManager.INSTANCE.register(IItemHandler.class, new Capability.IStorage() { + @Override + public Tag writeNBT(Capability capability, IItemHandler instance, Direction side) { + ListTag nbtTagList = new ListTag(); + int size = instance.getSlots(); + + for (int i = 0; i < size; i++) { + ItemStack stack = instance.getStackInSlot(i); + + if (!stack.isEmpty()) { + CompoundTag itemTag = new CompoundTag(); + itemTag.putInt("Slot", i); + stack.toTag(itemTag); + nbtTagList.add(itemTag); + } + } + + return nbtTagList; + } + + @Override + public void readNBT(Capability capability, IItemHandler instance, Direction side, Tag base) { + if (!(instance instanceof IItemHandlerModifiable)) { + throw new RuntimeException("IItemHandler instance does not implement IItemHandlerModifiable"); + } + + IItemHandlerModifiable itemHandlerModifiable = (IItemHandlerModifiable) instance; + ListTag tagList = (ListTag) base; + + for (int i = 0; i < tagList.size(); i++) { + CompoundTag itemTags = tagList.getCompound(i); + int j = itemTags.getInt("Slot"); + + if (j >= 0 && j < instance.getSlots()) { + itemHandlerModifiable.setStackInSlot(j, ItemStack.fromTag(itemTags)); + } + } + } + }, ItemStackHandler::new); + } +} diff --git a/patchwork-items/src/main/java/net/minecraftforge/items/IItemHandler.java b/patchwork-items/src/main/java/net/minecraftforge/items/IItemHandler.java new file mode 100644 index 00000000..23851bb9 --- /dev/null +++ b/patchwork-items/src/main/java/net/minecraftforge/items/IItemHandler.java @@ -0,0 +1,112 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.items; + +import javax.annotation.Nonnull; + +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; + +public interface IItemHandler { + /** + * Returns the number of slots available. + * + * @return The number of slots available + */ + int getSlots(); + + /** + * Returns the ItemStack in a given slot. + * + *

The result's stack size may be greater than the itemstack's max size. + * + *

If the result is empty, then the slot is empty. + * + *

IMPORTANT: This ItemStack MUST NOT be modified. This method is not for + * altering an inventory's contents. Any implementers who are able to detect + * modification through this method should throw an exception. + * + *

SERIOUSLY: DO NOT MODIFY THE RETURNED ITEMSTACK + * + * @param slot Slot to query + * @return ItemStack in given slot. Empty Itemstack if the slot is empty. + */ + @Nonnull + ItemStack getStackInSlot(int slot); + + /** + *

Inserts an ItemStack into the given slot and return the remainder. + * The ItemStack should not be modified in this function! + *

+ * Note: This behaviour is subtly different from net.minecraftforge.fluids.capability.IFluidHandler#fill(net.minecraftforge.fluids.FluidStack, boolean) + * + * @param slot Slot to insert into. + * @param stack ItemStack to insert. This must not be modified by the item handler. + * @param simulate If true, the insertion is only simulated + * @return The remaining ItemStack that was not inserted (if the entire stack is accepted, then return an empty ItemStack). + * May be the same as the input ItemStack if unchanged, otherwise a new ItemStack. + * The returned ItemStack can be safely modified after. + */ + @Nonnull + ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate); + + /** + * Extracts an ItemStack from the given slot. + *

The returned value must be empty if nothing is extracted, + * otherwise its stack size must be less than or equal to {@code amount} and {@link ItemStack#getMaxCount()} ()}. + *

+ * + * @param slot Slot to extract from. + * @param amount Amount to extract (may be greater than the current stack's max limit) + * @param simulate If true, the extraction is only simulated + * @return ItemStack extracted from the slot, must be empty if nothing can be extracted. + * The returned ItemStack can be safely modified after, so item handlers should return a new or copied stack. + */ + @Nonnull + ItemStack extractItem(int slot, int amount, boolean simulate); + + /** + * Retrieves the maximum stack size allowed to exist in the given slot. + * + * @param slot Slot to query. + * @return The maximum stack size allowed in the slot. + */ + int getSlotLimit(int slot); + + /** + *

This function re-implements the vanilla function {@link Inventory#isValidInvStack(int, ItemStack)}. + * It should be used instead of simulated insertions in cases where the contents and state of the inventory are + * irrelevant, mainly for the purpose of automation and logic (for instance, testing if a minecart can wait + * to deposit its items into a full inventory, or if the items in the minecart can never be placed into the + * inventory and should move on). + *

+ *
    + *
  • isItemValid is false when insertion of the item is never valid.
  • + *
  • When isItemValid is true, no assumptions can be made and insertion must be simulated case-by-case.
  • + *
  • The actual items in the inventory, its fullness, or any other state are not considered by isItemValid.
  • + *
+ * + * @param slot Slot to query for validity + * @param stack Stack to test with for validity + * @return true if the slot can insert the ItemStack, not considering the current state of the inventory. + * false if the slot can never insert the ItemStack in any situation. + */ + boolean isItemValid(int slot, @Nonnull ItemStack stack); +} diff --git a/patchwork-items/src/main/java/net/minecraftforge/items/IItemHandlerModifiable.java b/patchwork-items/src/main/java/net/minecraftforge/items/IItemHandlerModifiable.java new file mode 100644 index 00000000..e24731cc --- /dev/null +++ b/patchwork-items/src/main/java/net/minecraftforge/items/IItemHandlerModifiable.java @@ -0,0 +1,39 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.items; + +import javax.annotation.Nonnull; + +import net.minecraft.item.ItemStack; + +public interface IItemHandlerModifiable extends IItemHandler { + /** + * Overrides the stack in the given slot. This method is used by the + * standard Forge helper methods and classes. It is not intended for + * general use by other mods, and the handler may throw an error if it + * is called unexpectedly. + * + * @param slot Slot to modify + * @param stack ItemStack to set slot to (may be empty). + * @throws RuntimeException if the handler is called in a way that the handler + * was not expecting. + */ + void setStackInSlot(int slot, @Nonnull ItemStack stack); +} diff --git a/patchwork-items/src/main/java/net/minecraftforge/items/ItemHandlerHelper.java b/patchwork-items/src/main/java/net/minecraftforge/items/ItemHandlerHelper.java new file mode 100644 index 00000000..9fc6ed38 --- /dev/null +++ b/patchwork-items/src/main/java/net/minecraftforge/items/ItemHandlerHelper.java @@ -0,0 +1,219 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.items; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.MathHelper; + +import net.patchworkmc.impl.capability.CapabilityProviderHolder; + +public class ItemHandlerHelper { + private static boolean patchwork$areItemStackCapsCompatible(ItemStack a, ItemStack b) { + return ((CapabilityProviderHolder) (Object) a).areCapsCompatible(((CapabilityProviderHolder) (Object) b).getCapabilityProvider()); + } + + @Nonnull + public static ItemStack insertItem(IItemHandler dest, @Nonnull ItemStack stack, boolean simulate) { + if (dest == null || stack.isEmpty()) { + return stack; + } + + for (int i = 0; i < dest.getSlots(); i++) { + stack = dest.insertItem(i, stack, simulate); + + if (stack.isEmpty()) { + return ItemStack.EMPTY; + } + } + + return stack; + } + + public static boolean canItemStacksStack(@Nonnull ItemStack a, @Nonnull ItemStack b) { + if (a.isEmpty() || !a.isItemEqualIgnoreDamage(b) || a.hasTag() != b.hasTag()) { + return false; + } + + return (!a.hasTag() || a.getTag().equals(b.getTag())) && patchwork$areItemStackCapsCompatible(a, b); + } + + /** + * A relaxed version of canItemStacksStack that stacks itemstacks with different metadata if they don't have subtypes. + * This usually only applies when players pick up items. + */ + public static boolean canItemStacksStackRelaxed(@Nonnull ItemStack a, @Nonnull ItemStack b) { + if (a.isEmpty() || b.isEmpty() || a.getItem() != b.getItem()) { + return false; + } + + if (!a.isStackable()) { + return false; + } + + if (a.hasTag() != b.hasTag()) { + return false; + } + + return (!a.hasTag() || a.getTag().equals(b.getTag())) && patchwork$areItemStackCapsCompatible(a, b); + } + + @Nonnull + public static ItemStack copyStackWithSize(@Nonnull ItemStack itemStack, int size) { + if (size == 0) { + return ItemStack.EMPTY; + } + + ItemStack copy = itemStack.copy(); + copy.setCount(size); + return copy; + } + + /** + * Inserts the ItemStack into the inventory, filling up already present stacks first. + * This is equivalent to the behaviour of a player picking up an item. + * Note: This function stacks items without subtypes with different metadata together. + */ + @Nonnull + public static ItemStack insertItemStacked(IItemHandler inventory, @Nonnull ItemStack stack, boolean simulate) { + if (inventory == null || stack.isEmpty()) { + return stack; + } + + // not stackable -> just insert into a new slot + if (!stack.isStackable()) { + return insertItem(inventory, stack, simulate); + } + + int sizeInventory = inventory.getSlots(); + + // go through the inventory and try to fill up already existing items + for (int i = 0; i < sizeInventory; i++) { + ItemStack slot = inventory.getStackInSlot(i); + + if (canItemStacksStackRelaxed(slot, stack)) { + stack = inventory.insertItem(i, stack, simulate); + + if (stack.isEmpty()) { + break; + } + } + } + + // insert remainder into empty slots + if (!stack.isEmpty()) { + // find empty slot + for (int i = 0; i < sizeInventory; i++) { + if (inventory.getStackInSlot(i).isEmpty()) { + stack = inventory.insertItem(i, stack, simulate); + + if (stack.isEmpty()) { + break; + } + } + } + } + + return stack; + } + + /** + * giveItemToPlayer without preferred slot + */ + + /* TODO: PlayerMainInvWrapper + public static void giveItemToPlayer(PlayerEntity player, @Nonnull ItemStack stack) { + giveItemToPlayer(player, stack, -1); + }*/ + + /** + * Inserts the given itemstack into the players inventory. + * If the inventory can't hold it, the item will be dropped in the world at the players position. + * + * @param player The player to give the item to + * @param stack The itemstack to insert + */ + + /* TODO: PlayerMainInvWrapper + public static void giveItemToPlayer(PlayerEntity player, @Nonnull ItemStack stack, int preferredSlot) { + if (stack.isEmpty()) return; + + IItemHandler inventory = new PlayerMainInvWrapper(player.inventory); + World world = player.world; + + // try adding it into the inventory + ItemStack remainder = stack; + + // insert into preferred slot first + if (preferredSlot >= 0 && preferredSlot < inventory.getSlots()) { + remainder = inventory.insertItem(preferredSlot, stack, false); + } + + // then into the inventory in general + if (!remainder.isEmpty()) { + remainder = insertItemStacked(inventory, remainder, false); + } + + // play sound if something got picked up + if (remainder.isEmpty() || remainder.getCount() != stack.getCount()) { + world.playSound(null, player.x, player.y + 0.5, player.z, + SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.PLAYERS, 0.2F, ((world.random.nextFloat() - world.random.nextFloat()) * 0.7F + 1.0F) * 2.0F); + } + + // drop remaining itemstack into the world + if (!remainder.isEmpty() && !world.isClient) { + ItemEntity entityitem = new ItemEntity(world, player.x, player.y + 0.5, player.z, remainder); + entityitem.setPickupDelay(40); + entityitem.setVelocity(entityitem.getVelocity().multiply(0, 1, 0)); + + world.spawnEntity(entityitem); + } + }*/ + + /** + * This method uses the standard vanilla algorithm to calculate a comparator output for how "full" the inventory is. + * This method is an adaptation of Container#calculateComparatorOutput(Inventory). + * + * @param inv The inventory handler to test. + * @return A redstone value in the range [0,15] representing how "full" this inventory is. + */ + public static int calcRedstoneFromInventory(@Nullable IItemHandler inv) { + if (inv == null) { + return 0; + } else { + int itemsFound = 0; + float proportion = 0.0F; + + for (int j = 0; j < inv.getSlots(); ++j) { + ItemStack itemstack = inv.getStackInSlot(j); + + if (!itemstack.isEmpty()) { + proportion += (float) itemstack.getCount() / (float) Math.min(inv.getSlotLimit(j), itemstack.getMaxCount()); + ++itemsFound; + } + } + + proportion = proportion / (float) inv.getSlots(); + return MathHelper.floor(proportion * 14.0F) + (itemsFound > 0 ? 1 : 0); + } + } +} diff --git a/patchwork-items/src/main/java/net/minecraftforge/items/ItemStackHandler.java b/patchwork-items/src/main/java/net/minecraftforge/items/ItemStackHandler.java new file mode 100644 index 00000000..064ec704 --- /dev/null +++ b/patchwork-items/src/main/java/net/minecraftforge/items/ItemStackHandler.java @@ -0,0 +1,210 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.items; + +import javax.annotation.Nonnull; + +import net.minecraftforge.common.util.INBTSerializable; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.util.DefaultedList; + +import net.fabricmc.fabric.api.util.NbtType; + +public class ItemStackHandler implements IItemHandler, IItemHandlerModifiable, INBTSerializable { + protected DefaultedList stacks; + + public ItemStackHandler() { + this(1); + } + + public ItemStackHandler(int size) { + stacks = DefaultedList.ofSize(size, ItemStack.EMPTY); + } + + public ItemStackHandler(DefaultedList stacks) { + this.stacks = stacks; + } + + public void setSize(int size) { + stacks = DefaultedList.ofSize(size, ItemStack.EMPTY); + } + + @Override + public void setStackInSlot(int slot, @Nonnull ItemStack stack) { + validateSlotIndex(slot); + this.stacks.set(slot, stack); + onContentsChanged(slot); + } + + @Override + public int getSlots() { + return stacks.size(); + } + + @Override + @Nonnull + public ItemStack getStackInSlot(int slot) { + validateSlotIndex(slot); + return this.stacks.get(slot); + } + + @Override + @Nonnull + public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) { + if (stack.isEmpty()) { + return ItemStack.EMPTY; + } + + if (!isItemValid(slot, stack)) { + return stack; + } + + validateSlotIndex(slot); + + ItemStack existing = this.stacks.get(slot); + + int limit = getStackLimit(slot, stack); + + if (!existing.isEmpty()) { + if (!ItemHandlerHelper.canItemStacksStack(stack, existing)) { + return stack; + } + + limit -= existing.getCount(); + } + + if (limit <= 0) { + return stack; + } + + boolean reachedLimit = stack.getCount() > limit; + + if (!simulate) { + if (existing.isEmpty()) { + this.stacks.set(slot, reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, limit) : stack); + } else { + existing.increment(reachedLimit ? limit : stack.getCount()); + } + + onContentsChanged(slot); + } + + return reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, stack.getCount() - limit) : ItemStack.EMPTY; + } + + @Override + @Nonnull + public ItemStack extractItem(int slot, int amount, boolean simulate) { + if (amount == 0) { + return ItemStack.EMPTY; + } + + validateSlotIndex(slot); + + ItemStack existing = this.stacks.get(slot); + + if (existing.isEmpty()) { + return ItemStack.EMPTY; + } + + int toExtract = Math.min(amount, existing.getMaxCount()); + + if (existing.getCount() <= toExtract) { + if (!simulate) { + this.stacks.set(slot, ItemStack.EMPTY); + onContentsChanged(slot); + } + + return existing; + } else { + if (!simulate) { + this.stacks.set(slot, ItemHandlerHelper.copyStackWithSize(existing, existing.getCount() - toExtract)); + onContentsChanged(slot); + } + + return ItemHandlerHelper.copyStackWithSize(existing, toExtract); + } + } + + @Override + public int getSlotLimit(int slot) { + return 64; + } + + protected int getStackLimit(int slot, @Nonnull ItemStack stack) { + return Math.min(getSlotLimit(slot), stack.getMaxCount()); + } + + @Override + public boolean isItemValid(int slot, @Nonnull ItemStack stack) { + return true; + } + + @Override + public CompoundTag serializeNBT() { + ListTag nbtTagList = new ListTag(); + + for (int i = 0; i < stacks.size(); i++) { + if (!stacks.get(i).isEmpty()) { + CompoundTag itemTag = new CompoundTag(); + itemTag.putInt("Slot", i); + stacks.get(i).toTag(itemTag); + nbtTagList.add(itemTag); + } + } + + CompoundTag nbt = new CompoundTag(); + nbt.put("Items", nbtTagList); + nbt.putInt("Size", stacks.size()); + return nbt; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + setSize(nbt.contains("Size", NbtType.INT) ? nbt.getInt("Size") : stacks.size()); + ListTag tagList = nbt.getList("Items", NbtType.COMPOUND); + + for (int i = 0; i < tagList.size(); i++) { + CompoundTag itemTags = tagList.getCompound(i); + int slot = itemTags.getInt("Slot"); + + if (slot >= 0 && slot < stacks.size()) { + stacks.set(slot, ItemStack.fromTag(itemTags)); + } + } + + onLoad(); + } + + protected void validateSlotIndex(int slot) { + if (slot < 0 || slot >= stacks.size()) { + throw new RuntimeException("Slot " + slot + " not in valid range - [0," + stacks.size() + ")"); + } + } + + protected void onLoad() { + } + + protected void onContentsChanged(int slot) { + } +} diff --git a/patchwork-items/src/main/java/net/patchworkmc/impl/items/ItemHandlerInventoryWrapper.java b/patchwork-items/src/main/java/net/patchworkmc/impl/items/ItemHandlerInventoryWrapper.java new file mode 100644 index 00000000..219899e3 --- /dev/null +++ b/patchwork-items/src/main/java/net/patchworkmc/impl/items/ItemHandlerInventoryWrapper.java @@ -0,0 +1,92 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.items; + +import net.minecraftforge.items.IItemHandlerModifiable; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; + +public class ItemHandlerInventoryWrapper implements Inventory { + private IItemHandlerModifiable itemHandler; + + public ItemHandlerInventoryWrapper(IItemHandlerModifiable itemHandler) { + this.itemHandler = itemHandler; + } + + @Override + public int getInvSize() { + return itemHandler.getSlots(); + } + + @Override + public boolean isInvEmpty() { + for (int i = 0; i < itemHandler.getSlots(); i++) { + if (!itemHandler.getStackInSlot(i).isEmpty()) { + return false; + } + } + + return true; + } + + @Override + public ItemStack getInvStack(int slot) { + return itemHandler.getStackInSlot(slot); + } + + @Override + public ItemStack takeInvStack(int slot, int amount) { + return itemHandler.extractItem(slot, amount, false); + } + + @Override + public ItemStack removeInvStack(int slot) { + ItemStack copy = itemHandler.getStackInSlot(slot).copy(); + itemHandler.setStackInSlot(slot, ItemStack.EMPTY); + return copy; + } + + @Override + public void setInvStack(int slot, ItemStack stack) { + itemHandler.setStackInSlot(slot, stack); + } + + @Override + public void markDirty() { + } + + @Override + public boolean canPlayerUseInv(PlayerEntity player) { + return false; + } + + @Override + public void clear() { + for (int i = 0; i < itemHandler.getSlots(); i++) { + itemHandler.setStackInSlot(i, ItemStack.EMPTY); + } + } + + public IItemHandlerModifiable getItemHandler() { + return this.itemHandler; + } +} diff --git a/patchwork-items/src/main/java/net/patchworkmc/impl/items/PatchworkItems.java b/patchwork-items/src/main/java/net/patchworkmc/impl/items/PatchworkItems.java new file mode 100644 index 00000000..fba684d7 --- /dev/null +++ b/patchwork-items/src/main/java/net/patchworkmc/impl/items/PatchworkItems.java @@ -0,0 +1,38 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.items; + +import net.minecraftforge.items.CapabilityItemHandler; + +import net.minecraft.util.math.Direction; + +import net.fabricmc.api.ModInitializer; + +public class PatchworkItems implements ModInitializer { + /** + * Shared ThreadLocal for {@link net.patchworkmc.mixin.items.MixinDropperBlock} and {@link net.patchworkmc.mixin.items.MixinHopperBlockEntity}. + */ + public static final ThreadLocal currentSide = new ThreadLocal<>(); + + @Override + public void onInitialize() { + CapabilityItemHandler.register(); + } +} diff --git a/patchwork-items/src/main/java/net/patchworkmc/mixin/items/MixinDropperBlock.java b/patchwork-items/src/main/java/net/patchworkmc/mixin/items/MixinDropperBlock.java new file mode 100644 index 00000000..b4bd9c1f --- /dev/null +++ b/patchwork-items/src/main/java/net/patchworkmc/mixin/items/MixinDropperBlock.java @@ -0,0 +1,39 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.items; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.block.DropperBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import net.patchworkmc.impl.items.PatchworkItems; + +@Mixin(DropperBlock.class) +public class MixinDropperBlock { + @Inject(method = "dispense", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/entity/HopperBlockEntity;getInventoryAt(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/inventory/Inventory;")) + private void onDispenseGetInventory(World world, BlockPos pos, CallbackInfo ci) { + PatchworkItems.currentSide.set(world.getBlockState(pos).get(DropperBlock.FACING)); + } +} diff --git a/patchwork-items/src/main/java/net/patchworkmc/mixin/items/MixinHopperBlockEntity.java b/patchwork-items/src/main/java/net/patchworkmc/mixin/items/MixinHopperBlockEntity.java new file mode 100644 index 00000000..bc8ffb7d --- /dev/null +++ b/patchwork-items/src/main/java/net/patchworkmc/mixin/items/MixinHopperBlockEntity.java @@ -0,0 +1,93 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.items; + +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.IItemHandlerModifiable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.HopperBlock; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.Hopper; +import net.minecraft.block.entity.HopperBlockEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; + +import net.patchworkmc.impl.items.PatchworkItems; +import net.patchworkmc.impl.items.ItemHandlerInventoryWrapper; + +@Mixin(HopperBlockEntity.class) +public class MixinHopperBlockEntity { + @Inject(method = "getInputInventory", at = @At("HEAD")) + private static void onGetInputInventory(Hopper hopper, CallbackInfoReturnable cir) { + PatchworkItems.currentSide.set(Direction.UP); + } + + @Inject(method = "getOutputInventory", at = @At("HEAD")) + private void onGetOutputInventory(CallbackInfoReturnable cir) { + PatchworkItems.currentSide.set(((BlockEntity) (Object) this).getCachedState().get(HopperBlock.FACING)); + } + + @Inject(method = "getInventoryAt(Lnet/minecraft/world/World;DDD)Lnet/minecraft/inventory/Inventory;", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;getEntities(Lnet/minecraft/entity/Entity;Lnet/minecraft/util/math/Box;Ljava/util/function/Predicate;)Ljava/util/List;"), cancellable = true) + private static void onGetInventoryAt(World world, double x, double y, double z, CallbackInfoReturnable cir) { + BlockPos blockPos = new BlockPos(x, y, z); + BlockState blockState = world.getBlockState(blockPos); + Block block = blockState.getBlock(); + + if (block.hasBlockEntity()) { + BlockEntity blockEntity = world.getBlockEntity(blockPos); + + if (blockEntity != null) { + Direction direction = PatchworkItems.currentSide.get(); + + if (direction != null) { + LazyOptional capability = ((ICapabilityProvider) blockEntity).getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, direction.getOpposite()); + PatchworkItems.currentSide.remove(); + capability.ifPresent(result -> { + if (result instanceof IItemHandlerModifiable) { + cir.setReturnValue(new ItemHandlerInventoryWrapper((IItemHandlerModifiable) result)); + } + }); + } + } + } + } + + @Inject(method = "transfer(Lnet/minecraft/inventory/Inventory;Lnet/minecraft/inventory/Inventory;Lnet/minecraft/item/ItemStack;ILnet/minecraft/util/math/Direction;)Lnet/minecraft/item/ItemStack;", at = @At("HEAD"), cancellable = true) + private static void onTransfer(Inventory from, Inventory to, ItemStack stack, int slot, Direction direction, CallbackInfoReturnable cir) { + IItemHandlerModifiable itemHandler; + + if (to instanceof ItemHandlerInventoryWrapper) { + itemHandler = ((ItemHandlerInventoryWrapper) to).getItemHandler(); + cir.setReturnValue(itemHandler.insertItem(slot, stack, false)); + } + } +} diff --git a/patchwork-items/src/main/resources/assets/patchwork-items/icon.png b/patchwork-items/src/main/resources/assets/patchwork-items/icon.png new file mode 100644 index 00000000..de75d2fb Binary files /dev/null and b/patchwork-items/src/main/resources/assets/patchwork-items/icon.png differ diff --git a/patchwork-items/src/main/resources/fabric.mod.json b/patchwork-items/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..93ef2083 --- /dev/null +++ b/patchwork-items/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "patchwork-items", + "version": "${version}", + "name": "Patchwork Items", + "description": "A reimplementation of the Minecraft Forge item handling APIs.", + "authors": [ + "coderbot" + ], + "license": "LGPL-2.1-only", + "icon": "assets/patchwork-items/icon.png", + "environment": "*", + "depends": { + "patchwork-api-base": "*" + }, + "mixins": [ + "patchwork-items.mixins.json" + ], + "entrypoints": { + "main": ["net.patchworkmc.impl.items.PatchworkItems"] + }, + "custom": { + "modmenu:api": true, + "modmenu:parent": "patchwork" + } +} diff --git a/patchwork-items/src/main/resources/patchwork-items.mixins.json b/patchwork-items/src/main/resources/patchwork-items.mixins.json new file mode 100644 index 00000000..edf8c6d6 --- /dev/null +++ b/patchwork-items/src/main/resources/patchwork-items.mixins.json @@ -0,0 +1,12 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin.items", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MixinDropperBlock", + "MixinHopperBlockEntity" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/patchwork-key-bindings/build.gradle b/patchwork-key-bindings/build.gradle new file mode 100644 index 00000000..33c56484 --- /dev/null +++ b/patchwork-key-bindings/build.gradle @@ -0,0 +1,6 @@ +archivesBaseName = "patchwork-key-bindings" +version = getSubprojectVersion(project, "0.2.0") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') +} diff --git a/patchwork-key-bindings/src/main/java/net/minecraftforge/client/extensions/IForgeKeybinding.java b/patchwork-key-bindings/src/main/java/net/minecraftforge/client/extensions/IForgeKeybinding.java new file mode 100644 index 00000000..ee2384ac --- /dev/null +++ b/patchwork-key-bindings/src/main/java/net/minecraftforge/client/extensions/IForgeKeybinding.java @@ -0,0 +1,69 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.extensions; + +import javax.annotation.Nonnull; + +import net.minecraftforge.client.settings.IKeyConflictContext; +import net.minecraftforge.client.settings.KeyModifier; + +import net.minecraft.client.options.KeyBinding; +import net.minecraft.client.util.InputUtil; + +public interface IForgeKeybinding { + default KeyBinding getKeyBinding() { + return (KeyBinding) this; + } + + @Nonnull + InputUtil.KeyCode getKey(); + + /** + * Checks that the key conflict context and modifier are active, and that the keyCode matches this binding. + */ + default boolean isActiveAndMatches(InputUtil.KeyCode keyCode) { + return keyCode.getKeyCode() != 0 && keyCode.equals(getKey()) && getKeyConflictContext().isActive() && getKeyModifier().isActive(getKeyConflictContext()); + } + + default void setToDefault() { + setKeyModifierAndCode(getKeyModifierDefault(), getKeyBinding().getDefaultKeyCode()); + } + + IKeyConflictContext getKeyConflictContext(); + + void setKeyConflictContext(IKeyConflictContext keyConflictContext); + + KeyModifier getKeyModifierDefault(); + + KeyModifier getKeyModifier(); + + void setKeyModifierAndCode(KeyModifier keyModifier, InputUtil.KeyCode keyCode); + + /** + * Returns true when one of the bindings' key codes conflicts with the other's modifier. + */ + default boolean hasKeyCodeModifierConflict(KeyBinding other) { + if (getKeyConflictContext().conflicts(((IForgeKeybinding) other).getKeyConflictContext()) || ((IForgeKeybinding) other).getKeyConflictContext().conflicts(getKeyConflictContext())) { + return getKeyModifier().matches(((IForgeKeybinding) other).getKey()) || ((IForgeKeybinding) other).getKeyModifier().matches(getKey()); + } + + return false; + } +} diff --git a/patchwork-key-bindings/src/main/java/net/minecraftforge/client/settings/IKeyConflictContext.java b/patchwork-key-bindings/src/main/java/net/minecraftforge/client/settings/IKeyConflictContext.java new file mode 100644 index 00000000..78515753 --- /dev/null +++ b/patchwork-key-bindings/src/main/java/net/minecraftforge/client/settings/IKeyConflictContext.java @@ -0,0 +1,37 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.settings; + +/** + * Defines the context that a {@link KeyBinding} is used. + * Key conflicts occur when a {@link KeyBinding} has the same {@link IKeyConflictContext} and has conflicting modifiers and keyCodes. + */ +public interface IKeyConflictContext { + /** + * @return true if conditions are met to activate {@link KeyBinding}s with this context + */ + boolean isActive(); + + /** + * @return true if the other context can have {@link KeyBinding} conflicts with this one. + * This will be called on both contexts to check for conflicts. + */ + boolean conflicts(IKeyConflictContext other); +} diff --git a/patchwork-key-bindings/src/main/java/net/minecraftforge/client/settings/KeyConflictContext.java b/patchwork-key-bindings/src/main/java/net/minecraftforge/client/settings/KeyConflictContext.java new file mode 100644 index 00000000..b0ae3034 --- /dev/null +++ b/patchwork-key-bindings/src/main/java/net/minecraftforge/client/settings/KeyConflictContext.java @@ -0,0 +1,70 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.settings; + +import net.minecraft.client.MinecraftClient; + +public enum KeyConflictContext implements IKeyConflictContext { + /** + * Universal key bindings are used in every context and will conflict with any other context. + * Key Bindings are universal by default. + */ + UNIVERSAL { + @Override + public boolean isActive() { + return true; + } + + @Override + public boolean conflicts(IKeyConflictContext other) { + return true; + } + }, + + /** + * Gui key bindings are only used when a {@link GuiScreen} is open. + */ + GUI { + @Override + public boolean isActive() { + return MinecraftClient.getInstance().currentScreen != null; + } + + @Override + public boolean conflicts(IKeyConflictContext other) { + return this == other; + } + }, + + /** + * In-game key bindings are only used when a {@link GuiScreen} is not open. + */ + IN_GAME { + @Override + public boolean isActive() { + return !GUI.isActive(); + } + + @Override + public boolean conflicts(IKeyConflictContext other) { + return this == other; + } + } +} diff --git a/patchwork-key-bindings/src/main/java/net/minecraftforge/client/settings/KeyModifier.java b/patchwork-key-bindings/src/main/java/net/minecraftforge/client/settings/KeyModifier.java new file mode 100644 index 00000000..22cb1165 --- /dev/null +++ b/patchwork-key-bindings/src/main/java/net/minecraftforge/client/settings/KeyModifier.java @@ -0,0 +1,150 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.settings; + +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +import org.lwjgl.glfw.GLFW; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.client.util.InputUtil; + +public enum KeyModifier { + CONTROL { + @Override + public boolean matches(InputUtil.KeyCode key) { + int keyCode = key.getKeyCode(); + + if (MinecraftClient.IS_SYSTEM_MAC) { + return keyCode == GLFW.GLFW_KEY_LEFT_ALT || keyCode == GLFW.GLFW_KEY_RIGHT_ALT; + } else { + return keyCode == GLFW.GLFW_KEY_LEFT_CONTROL || keyCode == GLFW.GLFW_KEY_RIGHT_CONTROL; + } + } + + @Override + public boolean isActive(@Nullable IKeyConflictContext conflictContext) { + return Screen.hasControlDown(); + } + + @Override + public String getLocalizedComboName(InputUtil.KeyCode key, Supplier defaultLogic) { + String keyName = defaultLogic.get(); + String localizationFormatKey = MinecraftClient.IS_SYSTEM_MAC ? "forge.controlsgui.control.mac" : "forge.controlsgui.control"; + return I18n.translate(localizationFormatKey, keyName); + } + }, + SHIFT { + @Override + public boolean matches(InputUtil.KeyCode key) { + return key.getKeyCode() == GLFW.GLFW_KEY_LEFT_SHIFT || key.getKeyCode() == GLFW.GLFW_KEY_RIGHT_SHIFT; + } + + @Override + public boolean isActive(@Nullable IKeyConflictContext conflictContext) { + return Screen.hasShiftDown(); + } + + @Override + public String getLocalizedComboName(InputUtil.KeyCode key, Supplier defaultLogic) { + return I18n.translate("forge.controlsgui.shift", defaultLogic.get()); + } + }, + ALT { + @Override + public boolean matches(InputUtil.KeyCode key) { + return key.getKeyCode() == GLFW.GLFW_KEY_LEFT_ALT || key.getKeyCode() == GLFW.GLFW_KEY_RIGHT_ALT; + } + + @Override + public boolean isActive(@Nullable IKeyConflictContext conflictContext) { + return Screen.hasAltDown(); + } + + @Override + public String getLocalizedComboName(InputUtil.KeyCode keyCode, Supplier defaultLogic) { + return I18n.translate("forge.controlsgui.alt", defaultLogic.get()); + } + }, + NONE { + @Override + public boolean matches(InputUtil.KeyCode key) { + return false; + } + + @Override + public boolean isActive(@Nullable IKeyConflictContext conflictContext) { + if (conflictContext != null && !conflictContext.conflicts(KeyConflictContext.IN_GAME)) { + for (KeyModifier keyModifier : MODIFIER_VALUES) { + if (keyModifier.isActive(conflictContext)) { + return false; + } + } + } + + return true; + } + + @Override + public String getLocalizedComboName(InputUtil.KeyCode key, Supplier defaultLogic) { + return defaultLogic.get(); + } + }; + + public static final KeyModifier[] MODIFIER_VALUES = {SHIFT, CONTROL, ALT}; + + public static KeyModifier getActiveModifier() { + for (KeyModifier keyModifier : MODIFIER_VALUES) { + if (keyModifier.isActive(null)) { + return keyModifier; + } + } + + return NONE; + } + + public static boolean isKeyCodeModifier(InputUtil.KeyCode key) { + for (KeyModifier keyModifier : MODIFIER_VALUES) { + if (keyModifier.matches(key)) { + return true; + } + } + + return false; + } + + public static KeyModifier valueFromString(String stringValue) { + try { + return valueOf(stringValue); + } catch (NullPointerException | IllegalArgumentException ignored) { + return NONE; + } + } + + public abstract boolean matches(InputUtil.KeyCode key); + + public abstract boolean isActive(@Nullable IKeyConflictContext conflictContext); + + public abstract String getLocalizedComboName(InputUtil.KeyCode key, Supplier defaultLogic); +} diff --git a/patchwork-key-bindings/src/main/java/net/patchworkmc/api/keybindings/PatchworkKeyBinding.java b/patchwork-key-bindings/src/main/java/net/patchworkmc/api/keybindings/PatchworkKeyBinding.java new file mode 100644 index 00000000..664b217b --- /dev/null +++ b/patchwork-key-bindings/src/main/java/net/patchworkmc/api/keybindings/PatchworkKeyBinding.java @@ -0,0 +1,63 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.api.keybindings; + +import net.minecraftforge.client.extensions.IForgeKeybinding; +import net.minecraftforge.client.settings.IKeyConflictContext; +import net.minecraftforge.client.settings.KeyModifier; + +import net.minecraft.client.options.KeyBinding; +import net.minecraft.client.util.InputUtil; + +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; + +import net.patchworkmc.impl.keybindings.ForgeKeyBindingConstruct; + +public class PatchworkKeyBinding extends KeyBinding { + public PatchworkKeyBinding(String id, int keyCode, String category) { + super(id, keyCode, category); + KeyBindingHelper.registerKeyBinding(this); + } + + public PatchworkKeyBinding(String id, InputUtil.Type type, int code, String category) { + super(id, type, code, category); + KeyBindingHelper.registerKeyBinding(this); + } + + public PatchworkKeyBinding(String id, IKeyConflictContext keyConflictContext, final InputUtil.Type inputType, final int keyCode, String category) { + this(id, keyConflictContext, inputType.createFromCode(keyCode), category); + } + + public PatchworkKeyBinding(String id, IKeyConflictContext keyConflictContext, InputUtil.KeyCode keyCode, String category) { + this(id, keyConflictContext, KeyModifier.NONE, keyCode, category); + } + + public PatchworkKeyBinding(String id, IKeyConflictContext keyConflictContext, KeyModifier keyModifier, final InputUtil.Type inputType, final int keyCode, String category) { + this(id, keyConflictContext, keyModifier, inputType.createFromCode(keyCode), category); + } + + public PatchworkKeyBinding(String id, IKeyConflictContext keyConflictContext, KeyModifier keyModifier, InputUtil.KeyCode keyCode, String category) { + super(id, keyCode.getCategory(), keyCode.getKeyCode(), category); + ((IForgeKeybinding) this).setKeyConflictContext(keyConflictContext); + ((IForgeKeybinding) this).setKeyModifierAndCode(keyModifier, keyCode); + ((ForgeKeyBindingConstruct) this).patchwork$constructForgeKeyBindingOptions(keyConflictContext, keyModifier.matches(keyCode) ? KeyModifier.NONE : keyModifier); + KeyBindingHelper.registerKeyBinding(this); + } +} diff --git a/patchwork-key-bindings/src/main/java/net/patchworkmc/impl/keybindings/ForgeKeyBindingConstruct.java b/patchwork-key-bindings/src/main/java/net/patchworkmc/impl/keybindings/ForgeKeyBindingConstruct.java new file mode 100644 index 00000000..29217348 --- /dev/null +++ b/patchwork-key-bindings/src/main/java/net/patchworkmc/impl/keybindings/ForgeKeyBindingConstruct.java @@ -0,0 +1,27 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.keybindings; + +import net.minecraftforge.client.settings.IKeyConflictContext; +import net.minecraftforge.client.settings.KeyModifier; + +public interface ForgeKeyBindingConstruct { + void patchwork$constructForgeKeyBindingOptions(IKeyConflictContext keyConflictContext, KeyModifier keyModifier); +} diff --git a/patchwork-key-bindings/src/main/java/net/patchworkmc/mixin/keybindings/MixinKeyBinding.java b/patchwork-key-bindings/src/main/java/net/patchworkmc/mixin/keybindings/MixinKeyBinding.java new file mode 100644 index 00000000..ad2bcff2 --- /dev/null +++ b/patchwork-key-bindings/src/main/java/net/patchworkmc/mixin/keybindings/MixinKeyBinding.java @@ -0,0 +1,103 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.keybindings; + +import net.minecraftforge.client.extensions.IForgeKeybinding; +import net.minecraftforge.client.settings.IKeyConflictContext; +import net.minecraftforge.client.settings.KeyConflictContext; +import net.minecraftforge.client.settings.KeyModifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.client.options.KeyBinding; +import net.minecraft.client.util.InputUtil; + +import net.patchworkmc.impl.keybindings.ForgeKeyBindingConstruct; + +@Mixin(KeyBinding.class) +public abstract class MixinKeyBinding implements Comparable, IForgeKeybinding, ForgeKeyBindingConstruct { + @Shadow + private InputUtil.KeyCode keyCode; + + // These exist in forge to allow modifiers and conflicting keys to work, this is not implemented + // but these remain to avoid stubbing the methods + @Unique + private KeyModifier keyModifierDefault = KeyModifier.NONE; + @Unique + private KeyModifier keyModifier = KeyModifier.NONE; + @Unique + private IKeyConflictContext keyConflictContext = KeyConflictContext.UNIVERSAL; + + @Inject(method = "isPressed()Z", at = @At("HEAD")) + public void isPressed(CallbackInfoReturnable info) { + if (!getKeyConflictContext().isActive()) { + info.setReturnValue(false); + info.cancel(); + } + } + + @Override + public InputUtil.KeyCode getKey() { + return this.keyCode; + } + + @Override + public IKeyConflictContext getKeyConflictContext() { + return keyConflictContext; + } + + @Override + public void setKeyConflictContext(IKeyConflictContext keyConflictContext) { + this.keyConflictContext = keyConflictContext; + } + + @Override + public KeyModifier getKeyModifierDefault() { + return keyModifierDefault; + } + + @Override + public KeyModifier getKeyModifier() { + return keyModifier; + } + + @Override + public void setKeyModifierAndCode(KeyModifier keyModifier, InputUtil.KeyCode keyCode) { + this.keyCode = keyCode; + + if (keyModifier.matches(keyCode)) { + keyModifier = KeyModifier.NONE; + } + + this.keyModifier = keyModifier; + KeyBinding.updateKeysByCode(); + } + + @Override + public void patchwork$constructForgeKeyBindingOptions(IKeyConflictContext keyConflictContext, KeyModifier keyModifier) { + this.keyModifier = keyModifier; + this.keyModifierDefault = keyModifier; + this.keyConflictContext = keyConflictContext; + } +} diff --git a/patchwork-key-bindings/src/main/resources/assets/patchwork-key-bindings/icon.png b/patchwork-key-bindings/src/main/resources/assets/patchwork-key-bindings/icon.png new file mode 100644 index 00000000..de75d2fb Binary files /dev/null and b/patchwork-key-bindings/src/main/resources/assets/patchwork-key-bindings/icon.png differ diff --git a/patchwork-key-bindings/src/main/resources/fabric.mod.json b/patchwork-key-bindings/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..bed15e69 --- /dev/null +++ b/patchwork-key-bindings/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "patchwork-key-bindings", + "name": "Patchwork Keybindings", + "version": "${version}", + "license": "LGPL-2.1-only", + "icon": "assets/patchwork-key-bindings/icon.png", + "contact": { + "issues": "https://github.com/PatchworkMC/patchwork-api/issues", + "sources": "https://github.com/PatchworkMC/patchwork-api" + }, + "authors": [ + "PatchworkMC" + ], + "depends": { + "patchwork-api-base": "*" + }, + "mixins": [ + "patchwork-key-bindings.mixins.json" + ], + "description": "Implementation of the Forge Keybinding system.", + "custom": { + "modmenu:api": true, + "modmenu:parent": "patchwork" + } +} diff --git a/patchwork-key-bindings/src/main/resources/patchwork-key-bindings.mixins.json b/patchwork-key-bindings/src/main/resources/patchwork-key-bindings.mixins.json new file mode 100644 index 00000000..98f3d6ad --- /dev/null +++ b/patchwork-key-bindings/src/main/resources/patchwork-key-bindings.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin.keybindings", + "compatibilityLevel": "JAVA_8", + "client": [ + "MixinKeyBinding" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/patchwork-level-generators/build.gradle b/patchwork-level-generators/build.gradle index 597ee7b6..1f95fe0f 100644 --- a/patchwork-level-generators/build.gradle +++ b/patchwork-level-generators/build.gradle @@ -1,11 +1,12 @@ archivesBaseName = "patchwork-level-generators" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') - compile project(path: ':patchwork-events-world', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-events-world', configuration: 'dev') } -minecraft { +loom { accessWidener "src/main/resources/patchwork-level-generators.accesswidener" } diff --git a/patchwork-level-generators/src/main/resources/fabric.mod.json b/patchwork-level-generators/src/main/resources/fabric.mod.json index 01fc6bd6..018ec87d 100644 --- a/patchwork-level-generators/src/main/resources/fabric.mod.json +++ b/patchwork-level-generators/src/main/resources/fabric.mod.json @@ -13,7 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4" + "patchwork-api-base": "*" }, "description": "Implementation of the Forge World Type API.", "mixins": [ diff --git a/patchwork-loot/build.gradle b/patchwork-loot/build.gradle index 9029bd24..7b270e2c 100644 --- a/patchwork-loot/build.gradle +++ b/patchwork-loot/build.gradle @@ -1,2 +1,7 @@ archivesBaseName = "patchwork-loot" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') +} + diff --git a/patchwork-loot/src/main/java/net/minecraftforge/event/LootTableLoadEvent.java b/patchwork-loot/src/main/java/net/minecraftforge/event/LootTableLoadEvent.java new file mode 100644 index 00000000..93ecb6b8 --- /dev/null +++ b/patchwork-loot/src/main/java/net/minecraftforge/event/LootTableLoadEvent.java @@ -0,0 +1,67 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.event; + +import net.minecraftforge.eventbus.api.Event; + +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; +import net.minecraft.util.Identifier; + +/** + * Event fired when a LootTable json is loaded from json. + * This event is fired whenever resources are loaded, or when the server starts. + * This event will NOT be fired for LootTables loaded from the world folder, these are + * considered configurations files and should not be modified by mods. + * + *

Canceling the event will make it load a empty loot table.

+ */ +public class LootTableLoadEvent extends Event { + private final Identifier name; + private LootTable table; + private LootManager lootTableManager; + + public LootTableLoadEvent(Identifier name, LootTable table, LootManager lootTableManager) { + this.name = name; + this.table = table; + this.lootTableManager = lootTableManager; + } + + public Identifier getName() { + return this.name; + } + + public LootTable getTable() { + return this.table; + } + + public void setTable(LootTable table) { + this.table = table; + } + + public LootManager getLootTableManager() { + return this.lootTableManager; + } + + @Override + public boolean isCancelable() { + return true; + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootPool.java b/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootPool.java new file mode 100644 index 00000000..7aef5158 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootPool.java @@ -0,0 +1,42 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.api.loot; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTableRange; +import net.minecraft.loot.UniformLootTableRange; + +/** + * Interface for adding Forge methods added to LootPool and its inner classes. + */ +public interface ForgeLootPool { + // TODO: doesn't include methods having to do with freezing yet + + String getName(); + LootTableRange getRolls(); + LootTableRange getBonusRolls(); + void setRolls(UniformLootTableRange v); + void setBonusRolls(UniformLootTableRange v); + + public interface Builder { + LootPool.Builder name(String name); + LootPool.Builder bonusRolls(float min, float max); + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootTable.java b/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootTable.java new file mode 100644 index 00000000..86650bac --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/api/loot/ForgeLootTable.java @@ -0,0 +1,33 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.api.loot; + +import net.minecraft.loot.LootPool; + +/** + * Interface for adding Forge methods added to LootTable. + */ +public interface ForgeLootTable { + // TODO: doesn't include methods having to do with freezing yet + + LootPool getPool(String name); + LootPool removePool(String name); + void addPool(LootPool pool); +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/impl/event/loot/LootEvents.java b/patchwork-loot/src/main/java/net/patchworkmc/impl/event/loot/LootEvents.java new file mode 100644 index 00000000..c732f201 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/impl/event/loot/LootEvents.java @@ -0,0 +1,39 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.event.loot; + +import net.minecraftforge.event.LootTableLoadEvent; +import net.minecraftforge.common.MinecraftForge; + +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; +import net.minecraft.util.Identifier; + +public class LootEvents { + public static LootTable loadLootTable(Identifier name, LootTable table, LootManager manager) { + LootTableLoadEvent event = new LootTableLoadEvent(name, table, manager); + + if (MinecraftForge.EVENT_BUS.post(event)) { + return LootTable.EMPTY; + } + + return event.getTable(); + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java new file mode 100644 index 00000000..88a5d840 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/LootHooks.java @@ -0,0 +1,149 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.loot; + +import java.util.Deque; +import java.util.HashSet; + +import javax.annotation.Nullable; + +import com.google.common.collect.Queues; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; +import net.minecraft.util.Identifier; +import net.minecraft.util.JsonHelper; + +import net.patchworkmc.impl.event.loot.LootEvents; + +// NOTE: this class is more or less a direct copy of parts of Forge's ForgeHooks. +public class LootHooks { + // Made public for Patchwork's own use + public static ThreadLocal> lootContext = new ThreadLocal>(); + + public static LootTable loadLootTable(Gson gson, Identifier name, JsonElement data, boolean custom, LootManager lootTableManager) { + Deque que = lootContext.get(); + + if (que == null) { + que = Queues.newArrayDeque(); + lootContext.set(que); + } + + LootTable ret = null; + + try { + que.push(new LootTableContext(name, custom)); + ret = gson.fromJson(data, LootTable.class); + que.pop(); + } catch (JsonParseException e) { + que.pop(); + throw e; + } + + if (!custom) { + ret = LootEvents.loadLootTable(name, ret, lootTableManager); + } + + // if (ret != null) { + // ret.freeze(); + // } + + return ret; + } + + private static LootTableContext getLootTableContext() { + LootTableContext ctx = lootContext.get().peek(); + + if (ctx == null) { + throw new JsonParseException("Invalid call stack, could not grab json context!"); // Should I throw this? Do we care about custom deserializers outside the manager? + } + + return ctx; + } + + public static String readPoolName(JsonObject json) { + LootTableContext ctx = LootHooks.getLootTableContext(); + ctx.resetPoolCtx(); + + if (json.has("name")) { + return JsonHelper.getString(json, "name"); + } + + if (ctx.custom) { + return "custom#" + json.hashCode(); //We don't care about custom ones modders shouldn't be editing them! + } + + ctx.poolCount++; + + if (!ctx.vanilla) { + throw new JsonParseException("Loot Table \"" + ctx.name.toString() + "\" Missing `name` entry for pool #" + (ctx.poolCount - 1)); + } + + return ctx.poolCount == 1 ? "main" : "pool" + (ctx.poolCount - 1); + } + + // Made public for Patchwork's own use + public static class LootTableContext { + public final Identifier name; + public final boolean custom; + private final boolean vanilla; + public int poolCount = 0; + public int entryCount = 0; + private HashSet entryNames = Sets.newHashSet(); + + protected LootTableContext(Identifier name, boolean custom) { + this.name = name; + this.custom = custom; + this.vanilla = "minecraft".equals(this.name.getNamespace()); + } + + private void resetPoolCtx() { + this.entryCount = 0; + this.entryNames.clear(); + } + + public String validateEntryName(@Nullable String name) { + if (name != null && !this.entryNames.contains(name)) { + this.entryNames.add(name); + return name; + } + + if (!this.vanilla) { + throw new JsonParseException("Loot Table \"" + this.name.toString() + "\" Duplicate entry name \"" + name + "\" for pool #" + (this.poolCount - 1) + " entry #" + (this.entryCount - 1)); + } + + int x = 0; + + while (this.entryNames.contains(name + "#" + x)) { + x++; + } + + name = name + "#" + x; + this.entryNames.add(name); + + return name; + } + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/PatchworkLootPool.java b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/PatchworkLootPool.java new file mode 100644 index 00000000..68770139 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/impl/loot/PatchworkLootPool.java @@ -0,0 +1,28 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.loot; + +/** + * Forge does this through patching the constructor, we just add methods with + * mixins instead. + */ +public interface PatchworkLootPool { + void patchwork$setName(String name); +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinJsonDataLoader.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinJsonDataLoader.java new file mode 100644 index 00000000..85b3b420 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinJsonDataLoader.java @@ -0,0 +1,44 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import java.util.Map; + +import com.google.gson.JsonObject; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import net.minecraft.resource.JsonDataLoader; +import net.minecraft.resource.SinglePreparationResourceReloadListener; +import net.minecraft.util.Identifier; + +// TODO: Is there a better place to put this? +@Mixin(JsonDataLoader.class) +public abstract class MixinJsonDataLoader extends SinglePreparationResourceReloadListener> { + @Shadow + @Final + String dataType; + + // This does not get its own interface -- any mixins on subclasses wishing to use it should probably extend this mixin. + protected Identifier getPreparedPath(Identifier id) { + return new Identifier(id.getNamespace(), dataType + "/" + id.getPath() + ".json"); + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java new file mode 100644 index 00000000..4ae6cb97 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootManager.java @@ -0,0 +1,81 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import java.util.Map; +import java.util.function.BiConsumer; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import org.apache.logging.log4j.Logger; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import net.minecraft.loot.LootManager; +import net.minecraft.loot.LootTable; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; + +import net.patchworkmc.impl.loot.LootHooks; + +@Mixin(LootManager.class) +public abstract class MixinLootManager extends MixinJsonDataLoader { + @Shadow + @Final + private static Gson GSON; + + @Shadow + @Final + private static Logger LOGGER; + + @Redirect(method = "apply", at = @At(value = "INVOKE", target = "java/util/Map.forEach (Ljava/util/function/BiConsumer;)V", ordinal = 0)) + private void cancel_forEach(Map map, BiConsumer consumer) { + // ignore this call, we're gonna reintroduce it but with capturing locals + } + + @Inject(method = "apply", at = @At(value = "INVOKE", target = "java/util/Map.forEach (Ljava/util/function/BiConsumer;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD) + private void reintroduce_forEach(Map map, ResourceManager resourceManager, Profiler profiler, CallbackInfo info, ImmutableMap.Builder builder) { + map.forEach((id, jsonObject) -> { + try { + LootManager lootManager = (LootManager) (Object) this; + Resource res = resourceManager.getResource(this.getPreparedPath(id)); + LootTable lootTable = LootHooks.loadLootTable(GSON, id, jsonObject, res == null || !res.getResourcePackName().equals("Default"), lootManager); + builder.put(id, lootTable); + } catch (Exception ex) { + LOGGER.error("Couldn't parse loot table {}", id, ex); + } + }); + } + + @Overwrite + private static void method_20711(ImmutableMap.Builder builder, Identifier id, JsonObject obj) { + // We are effectively overwriting this lambda with our own, so let's make that explicit by actually overwriting it. + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java new file mode 100644 index 00000000..b538bfbf --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPool.java @@ -0,0 +1,82 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTableRange; +import net.minecraft.loot.UniformLootTableRange; + +import net.patchworkmc.api.loot.ForgeLootPool; +import net.patchworkmc.impl.loot.PatchworkLootPool; + +@Mixin(LootPool.class) +public class MixinLootPool implements PatchworkLootPool, ForgeLootPool { + // Forge has this as final, but I don't have a good way to initialize it if it is final. + @Unique + private String name; + + @Shadow + private UniformLootTableRange bonusRollsRange; + + @Shadow + private LootTableRange rollsRange; + + // implementation detail + // TODO: if we could have an inner class that was also a mixin, we could set this as protected? + @Override + public void patchwork$setName(String name) { + this.name = name; + } + + // Forge methods that should be added directly to the type + + // TODO: freezing stuff + + @Override + public String getName() { + return this.name; + } + + @Override + public LootTableRange getRolls() { + return rollsRange; + } + + @Override + public LootTableRange getBonusRolls() { + return this.bonusRollsRange; + } + + @Override + public void setRolls(UniformLootTableRange v) { + // checkFrozen(); + this.rollsRange = v; + } + + @Override + public void setBonusRolls(UniformLootTableRange v) { + // checkFrozen(); + this.bonusRollsRange = v; + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java new file mode 100644 index 00000000..d2d2d8fc --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolBuilder.java @@ -0,0 +1,60 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.UniformLootTableRange; + +import net.patchworkmc.api.loot.ForgeLootPool; +import net.patchworkmc.impl.loot.PatchworkLootPool; + +@Mixin(LootPool.Builder.class) +public abstract class MixinLootPoolBuilder implements ForgeLootPool.Builder { + @Unique + private String name; + + @Shadow + private UniformLootTableRange bonusRollsRange; + + @Inject(method = "build", at = @At("RETURN"), cancellable = true) + private void addNameToConstructor(CallbackInfoReturnable cir) { + LootPool ret = cir.getReturnValue(); + ((PatchworkLootPool) ret).patchwork$setName(name); + } + + @Override + public LootPool.Builder name(String name) { + this.name = name; + return (LootPool.Builder) (Object) this; + } + + @Override + public LootPool.Builder bonusRolls(float min, float max) { + this.bonusRollsRange = new UniformLootTableRange(min, max); + return (LootPool.Builder) (Object) this; + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java new file mode 100644 index 00000000..5f57ca15 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootPoolSerializer.java @@ -0,0 +1,64 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonElement; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; + +import net.minecraft.loot.LootPool; + +import net.patchworkmc.api.loot.ForgeLootPool; +import net.patchworkmc.impl.loot.LootHooks; +import net.patchworkmc.impl.loot.PatchworkLootPool; + +@Mixin(LootPool.Serializer.class) +public class MixinLootPoolSerializer { + @Inject(method = "deserialize", at = @At("RETURN"), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) + private void addNameToConstructor(JsonElement elem, Type ty, JsonDeserializationContext ctx, CallbackInfoReturnable cir, JsonObject obj) { + LootPool ret = cir.getReturnValue(); + ((PatchworkLootPool) ret).patchwork$setName(LootHooks.readPoolName(obj)); + + // is this necessary? + cir.setReturnValue(ret); + } + + @Redirect(method = "serialize", at = @At(value = "NEW", args = "class=com/google/gson/JsonObject")) + private static JsonObject serializeName(LootPool pool, Type type, JsonSerializationContext ctx) { + JsonObject ret = new JsonObject(); + + String name = ((ForgeLootPool) pool).getName(); + + if (name != null && !name.startsWith("custom#")) { + ret.add("name", ctx.serialize(name)); + } + + return ret; + } +} diff --git a/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java new file mode 100644 index 00000000..346b04c3 --- /dev/null +++ b/patchwork-loot/src/main/java/net/patchworkmc/mixin/loot/MixinLootTable.java @@ -0,0 +1,75 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.loot; + +import java.util.Arrays; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import net.minecraft.loot.LootPool; +import net.minecraft.loot.LootTable; + +import net.fabricmc.fabric.api.loot.v1.FabricLootSupplier; + +import net.patchworkmc.api.loot.ForgeLootTable; +import net.patchworkmc.api.loot.ForgeLootPool; + +@Mixin(LootTable.class) +public class MixinLootTable implements ForgeLootTable { + @Shadow + LootPool[] pools; + + // Forge added methods + + // TODO: freezing stuff + + @Override + public LootPool getPool(String name) { + return ((FabricLootSupplier) this).getPools().stream().filter(e -> name.equals(((ForgeLootPool) e).getName())).findFirst().orElse(null); + } + + @Override + public LootPool removePool(String name) { + // checkFrozen(); + for (int idx = 0; idx < pools.length; ++idx) { + LootPool pool = pools[idx]; + + if (name.equals(((ForgeLootPool) pool).getName())) { + // https://stackoverflow.com/a/644764 + System.arraycopy(pools, idx + 1, pools, idx, pools.length - 1 - idx); + return pool; + } + } + + return null; + } + + @Override + public void addPool(LootPool pool) { + // checkFrozen(); + if (((FabricLootSupplier) this).getPools().stream().anyMatch(e -> e == pool || ((ForgeLootPool) e).getName().equals(((ForgeLootPool) pool).getName()))) { + throw new RuntimeException("Attempted to add a duplicate pool to loot table: " + ((ForgeLootPool) pool).getName()); + } + + pools = Arrays.copyOf(pools, pools.length + 1); + pools[pools.length - 1] = pool; + } +} diff --git a/patchwork-loot/src/main/resources/assets/patchwork-loot/icon.png b/patchwork-loot/src/main/resources/assets/patchwork-loot/icon.png new file mode 100644 index 00000000..de75d2fb Binary files /dev/null and b/patchwork-loot/src/main/resources/assets/patchwork-loot/icon.png differ diff --git a/patchwork-loot/src/main/resources/fabric.mod.json b/patchwork-loot/src/main/resources/fabric.mod.json index 46a3e957..215c4733 100644 --- a/patchwork-loot/src/main/resources/fabric.mod.json +++ b/patchwork-loot/src/main/resources/fabric.mod.json @@ -13,7 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4" + "patchwork-api-base": "*" }, "mixins": [ "patchwork-loot.mixins.json" diff --git a/patchwork-loot/src/main/resources/patchwork-loot.mixins.json b/patchwork-loot/src/main/resources/patchwork-loot.mixins.json index 137951b0..4cedf63b 100644 --- a/patchwork-loot/src/main/resources/patchwork-loot.mixins.json +++ b/patchwork-loot/src/main/resources/patchwork-loot.mixins.json @@ -5,7 +5,13 @@ "mixins": [ "MixinAdvancementRewards", "MixinFishingBobberEntity", - "MixinLootContextTypes" + "MixinJsonDataLoader", + "MixinLootContextTypes", + "MixinLootManager", + "MixinLootPool", + "MixinLootPoolBuilder", + "MixinLootPoolSerializer", + "MixinLootTable" ], "injectors": { "defaultRequire": 1 diff --git a/patchwork-model-loader/build.gradle b/patchwork-model-loader/build.gradle new file mode 100644 index 00000000..77fa2553 --- /dev/null +++ b/patchwork-model-loader/build.gradle @@ -0,0 +1,7 @@ +archivesBaseName = "patchwork-model-loader" +version = getSubprojectVersion(project, "0.2.1") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-fml', configuration: 'dev') +} diff --git a/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelBakeEvent.java b/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelBakeEvent.java new file mode 100644 index 00000000..18ac9787 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelBakeEvent.java @@ -0,0 +1,59 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.event; + +import java.util.Map; + +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.eventbus.api.Event; + +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedModelManager; +import net.minecraft.util.Identifier; + +/** + * Fired when the BakedModelManager is notified of the resource manager reloading. + * Called after model registry is setup, but before it's passed to + * BlockModelShapes. + */ +public class ModelBakeEvent extends Event { + private final BakedModelManager modelManager; + private final Map modelRegistry; + private final ModelLoader modelLoader; + + public ModelBakeEvent(BakedModelManager modelManager, Map modelRegistry, + ModelLoader modelLoader) { + this.modelManager = modelManager; + this.modelRegistry = modelRegistry; + this.modelLoader = modelLoader; + } + + public BakedModelManager getModelManager() { + return modelManager; + } + + public Map getModelRegistry() { + return modelRegistry; + } + + public ModelLoader getModelLoader() { + return modelLoader; + } +} diff --git a/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelRegistryEvent.java b/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelRegistryEvent.java new file mode 100644 index 00000000..d31900d6 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/minecraftforge/client/event/ModelRegistryEvent.java @@ -0,0 +1,29 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.event; + +import net.minecraftforge.eventbus.api.Event; + +/** + * Fired when the {@link net.minecraftforge.client.model.ModelLoader} is ready + * to receive registrations. + */ +public class ModelRegistryEvent extends Event { +} diff --git a/patchwork-model-loader/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/patchwork-model-loader/src/main/java/net/minecraftforge/client/model/ModelLoader.java new file mode 100644 index 00000000..54dcafca --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -0,0 +1,109 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.model; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; + +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; + +import net.patchworkmc.impl.modelloader.SpecialModelProvider; + +public class ModelLoader extends net.minecraft.client.render.model.ModelLoader implements SpecialModelProvider { + private static final Marker MODELLOADING = MarkerManager.getMarker("MODELLOADING"); + private static Set specialModels = new HashSet<>(); + private static final Logger LOGGER = LogManager.getLogger(); + private final Map loadingExceptions = new HashMap<>(); + private boolean isLoading = false; + private static ModelLoader instance; + + @Nullable + public static ModelLoader instance() { + return instance; + } + + public boolean isLoading() { + return isLoading; + } + + public ModelLoader(ResourceManager resourceManager, SpriteAtlasTexture spriteAtlas, BlockColors blockColors, + Profiler profiler) { + super(resourceManager, spriteAtlas, blockColors, profiler); + } + + /** + * Indicate to vanilla that it should load and bake the given model, even if no + * blocks or items use it. This is useful if e.g. you have baked models only for + * entity renderers. Call during + * {@link net.minecraftforge.client.event.ModelRegistryEvent} + * + * @param rl The model, either {@link ModelResourceLocation} to point to a + * blockstate variant, or plain {@link ResourceLocation} to point + * directly to a json in the models folder. + */ + public static void addSpecialModel(Identifier rl) { + specialModels.add(rl); + } + + @Override + public Set getSpecialModels() { + return specialModels; + } + + /** + * Internal, do not use. + */ + public void onPostBakeEvent(Map modelRegistry) { + BakedModel missingModel = modelRegistry.get(MISSING); + + for (Map.Entry entry : loadingExceptions.entrySet()) { + // ignoring pure Identifier arguments, all things we care about pass + // ModelIdentifier + if (entry.getKey() instanceof ModelIdentifier) { + LOGGER.debug(MODELLOADING, "Model {} failed to load: {}", entry.getKey().toString(), + entry.getValue().getLocalizedMessage()); + final ModelIdentifier location = (ModelIdentifier) entry.getKey(); + final BakedModel model = modelRegistry.get(location); + + if (model == null) { + modelRegistry.put(location, missingModel); + } + } + } + + loadingExceptions.clear(); + isLoading = false; + } +} diff --git a/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/ModelEventDispatcher.java b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/ModelEventDispatcher.java new file mode 100644 index 00000000..1afc38d1 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/ModelEventDispatcher.java @@ -0,0 +1,37 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.modelloader; + +import java.util.Map; + +import net.minecraftforge.client.event.ModelBakeEvent; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.fml.ModLoader; + +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedModelManager; +import net.minecraft.util.Identifier; + +public class ModelEventDispatcher { + public static void onModelBake(BakedModelManager modelManager, Map modelRegistry, ModelLoader modelLoader) { + ModLoader.get().postEvent(new ModelBakeEvent(modelManager, modelRegistry, modelLoader)); + modelLoader.onPostBakeEvent(modelRegistry); + } +} diff --git a/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/Signatures.java b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/Signatures.java new file mode 100644 index 00000000..735b2b3a --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/Signatures.java @@ -0,0 +1,34 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.modelloader; + +public class Signatures { + public static final String Profiler_swap = "net/minecraft/util/profiler/Profiler.swap(Ljava/lang/String;)V"; + + public static final String ModelLoader_new = "(" + + "Lnet/minecraft/resource/ResourceManager;" + + "Lnet/minecraft/client/texture/SpriteAtlasTexture;" + + "Lnet/minecraft/client/color/block/BlockColors;" + + "Lnet/minecraft/util/profiler/Profiler;" + + ")" + + "Lnet/minecraft/client/render/model/ModelLoader;"; + + public static final String ModelLoader_addModel = "net/minecraft/client/render/model/ModelLoader.addModel(Lnet/minecraft/client/util/ModelIdentifier;)V"; +} diff --git a/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/SpecialModelProvider.java b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/SpecialModelProvider.java new file mode 100644 index 00000000..e39b5a46 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/patchworkmc/impl/modelloader/SpecialModelProvider.java @@ -0,0 +1,30 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.modelloader; + +import java.util.Set; + +import net.minecraft.util.Identifier; + +public interface SpecialModelProvider { + default Set getSpecialModels() { + return java.util.Collections.emptySet(); + } +} diff --git a/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinBakedModelManager.java b/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinBakedModelManager.java new file mode 100644 index 00000000..e2169543 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinBakedModelManager.java @@ -0,0 +1,59 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.modelloader; + +import java.util.Map; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.color.block.BlockColors; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.BakedModelManager; +import net.minecraft.client.render.model.ModelLoader; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.profiler.Profiler; + +import net.patchworkmc.impl.modelloader.ModelEventDispatcher; +import net.patchworkmc.impl.modelloader.Signatures; + +@Mixin(BakedModelManager.class) +public abstract class MixinBakedModelManager { + @Shadow + private Map models; + + @Redirect(method = "prepare", at = @At(value = "NEW", target = Signatures.ModelLoader_new, ordinal = 0)) + private ModelLoader patchwork_prepare_new_ModelLoader(ResourceManager resourceManager, SpriteAtlasTexture spriteAtlas, BlockColors blockColors, Profiler profiler) { + return new net.minecraftforge.client.model.ModelLoader(resourceManager, spriteAtlas, blockColors, profiler); + } + + @Inject(method = "apply", at = @At(shift = Shift.BEFORE, value = "INVOKE", target = Signatures.Profiler_swap, ordinal = 0)) + protected void patchwork_apply_swap(ModelLoader modelLoader, ResourceManager resourceManager, Profiler profiler, CallbackInfo ci) { + BakedModelManager me = (BakedModelManager) (Object) this; + ModelEventDispatcher.onModelBake(me, models, (net.minecraftforge.client.model.ModelLoader) modelLoader); + } +} diff --git a/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinModelLoader.java b/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinModelLoader.java new file mode 100644 index 00000000..fc082222 --- /dev/null +++ b/patchwork-model-loader/src/main/java/net/patchworkmc/mixin/modelloader/MixinModelLoader.java @@ -0,0 +1,89 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.modelloader; + +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; + +import net.minecraft.client.render.model.ModelLoader; +import net.minecraft.client.render.model.UnbakedModel; +import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.util.Identifier; + +import net.patchworkmc.impl.modelloader.Signatures; +import net.patchworkmc.impl.modelloader.SpecialModelProvider; + +@Mixin(ModelLoader.class) +public abstract class MixinModelLoader implements SpecialModelProvider { + @Unique + private static final ModelIdentifier TRIDENT_INV = new ModelIdentifier("minecraft:trident_in_hand#inventory"); + @Unique + private static final Logger LOGGER = LogManager.getLogger(ModelLoader.class); + + @Shadow + @Final + private Map modelsToBake; + + @Shadow + @Final + private Map unbakedModels; + + @Unique + private void patchwork$loadSpecialModel() { + for (Identifier id : getSpecialModels()) { + ModelLoader me = (ModelLoader) (Object) this; + UnbakedModel iunbakedmodel = me.getOrLoadModel(id); + this.unbakedModels.put(id, iunbakedmodel); + this.modelsToBake.put(id, iunbakedmodel); + } + } + + @Shadow + private void addModel(ModelIdentifier modelId) { } + + /** + * Due to the limitations of mixin, when targeting a constructor, we cannot use injection points other than "TAIL". + * There are multiple occurrences of addModel in the constructor, Forge inserts the patch after adding model for the trident. + * Here we just do another check to ensure that the injection point is correct. + * @param me + * @param modelId + */ + @Redirect(slice = @Slice(from = @At(value = "INVOKE_STRING", target = Signatures.Profiler_swap, args = "ldc=special")), + method = "", at = @At(value = "INVOKE", target = Signatures.ModelLoader_addModel, ordinal = 0)) + private void patchwork_addModel_return(ModelLoader me, ModelIdentifier modelId) { + addModel(modelId); + + if (modelId.equals(TRIDENT_INV)) { + LOGGER.debug("Patchwork is loading special models for Forge mods"); + patchwork$loadSpecialModel(); + } else { + LOGGER.warn("Patchwork was unable to load special models for Forge mods"); + } + } +} diff --git a/patchwork-model-loader/src/main/resources/assets/patchwork-model-loader/icon.png b/patchwork-model-loader/src/main/resources/assets/patchwork-model-loader/icon.png new file mode 100644 index 00000000..de75d2fb Binary files /dev/null and b/patchwork-model-loader/src/main/resources/assets/patchwork-model-loader/icon.png differ diff --git a/patchwork-model-loader/src/main/resources/fabric.mod.json b/patchwork-model-loader/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..87194392 --- /dev/null +++ b/patchwork-model-loader/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "patchwork-model-loader", + "name": "Patchwork Model Loader", + "version": "${version}", + "license": "LGPL-2.1-only", + "icon": "assets/patchwork-model-loader/icon.png", + "contact": { + "issues": "https://github.com/PatchworkMC/patchwork-api/issues", + "sources": "https://github.com/PatchworkMC/patchwork-api" + }, + "authors": [ + "PatchworkMC" + ], + "depends": { + "patchwork-api-base": "*", + "patchwork-fml": "*" + }, + "mixins": [ + "patchwork-model-loader.mixins.json" + ], + "description": "Implementation of the Forge Model Loader.", + "custom": { + "modmenu:api": true, + "modmenu:parent": "patchwork" + } +} diff --git a/patchwork-model-loader/src/main/resources/patchwork-model-loader.mixins.json b/patchwork-model-loader/src/main/resources/patchwork-model-loader.mixins.json new file mode 100644 index 00000000..9bd996d7 --- /dev/null +++ b/patchwork-model-loader/src/main/resources/patchwork-model-loader.mixins.json @@ -0,0 +1,12 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin.modelloader", + "compatibilityLevel": "JAVA_8", + "client": [ + "MixinBakedModelManager", + "MixinModelLoader" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/patchwork-networking-messages/build.gradle b/patchwork-networking-messages/build.gradle index 9fad475e..e1019e69 100644 --- a/patchwork-networking-messages/build.gradle +++ b/patchwork-networking-messages/build.gradle @@ -1,7 +1,7 @@ archivesBaseName = "patchwork-networking-messages" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.1") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') - compile project(path: ':patchwork-networking', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-networking', configuration: 'dev') } diff --git a/patchwork-networking-messages/src/main/java/net/patchworkmc/impl/networking/PatchworkPlayNetworkingMessages.java b/patchwork-networking-messages/src/main/java/net/patchworkmc/impl/networking/PatchworkPlayNetworkingMessages.java index d1b0ae9a..1db5de7a 100644 --- a/patchwork-networking-messages/src/main/java/net/patchworkmc/impl/networking/PatchworkPlayNetworkingMessages.java +++ b/patchwork-networking-messages/src/main/java/net/patchworkmc/impl/networking/PatchworkPlayNetworkingMessages.java @@ -37,36 +37,20 @@ import net.minecraft.util.PacketByteBuf; import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.network.ClientSidePacketRegistry; import net.fabricmc.fabric.api.network.ServerSidePacketRegistry; public class PatchworkPlayNetworkingMessages implements ModInitializer, MessageFactory { - private static final Logger LOGGER = LogManager.getLogger("patchwork-networking"); - private static final Identifier IDENTIFIER = new Identifier("fml", "play"); - private static final NetworkChannelVersion VERSION = new NetworkChannelVersion("FML2", version -> true, version -> true); - private static final short SPAWN_ENTITY = 0; - private static final short OPEN_CONTAINER = 1; + public static final Logger LOGGER = LogManager.getLogger("patchwork-networking"); + public static final Identifier IDENTIFIER = new Identifier("fml", "play"); + public static final NetworkChannelVersion VERSION = new NetworkChannelVersion("FML2", version -> true, version -> true); + public static final short SPAWN_ENTITY = 0; + public static final short OPEN_CONTAINER = 1; @Override public void onInitialize() { PatchworkNetworking.getVersionManager().createChannel(IDENTIFIER, VERSION); PatchworkNetworking.setFactory(this); - // TODO: Move to client initializer - ClientSidePacketRegistry.INSTANCE.register(IDENTIFIER, (context, buf) -> { - int id = buf.readUnsignedByte(); - - if (id == SPAWN_ENTITY) { - FMLPlayMessages.SpawnEntity spawn = FMLPlayMessages.SpawnEntity.decode(buf); - FMLPlayMessages.SpawnEntity.handle(spawn, context); - } else if (id == OPEN_CONTAINER) { - FMLPlayMessages.OpenContainer open = FMLPlayMessages.OpenContainer.decode(buf); - FMLPlayMessages.OpenContainer.handle(open, context); - } else { - LOGGER.warn("Received an unknown fml:play message with an id of {} and a payload of {} bytes", id, buf.readableBytes()); - } - }); - ServerSidePacketRegistry.INSTANCE.register(IDENTIFIER, (context, buf) -> { LOGGER.warn("Received an fml:play on the server, this should not happen! Kicking the offending client."); diff --git a/patchwork-networking-messages/src/main/java/net/patchworkmc/impl/networking/PatchworkPlayNetworkingMessagesClient.java b/patchwork-networking-messages/src/main/java/net/patchworkmc/impl/networking/PatchworkPlayNetworkingMessagesClient.java new file mode 100644 index 00000000..1c5a5825 --- /dev/null +++ b/patchwork-networking-messages/src/main/java/net/patchworkmc/impl/networking/PatchworkPlayNetworkingMessagesClient.java @@ -0,0 +1,45 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.networking; + +import net.minecraftforge.fml.network.FMLPlayMessages; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.network.ClientSidePacketRegistry; + +public class PatchworkPlayNetworkingMessagesClient implements ClientModInitializer { + @Override + public void onInitializeClient() { + // TODO: Move to client initializer + ClientSidePacketRegistry.INSTANCE.register(PatchworkPlayNetworkingMessages.IDENTIFIER, (context, buf) -> { + int id = buf.readUnsignedByte(); + + if (id == PatchworkPlayNetworkingMessages.SPAWN_ENTITY) { + FMLPlayMessages.SpawnEntity spawn = FMLPlayMessages.SpawnEntity.decode(buf); + FMLPlayMessages.SpawnEntity.handle(spawn, context); + } else if (id == PatchworkPlayNetworkingMessages.OPEN_CONTAINER) { + FMLPlayMessages.OpenContainer open = FMLPlayMessages.OpenContainer.decode(buf); + FMLPlayMessages.OpenContainer.handle(open, context); + } else { + PatchworkPlayNetworkingMessages.LOGGER.warn("Received an unknown fml:play message with an id of {} and a payload of {} bytes", id, buf.readableBytes()); + } + }); + } +} diff --git a/patchwork-networking-messages/src/main/java/net/patchworkmc/mixin/networking/MixinServerPlayerEntity.java b/patchwork-networking-messages/src/main/java/net/patchworkmc/mixin/networking/MixinServerPlayerEntity.java index 55c8ec24..f43fcc2d 100644 --- a/patchwork-networking-messages/src/main/java/net/patchworkmc/mixin/networking/MixinServerPlayerEntity.java +++ b/patchwork-networking-messages/src/main/java/net/patchworkmc/mixin/networking/MixinServerPlayerEntity.java @@ -20,29 +20,23 @@ package net.patchworkmc.mixin.networking; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; -import org.spongepowered.asm.mixin.gen.Invoker; +import org.spongepowered.asm.mixin.Shadow; import net.minecraft.server.network.ServerPlayerEntity; import net.patchworkmc.impl.networking.ContainerSyncAccess; @Mixin(ServerPlayerEntity.class) -public class MixinServerPlayerEntity implements ContainerSyncAccess { - @Accessor - private int getContainerSyncId() { - throw new AssertionError("Mixin not applied"); - } - - @Invoker - private void invokeIncrementContainerSyncId() { - throw new AssertionError("Mixin not applied"); - } +public abstract class MixinServerPlayerEntity implements ContainerSyncAccess { + @Shadow + private int containerSyncId; + @Shadow + protected abstract void incrementContainerSyncId(); @Override public int patchwork$getNewContainerSyncId() { - invokeIncrementContainerSyncId(); + incrementContainerSyncId(); - return getContainerSyncId(); + return containerSyncId; } } diff --git a/patchwork-networking-messages/src/main/resources/assets/patchwork-networking-messages/icon.png b/patchwork-networking-messages/src/main/resources/assets/patchwork-networking-messages/icon.png new file mode 100644 index 00000000..de75d2fb Binary files /dev/null and b/patchwork-networking-messages/src/main/resources/assets/patchwork-networking-messages/icon.png differ diff --git a/patchwork-networking-messages/src/main/resources/fabric.mod.json b/patchwork-networking-messages/src/main/resources/fabric.mod.json index 6552e4b2..45f3390f 100644 --- a/patchwork-networking-messages/src/main/resources/fabric.mod.json +++ b/patchwork-networking-messages/src/main/resources/fabric.mod.json @@ -13,14 +13,15 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", - "fabric-networking-v0": "*", - "patchwork-fml": "*", + "patchwork-api-base": "*", "patchwork-networking": "*" }, "entrypoints": { "main": [ "net.patchworkmc.impl.networking.PatchworkPlayNetworkingMessages" + ], + "client": [ + "net.patchworkmc.impl.networking.PatchworkPlayNetworkingMessagesClient" ] }, "mixins": [ diff --git a/patchwork-networking-messages/src/main/resources/patchwork-networking-messages.mixins.json b/patchwork-networking-messages/src/main/resources/patchwork-networking-messages.mixins.json index 0cd067e8..37fda991 100644 --- a/patchwork-networking-messages/src/main/resources/patchwork-networking-messages.mixins.json +++ b/patchwork-networking-messages/src/main/resources/patchwork-networking-messages.mixins.json @@ -4,7 +4,8 @@ "compatibilityLevel": "JAVA_8", "mixins": [ "MixinEntityType", - "MixinEntityTypeBuilder" + "MixinEntityTypeBuilder", + "MixinServerPlayerEntity" ], "injectors": { "defaultRequire": 1 diff --git a/patchwork-networking/build.gradle b/patchwork-networking/build.gradle index fa90b140..8598ae7f 100644 --- a/patchwork-networking/build.gradle +++ b/patchwork-networking/build.gradle @@ -1,6 +1,7 @@ archivesBaseName = "patchwork-networking" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.4.0") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-fml', configuration: 'dev') } diff --git a/patchwork-networking/src/main/java/net/minecraftforge/client/event/ClientPlayerNetworkEvent.java b/patchwork-networking/src/main/java/net/minecraftforge/client/event/ClientPlayerNetworkEvent.java new file mode 100644 index 00000000..a6c90336 --- /dev/null +++ b/patchwork-networking/src/main/java/net/minecraftforge/client/event/ClientPlayerNetworkEvent.java @@ -0,0 +1,104 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.client.event; + +import net.minecraftforge.eventbus.api.Event; + +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.network.ClientConnection; + +/** + * Client-side events fired on changes to player connectivity. + */ +public class ClientPlayerNetworkEvent extends Event { + private final ClientPlayerInteractionManager playerInteractionManager; + private final ClientPlayerEntity player; + private final ClientConnection clientConnection; + + ClientPlayerNetworkEvent(final ClientPlayerInteractionManager interactionManager, final ClientPlayerEntity player, final ClientConnection clientConnection) { + this.playerInteractionManager = interactionManager; + this.player = player; + this.clientConnection = clientConnection; + } + + /** + * @return The ClientPlayerInteractionManager instance for the client side. + */ + public ClientPlayerInteractionManager getController() { + return this.playerInteractionManager; + } + + /** + * @return The player instance, if present (otherwise, returns null). + */ + public ClientPlayerEntity getPlayer() { + return this.player; + } + + /** + * @return The network connection, if present (otherwise, returns null). + */ + public ClientConnection getNetworkManager() { + return this.clientConnection; + } + + /** + * Fired when the player logs out. + * + *

Note: This might also be fired when a new integrated server is being created.

+ */ + public static class LoggedInEvent extends ClientPlayerNetworkEvent { + public LoggedInEvent(final ClientPlayerInteractionManager interactionManager, final ClientPlayerEntity player, final ClientConnection clientConnection) { + super(interactionManager, player, clientConnection); + } + } + + /** + * Fired when the player logs out. + * + *

Note: This might also fire when a new integrated server is being created.

+ */ + public static class LoggedOutEvent extends ClientPlayerNetworkEvent { + public LoggedOutEvent(final ClientPlayerInteractionManager interactionManager, final ClientPlayerEntity player, final ClientConnection clientConnection) { + super(interactionManager, player, clientConnection); + } + } + + /** + * Fired when the player object respawns, such as dimension changes. + */ + public static class RespawnEvent extends ClientPlayerNetworkEvent { + private final ClientPlayerEntity oldPlayer; + + public RespawnEvent(final ClientPlayerInteractionManager interactionManager, final ClientPlayerEntity oldPlayer, final ClientPlayerEntity newPlayer, final ClientConnection clientConnection) { + super(interactionManager, newPlayer, clientConnection); + this.oldPlayer = oldPlayer; + } + + public ClientPlayerEntity getOldPlayer() { + return this.oldPlayer; + } + + public ClientPlayerEntity getNewPlayer() { + return super.getPlayer(); + } + } +} diff --git a/patchwork-networking/src/main/java/net/patchworkmc/impl/networking/ClientNetworkingEvents.java b/patchwork-networking/src/main/java/net/patchworkmc/impl/networking/ClientNetworkingEvents.java new file mode 100644 index 00000000..2545045a --- /dev/null +++ b/patchwork-networking/src/main/java/net/patchworkmc/impl/networking/ClientNetworkingEvents.java @@ -0,0 +1,41 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.impl.networking; + +import net.minecraftforge.client.event.ClientPlayerNetworkEvent; +import net.minecraftforge.common.MinecraftForge; + +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.network.ClientConnection; + +public class ClientNetworkingEvents { + public static void firePlayerLogin(final ClientPlayerInteractionManager interactionManager, final ClientPlayerEntity player, final ClientConnection clientConnection) { + MinecraftForge.EVENT_BUS.post(new ClientPlayerNetworkEvent.LoggedInEvent(interactionManager, player, clientConnection)); + } + + public static void firePlayerLogout(final ClientPlayerInteractionManager interactionManager, final ClientPlayerEntity player) { + MinecraftForge.EVENT_BUS.post(new ClientPlayerNetworkEvent.LoggedOutEvent(interactionManager, player, player != null ? player.networkHandler != null ? player.networkHandler.getConnection() : null : null)); + } + + public static void firePlayerRespawn(final ClientPlayerInteractionManager interactionManager, final ClientPlayerEntity oldPlayer, final ClientPlayerEntity newPlayer, final ClientConnection clientConnection) { + MinecraftForge.EVENT_BUS.post(new ClientPlayerNetworkEvent.RespawnEvent(interactionManager, oldPlayer, newPlayer, clientConnection)); + } +} diff --git a/patchwork-networking/src/main/java/net/patchworkmc/mixin/networking/client/MixinMinecraftClient.java b/patchwork-networking/src/main/java/net/patchworkmc/mixin/networking/client/MixinMinecraftClient.java new file mode 100644 index 00000000..0a541d38 --- /dev/null +++ b/patchwork-networking/src/main/java/net/patchworkmc/mixin/networking/client/MixinMinecraftClient.java @@ -0,0 +1,47 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.patchworkmc.mixin.networking.client; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerInteractionManager; + +import net.patchworkmc.impl.networking.ClientNetworkingEvents; + +@Mixin(MinecraftClient.class) +public class MixinMinecraftClient { + @Shadow + public ClientPlayerInteractionManager interactionManager; + + @Shadow + public ClientPlayerEntity player; + + @Inject(method = "disconnect(Lnet/minecraft/client/gui/screen/Screen;)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;reset()V", shift = At.Shift.AFTER)) + public void patchwork$hookDisconnect(CallbackInfo ci) { + ClientNetworkingEvents.firePlayerLogout(this.interactionManager, this.player); + } +} diff --git a/patchwork-networking/src/main/java/net/patchworkmc/mixin/networking/handler/MixinClientPlayNetworkHandler.java b/patchwork-networking/src/main/java/net/patchworkmc/mixin/networking/handler/MixinClientPlayNetworkHandler.java index 812e17a5..4fcd7104 100644 --- a/patchwork-networking/src/main/java/net/patchworkmc/mixin/networking/handler/MixinClientPlayNetworkHandler.java +++ b/patchwork-networking/src/main/java/net/patchworkmc/mixin/networking/handler/MixinClientPlayNetworkHandler.java @@ -26,13 +26,23 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.network.ClientConnection; import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket; +import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket; +import net.minecraft.world.dimension.DimensionType; + +import net.patchworkmc.impl.networking.ClientNetworkingEvents; @Mixin(ClientPlayNetworkHandler.class) public abstract class MixinClientPlayNetworkHandler { + @Shadow + private MinecraftClient client; + @Shadow public abstract ClientConnection getConnection(); @@ -42,4 +52,16 @@ public abstract class MixinClientPlayNetworkHandler { callback.cancel(); } } + + @Inject(method = "onGameJoin", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/GameJoinS2CPacket;getEntityId()I")) + public void patchwork$hookGameJoin(CallbackInfo ci) { + ClientNetworkingEvents.firePlayerLogin(this.client.interactionManager, this.client.player, this.client.getNetworkHandler().getConnection()); + } + + @Inject(method = "onPlayerRespawn", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;addPlayer(ILnet/minecraft/client/network/AbstractClientPlayerEntity;)V"), + locals = LocalCapture.CAPTURE_FAILHARD) + public void patchwork$hookPlayerRespawn(PlayerRespawnS2CPacket packet, CallbackInfo ci, DimensionType dimensionType, ClientPlayerEntity clientPlayerEntity, int i, String string, ClientPlayerEntity clientPlayerEntity2) { + ClientNetworkingEvents.firePlayerRespawn(this.client.interactionManager, clientPlayerEntity, clientPlayerEntity2, clientPlayerEntity2.networkHandler.getConnection()); + } } diff --git a/patchwork-networking/src/main/resources/fabric.mod.json b/patchwork-networking/src/main/resources/fabric.mod.json index d27421ae..f5a3fbdc 100644 --- a/patchwork-networking/src/main/resources/fabric.mod.json +++ b/patchwork-networking/src/main/resources/fabric.mod.json @@ -13,12 +13,12 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", - "fabric-networking-v0": "*", + "patchwork-api-base": "*", "patchwork-fml": "*" }, "mixins": [ "patchwork-networking.accessor.mixins.json", + "patchwork-networking.client.mixins.json", "patchwork-networking.handler.mixins.json", "patchwork-networking.packet.mixins.json" ], diff --git a/patchwork-networking/src/main/resources/patchwork-networking.client.mixins.json b/patchwork-networking/src/main/resources/patchwork-networking.client.mixins.json new file mode 100644 index 00000000..3eb9d42c --- /dev/null +++ b/patchwork-networking/src/main/resources/patchwork-networking.client.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "net.patchworkmc.mixin.networking.client", + "compatibilityLevel": "JAVA_8", + "client": [ + "MixinMinecraftClient" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/patchwork-recipes/build.gradle b/patchwork-recipes/build.gradle index f13ee8e0..ad6c0ed0 100644 --- a/patchwork-recipes/build.gradle +++ b/patchwork-recipes/build.gradle @@ -1,2 +1,6 @@ archivesBaseName = "patchwork-recipes" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') +} diff --git a/patchwork-recipes/src/main/resources/fabric.mod.json b/patchwork-recipes/src/main/resources/fabric.mod.json index 329f85ac..176a8769 100644 --- a/patchwork-recipes/src/main/resources/fabric.mod.json +++ b/patchwork-recipes/src/main/resources/fabric.mod.json @@ -11,7 +11,7 @@ "icon": "assets/patchwork-recipes/icon.png", "environment": "*", "depends": { - "fabricloader": ">=0.8.4", + "patchwork-api-base": "*", "fabric": "*" }, "mixins": [ diff --git a/patchwork-registries/build.gradle b/patchwork-registries/build.gradle index 2d6ea957..9f39bae5 100644 --- a/patchwork-registries/build.gradle +++ b/patchwork-registries/build.gradle @@ -1,6 +1,11 @@ archivesBaseName = "patchwork-registries" -version = getSubprojectVersion(project, "0.3.0") +version = getSubprojectVersion(project, "0.4.0") dependencies { - compile project(path: ':patchwork-fml', configuration: 'dev') + implementation project(path: ':patchwork-api-base', configuration: 'dev') + implementation project(path: ':patchwork-fml', configuration: 'dev') +} + +loom { + accessWidener "src/main/resources/patchwork-registries.accesswidener" } diff --git a/patchwork-registries/src/main/java/net/minecraftforge/fml/RegistryObject.java b/patchwork-registries/src/main/java/net/minecraftforge/fml/RegistryObject.java new file mode 100644 index 00000000..9024333b --- /dev/null +++ b/patchwork-registries/src/main/java/net/minecraftforge/fml/RegistryObject.java @@ -0,0 +1,296 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fml; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import javax.annotation.Nullable; + +import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.IForgeRegistryEntry; +import net.minecraftforge.registries.RegistryManager; + +import net.minecraft.util.Identifier; + +import net.patchworkmc.api.registries.ObjectHolderRegistry; + +public final class RegistryObject> implements Supplier { + private static RegistryObject EMPTY = new RegistryObject<>(); + private final Identifier name; + @Nullable + private T value; + + private RegistryObject() { + this.name = null; + } + + private > RegistryObject(Identifier name, Supplier> registryType) { + this(name, RegistryManager.ACTIVE.getRegistry(registryType.get())); + } + + @SuppressWarnings("unchecked") + private > RegistryObject(Identifier name, IForgeRegistry registry) { + if (registry == null) { + throw new IllegalArgumentException("Invalid registry argument, must not be null"); + } + + this.name = name; + ObjectHolderRegistry.INSTANCE.register(registry.getRegistrySuperType(), name.getNamespace(), name.getPath(), value -> { + this.value = (T) value; + }); + } + + @Deprecated + public static , U extends T> RegistryObject of(final String name, Supplier> registryType) { + return of(new Identifier(name), registryType); + } + + public static , U extends T> RegistryObject of(final Identifier name, Supplier> registryType) { + return new RegistryObject<>(name, registryType); + } + + @Deprecated + public static , U extends T> RegistryObject of(final String name, IForgeRegistry registry) { + return of(new Identifier(name), registry); + } + + public static , U extends T> RegistryObject of(final Identifier name, IForgeRegistry registry) { + return new RegistryObject<>(name, registry); + } + + private static > RegistryObject empty() { + @SuppressWarnings("unchecked") + RegistryObject t = (RegistryObject) EMPTY; + return t; + } + + /** + * Directly retrieves the wrapped Registry Object. This value will automatically be updated when the backing registry is updated. + */ + @Nullable + @Override + public T get() { + return this.value; + } + + public void updateReference(IForgeRegistry registry) { + this.value = registry.getValue(getId()); + } + + public Identifier getId() { + return this.name; + } + + /** + * @deprecated Prefer {@link #getId()} + */ + @Deprecated + public String getName() { + return getId().toString(); + } + + public Stream stream() { + return isPresent() ? Stream.of(get()) : Stream.of(); + } + + /** + * Return {@code true} if there is a mod object present, otherwise {@code false}. + * + * @return {@code true} if there is a mod object present, otherwise {@code false} + */ + public boolean isPresent() { + return get() != null; + } + + /** + * If a mod object is present, invoke the specified consumer with the object, + * otherwise do nothing. + * + * @param consumer block to be executed if a mod object is present + * @throws NullPointerException if mod object is present and {@code consumer} is + * null + */ + public void ifPresent(Consumer consumer) { + if (get() != null) { + consumer.accept(get()); + } + } + + /** + * If a mod object is present, and the mod object matches the given predicate, + * return an {@code RegistryObject} describing the value, otherwise return an + * empty {@code RegistryObject}. + * + * @param predicate a predicate to apply to the mod object, if present + * @return an {@code RegistryObject} describing the value of this {@code RegistryObject} + * if a mod object is present and the mod object matches the given predicate, + * otherwise an empty {@code RegistryObject} + * @throws NullPointerException if the predicate is null + */ + public RegistryObject filter(Predicate predicate) { + Objects.requireNonNull(predicate); + + if (!isPresent()) { + return this; + } else { + return predicate.test(get()) ? this : empty(); + } + } + + /** + * If a mod object is present, apply the provided mapping function to it, + * and if the result is non-null, return an {@code Optional} describing the + * result. Otherwise return an empty {@code Optional}. + * + * @param The type of the result of the mapping function + * @param mapper a mapping function to apply to the mod object, if present + * @return an {@code Optional} describing the result of applying a mapping + * function to the mod object of this {@code RegistryObject}, if a mod object is present, + * otherwise an empty {@code Optional} + * @throws NullPointerException if the mapping function is null + * @apiNote This method supports post-processing on optional values, without + * the need to explicitly check for a return status. + */ + public Optional map(Function mapper) { + Objects.requireNonNull(mapper); + + if (!isPresent()) { + return Optional.empty(); + } else { + return Optional.ofNullable(mapper.apply(get())); + } + } + + /** + * If a value is present, apply the provided {@code Optional}-bearing + * mapping function to it, return that result, otherwise return an empty + * {@code Optional}. This method is similar to {@link #map(Function)}, + * but the provided mapper is one whose result is already an {@code Optional}, + * and if invoked, {@code flatMap} does not wrap it with an additional + * {@code Optional}. + * + * @param The type parameter to the {@code Optional} returned by + * @param mapper a mapping function to apply to the mod object, if present + * the mapping function + * @return the result of applying an {@code Optional}-bearing mapping + * function to the value of this {@code Optional}, if a value is present, + * otherwise an empty {@code Optional} + * @throws NullPointerException if the mapping function is null or returns + * a null result + */ + public Optional flatMap(Function> mapper) { + Objects.requireNonNull(mapper); + + if (!isPresent()) { + return Optional.empty(); + } else { + return Objects.requireNonNull(mapper.apply(get())); + } + } + + /** + * If a mod object is present, lazily apply the provided mapping function to it, + * returning a supplier for the transformed result. If this object is empty, or the + * mapping function returns {@code null}, the supplier will return {@code null}. + * + * @param The type of the result of the mapping function + * @param mapper A mapping function to apply to the mod object, if present + * @return A {@code Supplier} lazily providing the result of applying a mapping + * function to the mod object of this {@code RegistryObject}, if a mod object is present, + * otherwise a supplier returning {@code null} + * @throws NullPointerException if the mapping function is {@code null} + * @apiNote This method supports post-processing on optional values, without + * the need to explicitly check for a return status. + */ + public Supplier lazyMap(Function mapper) { + Objects.requireNonNull(mapper); + return () -> isPresent() ? mapper.apply(get()) : null; + } + + /** + * Return the mod object if present, otherwise return {@code other}. + * + * @param other the mod object to be returned if there is no mod object present, may + * be null + * @return the mod object, if present, otherwise {@code other} + */ + public T orElse(T other) { + return isPresent() ? get() : other; + } + + /** + * Return the mod object if present, otherwise invoke {@code other} and return + * the result of that invocation. + * + * @param other a {@code Supplier} whose result is returned if no mod object + * is present + * @return the mod object if present otherwise the result of {@code other.get()} + * @throws NullPointerException if mod object is not present and {@code other} is + * null + */ + public T orElseGet(Supplier other) { + return isPresent() ? get() : other.get(); + } + + /** + * Return the contained mod object, if present, otherwise throw an exception + * to be created by the provided supplier. + * + * @param Type of the exception to be thrown + * @param exceptionSupplier The supplier which will return the exception to + * be thrown + * @return the present mod object + * @throws X if there is no mod object present + * @throws NullPointerException if no mod object is present and + * {@code exceptionSupplier} is null + * @apiNote A method reference to the exception constructor with an empty + * argument list can be used as the supplier. For example, + * {@code IllegalStateException::new} + */ + public T orElseThrow(Supplier exceptionSupplier) throws X { + if (get() != null) { + return get(); + } else { + throw exceptionSupplier.get(); + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + + if (obj instanceof RegistryObject) { + return Objects.equals(((RegistryObject) obj).name, name); + } + + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } +} diff --git a/patchwork-registries/src/main/java/net/minecraftforge/fml/client/registry/ClientRegistry.java b/patchwork-registries/src/main/java/net/minecraftforge/fml/client/registry/ClientRegistry.java index 41054f51..0fda3e3c 100644 --- a/patchwork-registries/src/main/java/net/minecraftforge/fml/client/registry/ClientRegistry.java +++ b/patchwork-registries/src/main/java/net/minecraftforge/fml/client/registry/ClientRegistry.java @@ -24,7 +24,6 @@ import net.minecraft.block.entity.BlockEntity; import net.minecraft.client.options.KeyBinding; -import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher; import net.minecraft.client.render.block.entity.BlockEntityRenderer; import net.minecraft.entity.Entity; import net.minecraft.util.Identifier; @@ -44,7 +43,7 @@ public class ClientRegistry { */ public static synchronized void bindTileEntitySpecialRenderer(Class tileEntityClass, BlockEntityRenderer specialRenderer) { BlockEntityRendererRegistry.INSTANCE.register(tileEntityClass, specialRenderer); - specialRenderer.setRenderManager(BlockEntityRenderDispatcher.INSTANCE); + // Done by Fabric API: specialRenderer.setRenderManager(BlockEntityRenderDispatcher.INSTANCE); } /** diff --git a/patchwork-registries/src/main/java/net/minecraftforge/registries/DeferredRegister.java b/patchwork-registries/src/main/java/net/minecraftforge/registries/DeferredRegister.java new file mode 100644 index 00000000..73a9eda0 --- /dev/null +++ b/patchwork-registries/src/main/java/net/minecraftforge/registries/DeferredRegister.java @@ -0,0 +1,104 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.registries; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.function.Supplier; + +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.RegistryObject; + +import net.minecraft.util.Identifier; + +/** + * Utility class to help with managing registry entries. + * Maintains a list of all suppliers for entries and registers them during the proper Register event. + * Suppliers should return NEW instances every time. + * + * @param The base registry type, must be a concrete base class, do not use subclasses or wild cards. + */ +public class DeferredRegister> { + private final IForgeRegistry type; + private final String modid; + private final Map, Supplier> entries = new LinkedHashMap<>(); + private final Set> entriesView = Collections.unmodifiableSet(entries.keySet()); + + public DeferredRegister(IForgeRegistry reg, String modid) { + this.type = reg; + this.modid = modid; + } + + /** + * Adds a new supplier to the list of entries to be registered, and returns a RegistryObject that will be populated with the created entry automatically. + * + * @param name The new entry's name, it will automatically have the modid prefixed. + * @param sup A factory for the new entry, it should return a new instance every time it is called. + * @return A RegistryObject that will be updated with when the entries in the registry change. + */ + @SuppressWarnings("unchecked") + public RegistryObject register(final String name, final Supplier sup) { + Objects.requireNonNull(name); + Objects.requireNonNull(sup); + final Identifier key = new Identifier(modid, name); + RegistryObject ret = RegistryObject.of(key, this.type); + + if (entries.putIfAbsent((RegistryObject) ret, () -> (T) sup.get().setRegistryName(key)) != null) { + throw new IllegalArgumentException("Duplicate registration " + name); + } + + return ret; + } + + /** + * Adds our event handler to the specified event bus, this MUST be called in order for this class to function. + * See the example usage. + * + * @param bus The Mod Specific event bus. + */ + public void register(IEventBus bus) { + bus.addListener(this::addEntries); + } + + /** + * @return The unmodifiable view of registered entries. Useful for bulk operations on all values. + */ + public Collection> getEntries() { + return entriesView; + } + + private void addEntries(RegistryEvent.Register event) { + if (event.getGenericType() == this.type.getRegistrySuperType()) { + @SuppressWarnings("unchecked") + IForgeRegistry reg = (IForgeRegistry) event.getRegistry(); + + for (Entry, Supplier> e : entries.entrySet()) { + reg.register(e.getValue().get()); + e.getKey().updateReference(reg); + } + } + } +} diff --git a/patchwork-registries/src/main/java/net/minecraftforge/registries/ForgeRegistryEntry.java b/patchwork-registries/src/main/java/net/minecraftforge/registries/ForgeRegistryEntry.java index 9a77918d..02ccfab0 100644 --- a/patchwork-registries/src/main/java/net/minecraftforge/registries/ForgeRegistryEntry.java +++ b/patchwork-registries/src/main/java/net/minecraftforge/registries/ForgeRegistryEntry.java @@ -21,24 +21,24 @@ import net.minecraft.util.Identifier; -public abstract class ForgeRegistryEntry implements IForgeRegistryEntry { +public abstract class ForgeRegistryEntry> implements IForgeRegistryEntry { private Identifier registryName; @Override - public final IForgeRegistryEntry setRegistryName(Identifier name) { + public final V setRegistryName(Identifier name) { return setRegistryName(name.toString()); } - public final IForgeRegistryEntry setRegistryName(String name) { + public final V setRegistryName(String name) { if (getRegistryName() != null) { throw new IllegalStateException("Attempted to set registry name with existing registry name! New: " + name + " Old: " + getRegistryName()); } this.registryName = GameData.checkPrefix(name, true); - return this; + return (V) this; } - public final IForgeRegistryEntry setRegistryName(String modID, String name) { + public final V setRegistryName(String modID, String name) { return setRegistryName(modID + ":" + name); } diff --git a/patchwork-registries/src/main/java/net/minecraftforge/registries/IForgeRegistryEntry.java b/patchwork-registries/src/main/java/net/minecraftforge/registries/IForgeRegistryEntry.java index 9834ab7f..6a79c353 100644 --- a/patchwork-registries/src/main/java/net/minecraftforge/registries/IForgeRegistryEntry.java +++ b/patchwork-registries/src/main/java/net/minecraftforge/registries/IForgeRegistryEntry.java @@ -39,7 +39,7 @@ public interface IForgeRegistryEntry { * @param name the unique {@link Identifier} * @return this instance, to allow for chaining */ - IForgeRegistryEntry setRegistryName(Identifier name); + V setRegistryName(Identifier name); /** * Determines the type for this entry, used to look up the correct registry in the global registries list as there can only be one diff --git a/patchwork-registries/src/main/java/net/patchworkmc/impl/registries/ExtendedForgeRegistryEntry.java b/patchwork-registries/src/main/java/net/patchworkmc/impl/registries/ExtendedForgeRegistryEntry.java index 17907290..606707c3 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/impl/registries/ExtendedForgeRegistryEntry.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/impl/registries/ExtendedForgeRegistryEntry.java @@ -24,8 +24,9 @@ import net.minecraft.util.Identifier; -public interface ExtendedForgeRegistryEntry extends IForgeRegistryEntry { - default IForgeRegistryEntry setRegistryName(String full) { +@SuppressWarnings("unchecked") +public interface ExtendedForgeRegistryEntry> extends IForgeRegistryEntry { + default V setRegistryName(String full) { String activeNamespace = ModLoadingContext.get().getActiveNamespace(); if (activeNamespace == null || activeNamespace.equals("minecraft")) { @@ -47,7 +48,10 @@ default IForgeRegistryEntry setRegistryName(String full) { return this.setRegistryName(identifier); } - default IForgeRegistryEntry setRegistryName(String namespace, String name) { + default V setRegistryName(String namespace, String name) { return this.setRegistryName(new Identifier(namespace, name)); } + + @Override + V setRegistryName(Identifier name); } diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinActivity.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinActivity.java index 6fc3279f..9809b5f0 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinActivity.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinActivity.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Activity.class) -public class MixinActivity implements ExtendedForgeRegistryEntry { +public class MixinActivity implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBiome.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBiome.java index a4886a4d..8b36e4cd 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBiome.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBiome.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Biome.class) -public class MixinBiome implements ExtendedForgeRegistryEntry { +public class MixinBiome implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBiomeSourceType.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBiomeSourceType.java index b4ae7904..aa4c7147 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBiomeSourceType.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBiomeSourceType.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(BiomeSourceType.class) -public class MixinBiomeSourceType implements ExtendedForgeRegistryEntry { +public class MixinBiomeSourceType implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBlock.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBlock.java index 8c4a2bc1..40c7afa6 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBlock.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBlock.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Block.class) -public class MixinBlock implements ExtendedForgeRegistryEntry { +public class MixinBlock implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBlockEntityType.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBlockEntityType.java index 5955dae2..bd700b43 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBlockEntityType.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinBlockEntityType.java @@ -31,19 +31,19 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(BlockEntityType.class) -public class MixinBlockEntityType implements ExtendedForgeRegistryEntry { +public class MixinBlockEntityType implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; } public Identifier getRegistryName() { - BlockEntityType blockEntityType = (BlockEntityType) (Object) this; + BlockEntityType blockEntityType = (BlockEntityType) (Object) this; return Identifiers.getOrFallback(Registry.BLOCK_ENTITY, blockEntityType, registryName); } diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinCarver.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinCarver.java index 839eded0..5494bcdd 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinCarver.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinCarver.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Carver.class) -public class MixinCarver implements ExtendedForgeRegistryEntry { +public class MixinCarver implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinChunkGeneratorType.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinChunkGeneratorType.java index 78619711..0e5653d5 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinChunkGeneratorType.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinChunkGeneratorType.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(ChunkGeneratorType.class) -public class MixinChunkGeneratorType implements ExtendedForgeRegistryEntry { +public class MixinChunkGeneratorType implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinChunkStatus.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinChunkStatus.java index 00d78fdb..c486f7bf 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinChunkStatus.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinChunkStatus.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(ChunkStatus.class) -public class MixinChunkStatus implements ExtendedForgeRegistryEntry { +public class MixinChunkStatus implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinContainerType.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinContainerType.java index a4f6c18a..470a8909 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinContainerType.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinContainerType.java @@ -31,15 +31,15 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(ContainerType.class) -public class MixinContainerType implements ExtendedForgeRegistryEntry { +public class MixinContainerType implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; - return this; + return (IForgeRegistryEntry) this; } public Identifier getRegistryName() { diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinDecorator.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinDecorator.java index 1574d0dd..8a39e72e 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinDecorator.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinDecorator.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Decorator.class) -public class MixinDecorator implements ExtendedForgeRegistryEntry { +public class MixinDecorator implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinEnchantment.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinEnchantment.java index 2aed1df2..78457365 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinEnchantment.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinEnchantment.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Enchantment.class) -public class MixinEnchantment implements ExtendedForgeRegistryEntry { +public class MixinEnchantment implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinEntityType.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinEntityType.java index f2ea1ca4..5576cc1c 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinEntityType.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinEntityType.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(EntityType.class) -public class MixinEntityType implements ExtendedForgeRegistryEntry { +public class MixinEntityType implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinFeature.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinFeature.java index fe01389c..f883d181 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinFeature.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinFeature.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Feature.class) -public class MixinFeature implements ExtendedForgeRegistryEntry { +public class MixinFeature implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinFluid.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinFluid.java index ba2f4676..1526f58e 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinFluid.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinFluid.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Fluid.class) -public class MixinFluid implements ExtendedForgeRegistryEntry { +public class MixinFluid implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinItem.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinItem.java index 36cec86c..360ef3d7 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinItem.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinItem.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Item.class) -public class MixinItem implements ExtendedForgeRegistryEntry { +public class MixinItem implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinMemoryModuleType.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinMemoryModuleType.java index feabaac7..798d7edc 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinMemoryModuleType.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinMemoryModuleType.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(MemoryModuleType.class) -public class MixinMemoryModuleType implements ExtendedForgeRegistryEntry { +public class MixinMemoryModuleType implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPaintingMotive.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPaintingMotive.java index 24857aaa..0beabe8d 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPaintingMotive.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPaintingMotive.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(PaintingMotive.class) -public class MixinPaintingMotive implements ExtendedForgeRegistryEntry { +public class MixinPaintingMotive implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinParticleType.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinParticleType.java index d9df6636..dbadd769 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinParticleType.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinParticleType.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(ParticleType.class) -public class MixinParticleType implements ExtendedForgeRegistryEntry { +public class MixinParticleType implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPointOfInterestType.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPointOfInterestType.java index d6a4864c..2dda47a6 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPointOfInterestType.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPointOfInterestType.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(PointOfInterestType.class) -public class MixinPointOfInterestType implements ExtendedForgeRegistryEntry { +public class MixinPointOfInterestType implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPotion.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPotion.java index 26fd4fc8..ae19f7e8 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPotion.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinPotion.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Potion.class) -public class MixinPotion implements ExtendedForgeRegistryEntry { +public class MixinPotion implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinRecipeSerializerSubclass.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinRecipeSerializerSubclass.java index c38f7da0..2efd2ec6 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinRecipeSerializerSubclass.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinRecipeSerializerSubclass.java @@ -36,12 +36,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin({ShapedRecipe.Serializer.class, ShapelessRecipe.Serializer.class, CookingRecipeSerializer.class, CuttingRecipe.Serializer.class, SpecialRecipeSerializer.class}) -public class MixinRecipeSerializerSubclass implements ExtendedForgeRegistryEntry { +public class MixinRecipeSerializerSubclass implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSchedule.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSchedule.java index 5b83c739..e2ace86b 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSchedule.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSchedule.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(Schedule.class) -public class MixinSchedule implements ExtendedForgeRegistryEntry { +public class MixinSchedule implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSensorType.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSensorType.java index 34f56c2c..133ffef8 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSensorType.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSensorType.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(SensorType.class) -public class MixinSensorType implements ExtendedForgeRegistryEntry { +public class MixinSensorType implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSimpleRegistry.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSimpleRegistry.java index a5d181fd..0242cbf5 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSimpleRegistry.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSimpleRegistry.java @@ -19,8 +19,8 @@ package net.patchworkmc.mixin.registries; -import org.spongepowered.asm.mixin.Mixin; import net.minecraftforge.registries.ForgeRegistry; +import org.spongepowered.asm.mixin.Mixin; import net.minecraft.util.registry.SimpleRegistry; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSoundEvent.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSoundEvent.java index 578dbd9f..1013b038 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSoundEvent.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSoundEvent.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(SoundEvent.class) -public class MixinSoundEvent implements ExtendedForgeRegistryEntry { +public class MixinSoundEvent implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinStatType.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinStatType.java index 13c956e9..db14387a 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinStatType.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinStatType.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(StatType.class) -public class MixinStatType implements ExtendedForgeRegistryEntry { +public class MixinStatType implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinStatusEffect.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinStatusEffect.java index 79b89da3..1156d377 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinStatusEffect.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinStatusEffect.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(StatusEffect.class) -public class MixinStatusEffect implements ExtendedForgeRegistryEntry { +public class MixinStatusEffect implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSurfaceBuilder.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSurfaceBuilder.java index 122097c9..d96c5398 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSurfaceBuilder.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinSurfaceBuilder.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(SurfaceBuilder.class) -public class MixinSurfaceBuilder implements ExtendedForgeRegistryEntry { +public class MixinSurfaceBuilder implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinVillagerProfession.java b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinVillagerProfession.java index c18e3a60..cbc79c91 100644 --- a/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinVillagerProfession.java +++ b/patchwork-registries/src/main/java/net/patchworkmc/mixin/registries/MixinVillagerProfession.java @@ -31,12 +31,12 @@ import net.patchworkmc.impl.registries.Identifiers; @Mixin(VillagerProfession.class) -public class MixinVillagerProfession implements ExtendedForgeRegistryEntry { +public class MixinVillagerProfession implements ExtendedForgeRegistryEntry { @Unique private Identifier registryName; @Override - public IForgeRegistryEntry setRegistryName(Identifier name) { + public IForgeRegistryEntry setRegistryName(Identifier name) { this.registryName = name; return this; diff --git a/patchwork-registries/src/main/resources/fabric.mod.json b/patchwork-registries/src/main/resources/fabric.mod.json index 0f861440..8064936c 100644 --- a/patchwork-registries/src/main/resources/fabric.mod.json +++ b/patchwork-registries/src/main/resources/fabric.mod.json @@ -13,13 +13,14 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", + "patchwork-api-base": "*", "patchwork-fml": "*" }, "description": "Implementation of the Forge Registry API.", "mixins": [ "patchwork-registries.mixins.json" ], + "accessWidener": "patchwork-registries.accesswidener", "custom": { "modmenu:api": true, "modmenu:parent": "patchwork" diff --git a/patchwork-registries/src/main/resources/patchwork-registries.accesswidener b/patchwork-registries/src/main/resources/patchwork-registries.accesswidener new file mode 100644 index 00000000..91b7b260 --- /dev/null +++ b/patchwork-registries/src/main/resources/patchwork-registries.accesswidener @@ -0,0 +1,3 @@ +accessWidener v1 named + +accessible method net/minecraft/entity/SpawnRestriction register (Lnet/minecraft/entity/EntityType;Lnet/minecraft/entity/SpawnRestriction$Location;Lnet/minecraft/world/Heightmap$Type;Lnet/minecraft/entity/SpawnRestriction$class_4306;)V diff --git a/patchwork-resource/readme.md b/patchwork-resource/README.md similarity index 100% rename from patchwork-resource/readme.md rename to patchwork-resource/README.md diff --git a/patchwork-resource/build.gradle b/patchwork-resource/build.gradle index f60f2882..0eb1c3b8 100644 --- a/patchwork-resource/build.gradle +++ b/patchwork-resource/build.gradle @@ -1,2 +1,6 @@ archivesBaseName = "patchwork-resource" -version = getSubprojectVersion(project, "0.1.0") \ No newline at end of file +version = getSubprojectVersion(project, "0.2.0") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') +} diff --git a/patchwork-resource/src/main/resources/fabric.mod.json b/patchwork-resource/src/main/resources/fabric.mod.json index 809ef331..16abce0b 100644 --- a/patchwork-resource/src/main/resources/fabric.mod.json +++ b/patchwork-resource/src/main/resources/fabric.mod.json @@ -13,7 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", + "patchwork-api-base": "*", "patchwork-fml": "*" }, "mixins": [ diff --git a/patchwork-tags/build.gradle b/patchwork-tags/build.gradle new file mode 100644 index 00000000..966af4df --- /dev/null +++ b/patchwork-tags/build.gradle @@ -0,0 +1,10 @@ +archivesBaseName = "patchwork-tags" +version = getSubprojectVersion(project, "0.1.0") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') +} + +loom { + accessWidener "src/main/resources/patchwork-tags.accesswidener" +} diff --git a/patchwork-tags/src/main/java/net/minecraftforge/common/Tags.java b/patchwork-tags/src/main/java/net/minecraftforge/common/Tags.java new file mode 100644 index 00000000..23309457 --- /dev/null +++ b/patchwork-tags/src/main/java/net/minecraftforge/common/Tags.java @@ -0,0 +1,271 @@ +/* + * Minecraft Forge, Patchwork Project + * Copyright (c) 2016-2020, 2019-2020 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.common; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.tag.BlockTags; +import net.minecraft.tag.ItemTags; +import net.minecraft.tag.Tag; +import net.minecraft.util.Identifier; + +// Yoinked directly from forge +public class Tags { + public static class Blocks { + public static final Tag CHESTS = tag("chests"); + public static final Tag CHESTS_ENDER = tag("chests/ender"); + public static final Tag CHESTS_TRAPPED = tag("chests/trapped"); + public static final Tag CHESTS_WOODEN = tag("chests/wooden"); + public static final Tag COBBLESTONE = tag("cobblestone"); + public static final Tag DIRT = tag("dirt"); + public static final Tag END_STONES = tag("end_stones"); + public static final Tag FENCE_GATES = tag("fence_gates"); + public static final Tag FENCE_GATES_WOODEN = tag("fence_gates/wooden"); + public static final Tag FENCES = tag("fences"); + public static final Tag FENCES_NETHER_BRICK = tag("fences/nether_brick"); + public static final Tag FENCES_WOODEN = tag("fences/wooden"); + + public static final Tag GLASS = tag("glass"); + public static final Tag GLASS_BLACK = tag("glass/black"); + public static final Tag GLASS_BLUE = tag("glass/blue"); + public static final Tag GLASS_BROWN = tag("glass/brown"); + public static final Tag GLASS_COLORLESS = tag("glass/colorless"); + public static final Tag GLASS_CYAN = tag("glass/cyan"); + public static final Tag GLASS_GRAY = tag("glass/gray"); + public static final Tag GLASS_GREEN = tag("glass/green"); + public static final Tag GLASS_LIGHT_BLUE = tag("glass/light_blue"); + public static final Tag GLASS_LIGHT_GRAY = tag("glass/light_gray"); + public static final Tag GLASS_LIME = tag("glass/lime"); + public static final Tag GLASS_MAGENTA = tag("glass/magenta"); + public static final Tag GLASS_ORANGE = tag("glass/orange"); + public static final Tag GLASS_PINK = tag("glass/pink"); + public static final Tag GLASS_PURPLE = tag("glass/purple"); + public static final Tag GLASS_RED = tag("glass/red"); + public static final Tag GLASS_WHITE = tag("glass/white"); + public static final Tag GLASS_YELLOW = tag("glass/yellow"); + + public static final Tag GLASS_PANES = tag("glass_panes"); + public static final Tag GLASS_PANES_BLACK = tag("glass_panes/black"); + public static final Tag GLASS_PANES_BLUE = tag("glass_panes/blue"); + public static final Tag GLASS_PANES_BROWN = tag("glass_panes/brown"); + public static final Tag GLASS_PANES_COLORLESS = tag("glass_panes/colorless"); + public static final Tag GLASS_PANES_CYAN = tag("glass_panes/cyan"); + public static final Tag GLASS_PANES_GRAY = tag("glass_panes/gray"); + public static final Tag GLASS_PANES_GREEN = tag("glass_panes/green"); + public static final Tag GLASS_PANES_LIGHT_BLUE = tag("glass_panes/light_blue"); + public static final Tag GLASS_PANES_LIGHT_GRAY = tag("glass_panes/light_gray"); + public static final Tag GLASS_PANES_LIME = tag("glass_panes/lime"); + public static final Tag GLASS_PANES_MAGENTA = tag("glass_panes/magenta"); + public static final Tag GLASS_PANES_ORANGE = tag("glass_panes/orange"); + public static final Tag GLASS_PANES_PINK = tag("glass_panes/pink"); + public static final Tag GLASS_PANES_PURPLE = tag("glass_panes/purple"); + public static final Tag GLASS_PANES_RED = tag("glass_panes/red"); + public static final Tag GLASS_PANES_WHITE = tag("glass_panes/white"); + public static final Tag GLASS_PANES_YELLOW = tag("glass_panes/yellow"); + + public static final Tag GRAVEL = tag("gravel"); + public static final Tag NETHERRACK = tag("netherrack"); + public static final Tag OBSIDIAN = tag("obsidian"); + public static final Tag ORES = tag("ores"); + public static final Tag ORES_COAL = tag("ores/coal"); + public static final Tag ORES_DIAMOND = tag("ores/diamond"); + public static final Tag ORES_EMERALD = tag("ores/emerald"); + public static final Tag ORES_GOLD = tag("ores/gold"); + public static final Tag ORES_IRON = tag("ores/iron"); + public static final Tag ORES_LAPIS = tag("ores/lapis"); + public static final Tag ORES_QUARTZ = tag("ores/quartz"); + public static final Tag ORES_REDSTONE = tag("ores/redstone"); + + public static final Tag SAND = tag("sand"); + public static final Tag SAND_COLORLESS = tag("sand/colorless"); + public static final Tag SAND_RED = tag("sand/red"); + + public static final Tag SANDSTONE = tag("sandstone"); + public static final Tag STAINED_GLASS = tag("stained_glass"); + public static final Tag STAINED_GLASS_PANES = tag("stained_glass_panes"); + public static final Tag STONE = tag("stone"); + public static final Tag STORAGE_BLOCKS = tag("storage_blocks"); + public static final Tag STORAGE_BLOCKS_COAL = tag("storage_blocks/coal"); + public static final Tag STORAGE_BLOCKS_DIAMOND = tag("storage_blocks/diamond"); + public static final Tag STORAGE_BLOCKS_EMERALD = tag("storage_blocks/emerald"); + public static final Tag STORAGE_BLOCKS_GOLD = tag("storage_blocks/gold"); + public static final Tag STORAGE_BLOCKS_IRON = tag("storage_blocks/iron"); + public static final Tag STORAGE_BLOCKS_LAPIS = tag("storage_blocks/lapis"); + public static final Tag STORAGE_BLOCKS_QUARTZ = tag("storage_blocks/quartz"); + public static final Tag STORAGE_BLOCKS_REDSTONE = tag("storage_blocks/redstone"); + + private static Tag tag(String name) { + return new BlockTags.CachingTag(new Identifier("forge", name)); + } + } + + public static class Items { + public static final Tag ARROWS = tag("arrows"); + public static final Tag BEACON_PAYMENT = tag("beacon_payment"); + public static final Tag BONES = tag("bones"); + public static final Tag BOOKSHELVES = tag("bookshelves"); + public static final Tag CHESTS = tag("chests"); + public static final Tag CHESTS_ENDER = tag("chests/ender"); + public static final Tag CHESTS_TRAPPED = tag("chests/trapped"); + public static final Tag CHESTS_WOODEN = tag("chests/wooden"); + public static final Tag COBBLESTONE = tag("cobblestone"); + public static final Tag CROPS = tag("crops"); + public static final Tag CROPS_BEETROOT = tag("crops/beetroot"); + public static final Tag CROPS_CARROT = tag("crops/carrot"); + public static final Tag CROPS_NETHER_WART = tag("crops/nether_wart"); + public static final Tag CROPS_POTATO = tag("crops/potato"); + public static final Tag CROPS_WHEAT = tag("crops/wheat"); + public static final Tag DUSTS = tag("dusts"); + public static final Tag DUSTS_PRISMARINE = tag("dusts/prismarine"); + public static final Tag DUSTS_REDSTONE = tag("dusts/redstone"); + public static final Tag DUSTS_GLOWSTONE = tag("dusts/glowstone"); + + public static final Tag DYES = tag("dyes"); + public static final Tag DYES_BLACK = tag("dyes/black"); + public static final Tag DYES_RED = tag("dyes/red"); + public static final Tag DYES_GREEN = tag("dyes/green"); + public static final Tag DYES_BROWN = tag("dyes/brown"); + public static final Tag DYES_BLUE = tag("dyes/blue"); + public static final Tag DYES_PURPLE = tag("dyes/purple"); + public static final Tag DYES_CYAN = tag("dyes/cyan"); + public static final Tag DYES_LIGHT_GRAY = tag("dyes/light_gray"); + public static final Tag DYES_GRAY = tag("dyes/gray"); + public static final Tag DYES_PINK = tag("dyes/pink"); + public static final Tag DYES_LIME = tag("dyes/lime"); + public static final Tag DYES_YELLOW = tag("dyes/yellow"); + public static final Tag DYES_LIGHT_BLUE = tag("dyes/light_blue"); + public static final Tag DYES_MAGENTA = tag("dyes/magenta"); + public static final Tag DYES_ORANGE = tag("dyes/orange"); + public static final Tag DYES_WHITE = tag("dyes/white"); + + public static final Tag EGGS = tag("eggs"); + public static final Tag END_STONES = tag("end_stones"); + public static final Tag ENDER_PEARLS = tag("ender_pearls"); + public static final Tag FEATHERS = tag("feathers"); + public static final Tag FENCE_GATES = tag("fence_gates"); + public static final Tag FENCE_GATES_WOODEN = tag("fence_gates/wooden"); + public static final Tag FENCES = tag("fences"); + public static final Tag FENCES_NETHER_BRICK = tag("fences/nether_brick"); + public static final Tag FENCES_WOODEN = tag("fences/wooden"); + public static final Tag GEMS = tag("gems"); + public static final Tag GEMS_DIAMOND = tag("gems/diamond"); + public static final Tag GEMS_EMERALD = tag("gems/emerald"); + public static final Tag GEMS_LAPIS = tag("gems/lapis"); + public static final Tag GEMS_PRISMARINE = tag("gems/prismarine"); + public static final Tag GEMS_QUARTZ = tag("gems/quartz"); + + public static final Tag GLASS = tag("glass"); + public static final Tag GLASS_BLACK = tag("glass/black"); + public static final Tag GLASS_BLUE = tag("glass/blue"); + public static final Tag GLASS_BROWN = tag("glass/brown"); + public static final Tag GLASS_COLORLESS = tag("glass/colorless"); + public static final Tag GLASS_CYAN = tag("glass/cyan"); + public static final Tag GLASS_GRAY = tag("glass/gray"); + public static final Tag GLASS_GREEN = tag("glass/green"); + public static final Tag GLASS_LIGHT_BLUE = tag("glass/light_blue"); + public static final Tag GLASS_LIGHT_GRAY = tag("glass/light_gray"); + public static final Tag GLASS_LIME = tag("glass/lime"); + public static final Tag GLASS_MAGENTA = tag("glass/magenta"); + public static final Tag GLASS_ORANGE = tag("glass/orange"); + public static final Tag GLASS_PINK = tag("glass/pink"); + public static final Tag GLASS_PURPLE = tag("glass/purple"); + public static final Tag GLASS_RED = tag("glass/red"); + public static final Tag GLASS_WHITE = tag("glass/white"); + public static final Tag GLASS_YELLOW = tag("glass/yellow"); + + public static final Tag GLASS_PANES = tag("glass_panes"); + public static final Tag GLASS_PANES_BLACK = tag("glass_panes/black"); + public static final Tag GLASS_PANES_BLUE = tag("glass_panes/blue"); + public static final Tag GLASS_PANES_BROWN = tag("glass_panes/brown"); + public static final Tag GLASS_PANES_COLORLESS = tag("glass_panes/colorless"); + public static final Tag GLASS_PANES_CYAN = tag("glass_panes/cyan"); + public static final Tag GLASS_PANES_GRAY = tag("glass_panes/gray"); + public static final Tag GLASS_PANES_GREEN = tag("glass_panes/green"); + public static final Tag GLASS_PANES_LIGHT_BLUE = tag("glass_panes/light_blue"); + public static final Tag GLASS_PANES_LIGHT_GRAY = tag("glass_panes/light_gray"); + public static final Tag GLASS_PANES_LIME = tag("glass_panes/lime"); + public static final Tag GLASS_PANES_MAGENTA = tag("glass_panes/magenta"); + public static final Tag GLASS_PANES_ORANGE = tag("glass_panes/orange"); + public static final Tag GLASS_PANES_PINK = tag("glass_panes/pink"); + public static final Tag GLASS_PANES_PURPLE = tag("glass_panes/purple"); + public static final Tag GLASS_PANES_RED = tag("glass_panes/red"); + public static final Tag GLASS_PANES_WHITE = tag("glass_panes/white"); + public static final Tag GLASS_PANES_YELLOW = tag("glass_panes/yellow"); + + public static final Tag GRAVEL = tag("gravel"); + public static final Tag GUNPOWDER = tag("gunpowder"); + public static final Tag HEADS = tag("heads"); + public static final Tag INGOTS = tag("ingots"); + public static final Tag INGOTS_BRICK = tag("ingots/brick"); + public static final Tag INGOTS_GOLD = tag("ingots/gold"); + public static final Tag INGOTS_IRON = tag("ingots/iron"); + public static final Tag INGOTS_NETHER_BRICK = tag("ingots/nether_brick"); + public static final Tag LEATHER = tag("leather"); + public static final Tag MUSHROOMS = tag("mushrooms"); + public static final Tag MUSIC_DISCS = tag("music_discs"); + public static final Tag NETHER_STARS = tag("nether_stars"); + public static final Tag NETHERRACK = tag("netherrack"); + public static final Tag NUGGETS = tag("nuggets"); + public static final Tag NUGGETS_GOLD = tag("nuggets/gold"); + public static final Tag NUGGETS_IRON = tag("nuggets/iron"); + public static final Tag OBSIDIAN = tag("obsidian"); + public static final Tag ORES = tag("ores"); + public static final Tag ORES_COAL = tag("ores/coal"); + public static final Tag ORES_DIAMOND = tag("ores/diamond"); + public static final Tag ORES_EMERALD = tag("ores/emerald"); + public static final Tag ORES_GOLD = tag("ores/gold"); + public static final Tag ORES_IRON = tag("ores/iron"); + public static final Tag ORES_LAPIS = tag("ores/lapis"); + public static final Tag ORES_QUARTZ = tag("ores/quartz"); + public static final Tag ORES_REDSTONE = tag("ores/redstone"); + public static final Tag RODS = tag("rods"); + public static final Tag RODS_BLAZE = tag("rods/blaze"); + public static final Tag RODS_WOODEN = tag("rods/wooden"); + + public static final Tag SAND = tag("sand"); + public static final Tag SAND_COLORLESS = tag("sand/colorless"); + public static final Tag SAND_RED = tag("sand/red"); + + public static final Tag SANDSTONE = tag("sandstone"); + public static final Tag SEEDS = tag("seeds"); + public static final Tag SEEDS_BEETROOT = tag("seeds/beetroot"); + public static final Tag SEEDS_MELON = tag("seeds/melon"); + public static final Tag SEEDS_PUMPKIN = tag("seeds/pumpkin"); + public static final Tag SEEDS_WHEAT = tag("seeds/wheat"); + public static final Tag SLIMEBALLS = tag("slimeballs"); + public static final Tag STAINED_GLASS = tag("stained_glass"); + public static final Tag STAINED_GLASS_PANES = tag("stained_glass_panes"); + public static final Tag STONE = tag("stone"); + public static final Tag STORAGE_BLOCKS = tag("storage_blocks"); + public static final Tag STORAGE_BLOCKS_COAL = tag("storage_blocks/coal"); + public static final Tag STORAGE_BLOCKS_DIAMOND = tag("storage_blocks/diamond"); + public static final Tag STORAGE_BLOCKS_EMERALD = tag("storage_blocks/emerald"); + public static final Tag STORAGE_BLOCKS_GOLD = tag("storage_blocks/gold"); + public static final Tag STORAGE_BLOCKS_IRON = tag("storage_blocks/iron"); + public static final Tag STORAGE_BLOCKS_LAPIS = tag("storage_blocks/lapis"); + public static final Tag STORAGE_BLOCKS_QUARTZ = tag("storage_blocks/quartz"); + public static final Tag STORAGE_BLOCKS_REDSTONE = tag("storage_blocks/redstone"); + public static final Tag STRING = tag("string"); + + private static Tag tag(String name) { + return new ItemTags.CachingTag(new Identifier("forge", name)); + } + } +} diff --git a/patchwork-tags/src/main/resources/assets/patchwork-tags/icon.png b/patchwork-tags/src/main/resources/assets/patchwork-tags/icon.png new file mode 100644 index 00000000..de75d2fb Binary files /dev/null and b/patchwork-tags/src/main/resources/assets/patchwork-tags/icon.png differ diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests.json new file mode 100644 index 00000000..5b2186a2 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "#forge:chests/ender", + "#forge:chests/trapped", + "#forge:chests/wooden" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests/ender.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests/ender.json new file mode 100644 index 00000000..d7a16e96 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests/ender.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:ender_chest" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests/trapped.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests/trapped.json new file mode 100644 index 00000000..55c7aec1 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests/trapped.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:trapped_chest" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests/wooden.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests/wooden.json new file mode 100644 index 00000000..46ff16fc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/chests/wooden.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "minecraft:chest", + "minecraft:trapped_chest" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/cobblestone.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/cobblestone.json new file mode 100644 index 00000000..a5c96d86 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/cobblestone.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "minecraft:cobblestone", + "minecraft:infested_cobblestone", + "minecraft:mossy_cobblestone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/dirt.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/dirt.json new file mode 100644 index 00000000..0e471849 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/dirt.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "minecraft:dirt", + "minecraft:coarse_dirt", + "minecraft:podzol" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/end_stones.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/end_stones.json new file mode 100644 index 00000000..6ed8faad --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/end_stones.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:end_stone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/fence_gates.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/fence_gates.json new file mode 100644 index 00000000..4fc8670f --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/fence_gates.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "#forge:fence_gates/wooden" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/fence_gates/wooden.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/fence_gates/wooden.json new file mode 100644 index 00000000..7b803077 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/fence_gates/wooden.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "values": [ + "minecraft:oak_fence_gate", + "minecraft:spruce_fence_gate", + "minecraft:birch_fence_gate", + "minecraft:jungle_fence_gate", + "minecraft:acacia_fence_gate", + "minecraft:dark_oak_fence_gate" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/fences.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/fences.json new file mode 100644 index 00000000..a7e4edcd --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/fences.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#forge:fences/nether_brick", + "#forge:fences/wooden" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/fences/nether_brick.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/fences/nether_brick.json new file mode 100644 index 00000000..49884116 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/fences/nether_brick.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:nether_brick_fence" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/fences/wooden.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/fences/wooden.json new file mode 100644 index 00000000..af77edde --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/fences/wooden.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "values": [ + "minecraft:oak_fence", + "minecraft:spruce_fence", + "minecraft:birch_fence", + "minecraft:jungle_fence", + "minecraft:acacia_fence", + "minecraft:dark_oak_fence" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass.json new file mode 100644 index 00000000..6df0a8cb --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#forge:glass/colorless", + "#forge:stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/black.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/black.json new file mode 100644 index 00000000..38eddb25 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/black.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:black_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/blue.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/blue.json new file mode 100644 index 00000000..5e20981a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/blue.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:blue_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/brown.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/brown.json new file mode 100644 index 00000000..760da354 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/brown.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:brown_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/colorless.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/colorless.json new file mode 100644 index 00000000..8e580743 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/colorless.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/cyan.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/cyan.json new file mode 100644 index 00000000..75159cfc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/cyan.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:cyan_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/gray.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/gray.json new file mode 100644 index 00000000..7da37999 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/gray.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gray_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/green.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/green.json new file mode 100644 index 00000000..88560a1b --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/green.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:green_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/light_blue.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/light_blue.json new file mode 100644 index 00000000..0cad485f --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/light_blue.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:light_blue_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/light_gray.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/light_gray.json new file mode 100644 index 00000000..155b462b --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/light_gray.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:light_gray_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/lime.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/lime.json new file mode 100644 index 00000000..83f297e0 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/lime.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:lime_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/magenta.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/magenta.json new file mode 100644 index 00000000..1a05a1a4 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/magenta.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:magenta_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/orange.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/orange.json new file mode 100644 index 00000000..dce95254 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/orange.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:orange_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/pink.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/pink.json new file mode 100644 index 00000000..736709fb --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/pink.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:pink_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/purple.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/purple.json new file mode 100644 index 00000000..25891652 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/purple.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:purple_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/red.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/red.json new file mode 100644 index 00000000..5b4e58b0 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/red.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:red_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/white.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/white.json new file mode 100644 index 00000000..7da94883 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/white.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:white_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/yellow.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/yellow.json new file mode 100644 index 00000000..45bb137b --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass/yellow.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:yellow_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes.json new file mode 100644 index 00000000..175f6a66 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#forge:glass_panes/colorless", + "#forge:stained_glass_panes" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/black.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/black.json new file mode 100644 index 00000000..ab7ea540 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/black.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:black_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/blue.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/blue.json new file mode 100644 index 00000000..3e454494 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/blue.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:blue_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/brown.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/brown.json new file mode 100644 index 00000000..b0fc36bb --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/brown.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:brown_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/colorless.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/colorless.json new file mode 100644 index 00000000..e312863d --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/colorless.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/cyan.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/cyan.json new file mode 100644 index 00000000..a50053ff --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/cyan.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:cyan_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/gray.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/gray.json new file mode 100644 index 00000000..4469029f --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/gray.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gray_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/green.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/green.json new file mode 100644 index 00000000..4a7ed223 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/green.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:green_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/light_blue.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/light_blue.json new file mode 100644 index 00000000..82605a7d --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/light_blue.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:light_blue_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/light_gray.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/light_gray.json new file mode 100644 index 00000000..267d4e6a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/light_gray.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:light_gray_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/lime.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/lime.json new file mode 100644 index 00000000..54389292 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/lime.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:lime_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/magenta.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/magenta.json new file mode 100644 index 00000000..94c5d119 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/magenta.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:magenta_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/orange.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/orange.json new file mode 100644 index 00000000..c51a3f3e --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/orange.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:orange_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/pink.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/pink.json new file mode 100644 index 00000000..34085961 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/pink.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:pink_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/purple.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/purple.json new file mode 100644 index 00000000..5c9fe059 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/purple.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:purple_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/red.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/red.json new file mode 100644 index 00000000..11bca5b1 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/red.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:red_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/white.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/white.json new file mode 100644 index 00000000..9a2f1c27 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/white.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:white_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/yellow.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/yellow.json new file mode 100644 index 00000000..7fe639cc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/glass_panes/yellow.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:yellow_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/gravel.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/gravel.json new file mode 100644 index 00000000..47a682ab --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/gravel.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gravel" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/netherrack.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/netherrack.json new file mode 100644 index 00000000..9d430375 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/netherrack.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:netherrack" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/obsidian.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/obsidian.json new file mode 100644 index 00000000..129433a5 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/obsidian.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:obsidian" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores.json new file mode 100644 index 00000000..bc2cb711 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "#forge:ores/coal", + "#forge:ores/diamond", + "#forge:ores/emerald", + "#forge:ores/gold", + "#forge:ores/iron", + "#forge:ores/lapis", + "#forge:ores/redstone", + "#forge:ores/quartz" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/coal.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/coal.json new file mode 100644 index 00000000..7c71d780 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/coal.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:coal_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/diamond.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/diamond.json new file mode 100644 index 00000000..072563e4 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/diamond.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:diamond_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/emerald.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/emerald.json new file mode 100644 index 00000000..3972c84a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/emerald.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:emerald_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/gold.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/gold.json new file mode 100644 index 00000000..ebbc8168 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/gold.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gold_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/iron.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/iron.json new file mode 100644 index 00000000..dd2164d9 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/iron.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:iron_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/lapis.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/lapis.json new file mode 100644 index 00000000..68d1a961 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/lapis.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:lapis_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/quartz.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/quartz.json new file mode 100644 index 00000000..46ba0451 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/quartz.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:nether_quartz_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/redstone.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/redstone.json new file mode 100644 index 00000000..c88b6802 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/ores/redstone.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:redstone_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/sand.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/sand.json new file mode 100644 index 00000000..a011fb45 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/sand.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#forge:sand/colorless", + "#forge:sand/red" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/sand/colorless.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/sand/colorless.json new file mode 100644 index 00000000..78337d59 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/sand/colorless.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:sand" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/sand/red.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/sand/red.json new file mode 100644 index 00000000..5b5bbef0 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/sand/red.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:red_sand" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/sandstone.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/sandstone.json new file mode 100644 index 00000000..b497d97f --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/sandstone.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "minecraft:sandstone", + "minecraft:cut_sandstone", + "minecraft:chiseled_sandstone", + "minecraft:smooth_sandstone", + "minecraft:red_sandstone", + "minecraft:cut_red_sandstone", + "minecraft:chiseled_red_sandstone", + "minecraft:smooth_red_sandstone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/stained_glass.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/stained_glass.json new file mode 100644 index 00000000..a36fc9bc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/stained_glass.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_stained_glass", + "minecraft:orange_stained_glass", + "minecraft:magenta_stained_glass", + "minecraft:light_blue_stained_glass", + "minecraft:yellow_stained_glass", + "minecraft:lime_stained_glass", + "minecraft:pink_stained_glass", + "minecraft:gray_stained_glass", + "minecraft:light_gray_stained_glass", + "minecraft:cyan_stained_glass", + "minecraft:purple_stained_glass", + "minecraft:blue_stained_glass", + "minecraft:brown_stained_glass", + "minecraft:green_stained_glass", + "minecraft:red_stained_glass", + "minecraft:black_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/stained_glass_panes.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/stained_glass_panes.json new file mode 100644 index 00000000..3ae1826a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/stained_glass_panes.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_stained_glass_pane", + "minecraft:orange_stained_glass_pane", + "minecraft:magenta_stained_glass_pane", + "minecraft:light_blue_stained_glass_pane", + "minecraft:yellow_stained_glass_pane", + "minecraft:lime_stained_glass_pane", + "minecraft:pink_stained_glass_pane", + "minecraft:gray_stained_glass_pane", + "minecraft:light_gray_stained_glass_pane", + "minecraft:cyan_stained_glass_pane", + "minecraft:purple_stained_glass_pane", + "minecraft:blue_stained_glass_pane", + "minecraft:brown_stained_glass_pane", + "minecraft:green_stained_glass_pane", + "minecraft:red_stained_glass_pane", + "minecraft:black_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/stone.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/stone.json new file mode 100644 index 00000000..108b95dc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/stone.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "minecraft:andesite", + "minecraft:diorite", + "minecraft:granite", + "minecraft:infested_stone", + "minecraft:stone", + "minecraft:polished_andesite", + "minecraft:polished_diorite", + "minecraft:polished_granite" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks.json new file mode 100644 index 00000000..e3658b35 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "#forge:storage_blocks/coal", + "#forge:storage_blocks/diamond", + "#forge:storage_blocks/emerald", + "#forge:storage_blocks/gold", + "#forge:storage_blocks/iron", + "#forge:storage_blocks/lapis", + "#forge:storage_blocks/quartz", + "#forge:storage_blocks/redstone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/coal.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/coal.json new file mode 100644 index 00000000..8654a02c --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/coal.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:coal_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/diamond.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/diamond.json new file mode 100644 index 00000000..34d42073 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/diamond.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:diamond_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/emerald.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/emerald.json new file mode 100644 index 00000000..e7cca299 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/emerald.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:emerald_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/gold.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/gold.json new file mode 100644 index 00000000..5cde185e --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/gold.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gold_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/iron.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/iron.json new file mode 100644 index 00000000..7b50a707 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/iron.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:iron_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/lapis.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/lapis.json new file mode 100644 index 00000000..b3e7a3e2 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/lapis.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:lapis_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/quartz.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/quartz.json new file mode 100644 index 00000000..2bf357b8 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/quartz.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:quartz_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/redstone.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/redstone.json new file mode 100644 index 00000000..4b3277bc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/storage_blocks/redstone.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:redstone_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/supports_beacon.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/supports_beacon.json new file mode 100644 index 00000000..e58d0693 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/supports_beacon.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "minecraft:emerald_block", + "minecraft:gold_block", + "minecraft:diamond_block", + "minecraft:iron_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/blocks/supports_conduit.json b/patchwork-tags/src/main/resources/data/forge/tags/blocks/supports_conduit.json new file mode 100644 index 00000000..81680e19 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/blocks/supports_conduit.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "minecraft:prismarine", + "minecraft:prismarine_bricks", + "minecraft:sea_lantern", + "minecraft:dark_prismarine" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/arrows.json b/patchwork-tags/src/main/resources/data/forge/tags/items/arrows.json new file mode 100644 index 00000000..03afe251 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/arrows.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "minecraft:arrow", + "minecraft:tipped_arrow", + "minecraft:spectral_arrow" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/beacon_payment.json b/patchwork-tags/src/main/resources/data/forge/tags/items/beacon_payment.json new file mode 100644 index 00000000..ecee95be --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/beacon_payment.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "minecraft:emerald", + "minecraft:diamond", + "minecraft:gold_ingot", + "minecraft:iron_ingot" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/bones.json b/patchwork-tags/src/main/resources/data/forge/tags/items/bones.json new file mode 100644 index 00000000..db42b75b --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/bones.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:bone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/bookshelves.json b/patchwork-tags/src/main/resources/data/forge/tags/items/bookshelves.json new file mode 100644 index 00000000..8e94bbbc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/bookshelves.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:bookshelf" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/chests.json b/patchwork-tags/src/main/resources/data/forge/tags/items/chests.json new file mode 100644 index 00000000..5b2186a2 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/chests.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "#forge:chests/ender", + "#forge:chests/trapped", + "#forge:chests/wooden" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/chests/ender.json b/patchwork-tags/src/main/resources/data/forge/tags/items/chests/ender.json new file mode 100644 index 00000000..d7a16e96 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/chests/ender.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:ender_chest" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/chests/trapped.json b/patchwork-tags/src/main/resources/data/forge/tags/items/chests/trapped.json new file mode 100644 index 00000000..55c7aec1 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/chests/trapped.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:trapped_chest" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/chests/wooden.json b/patchwork-tags/src/main/resources/data/forge/tags/items/chests/wooden.json new file mode 100644 index 00000000..46ff16fc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/chests/wooden.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "minecraft:chest", + "minecraft:trapped_chest" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/cobblestone.json b/patchwork-tags/src/main/resources/data/forge/tags/items/cobblestone.json new file mode 100644 index 00000000..a5c96d86 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/cobblestone.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "minecraft:cobblestone", + "minecraft:infested_cobblestone", + "minecraft:mossy_cobblestone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/crops.json b/patchwork-tags/src/main/resources/data/forge/tags/items/crops.json new file mode 100644 index 00000000..b1c3220a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/crops.json @@ -0,0 +1,10 @@ +{ + "replace": false, + "values": [ + "#forge:crops/beetroot", + "#forge:crops/carrot", + "#forge:crops/nether_wart", + "#forge:crops/potato", + "#forge:crops/wheat" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/crops/beetroot.json b/patchwork-tags/src/main/resources/data/forge/tags/items/crops/beetroot.json new file mode 100644 index 00000000..a3617f9e --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/crops/beetroot.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:beetroot" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/crops/carrot.json b/patchwork-tags/src/main/resources/data/forge/tags/items/crops/carrot.json new file mode 100644 index 00000000..efe5accf --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/crops/carrot.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:carrot" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/crops/nether_wart.json b/patchwork-tags/src/main/resources/data/forge/tags/items/crops/nether_wart.json new file mode 100644 index 00000000..74d9d420 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/crops/nether_wart.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:nether_wart" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/crops/potato.json b/patchwork-tags/src/main/resources/data/forge/tags/items/crops/potato.json new file mode 100644 index 00000000..0cf13c6c --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/crops/potato.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:potato" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/crops/wheat.json b/patchwork-tags/src/main/resources/data/forge/tags/items/crops/wheat.json new file mode 100644 index 00000000..6be645cd --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/crops/wheat.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:wheat" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dusts.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dusts.json new file mode 100644 index 00000000..512397df --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dusts.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "#forge:dusts/glowstone", + "#forge:dusts/prismarine", + "#forge:dusts/redstone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dusts/glowstone.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dusts/glowstone.json new file mode 100644 index 00000000..826dbd18 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dusts/glowstone.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:glowstone_dust" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dusts/prismarine.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dusts/prismarine.json new file mode 100644 index 00000000..6ba76b04 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dusts/prismarine.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:prismarine_shard" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dusts/redstone.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dusts/redstone.json new file mode 100644 index 00000000..ffe4ba88 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dusts/redstone.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:redstone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes.json new file mode 100644 index 00000000..ac203933 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_dye", + "minecraft:orange_dye", + "minecraft:magenta_dye", + "minecraft:light_blue_dye", + "minecraft:yellow_dye", + "minecraft:lime_dye", + "minecraft:pink_dye", + "minecraft:gray_dye", + "minecraft:light_gray_dye", + "minecraft:cyan_dye", + "minecraft:purple_dye", + "minecraft:blue_dye", + "minecraft:brown_dye", + "minecraft:green_dye", + "minecraft:red_dye", + "minecraft:black_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/black.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/black.json new file mode 100644 index 00000000..e4172681 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/black.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:black_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/blue.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/blue.json new file mode 100644 index 00000000..854f2bb5 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/blue.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:blue_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/brown.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/brown.json new file mode 100644 index 00000000..59550676 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/brown.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:brown_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/cyan.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/cyan.json new file mode 100644 index 00000000..06215afb --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/cyan.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:cyan_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/gray.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/gray.json new file mode 100644 index 00000000..8c6e48e8 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/gray.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gray_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/green.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/green.json new file mode 100644 index 00000000..f2072a97 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/green.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:green_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/light_blue.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/light_blue.json new file mode 100644 index 00000000..b483f2ad --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/light_blue.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:light_blue_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/light_gray.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/light_gray.json new file mode 100644 index 00000000..811c46a8 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/light_gray.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:light_gray_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/lime.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/lime.json new file mode 100644 index 00000000..e7916df3 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/lime.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:lime_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/magenta.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/magenta.json new file mode 100644 index 00000000..2c7129d2 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/magenta.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:magenta_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/orange.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/orange.json new file mode 100644 index 00000000..390bdfc7 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/orange.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:orange_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/pink.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/pink.json new file mode 100644 index 00000000..bc35aa76 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/pink.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:pink_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/purple.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/purple.json new file mode 100644 index 00000000..0a95460a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/purple.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:purple_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/red.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/red.json new file mode 100644 index 00000000..3e8be73f --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/red.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:red_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/white.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/white.json new file mode 100644 index 00000000..59b63da2 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/white.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:white_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/yellow.json b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/yellow.json new file mode 100644 index 00000000..04e067b6 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/dyes/yellow.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:yellow_dye" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/eggs.json b/patchwork-tags/src/main/resources/data/forge/tags/items/eggs.json new file mode 100644 index 00000000..3c471673 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/eggs.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:egg" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/end_stones.json b/patchwork-tags/src/main/resources/data/forge/tags/items/end_stones.json new file mode 100644 index 00000000..6ed8faad --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/end_stones.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:end_stone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ender_pearls.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ender_pearls.json new file mode 100644 index 00000000..4863a360 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ender_pearls.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:ender_pearl" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/feathers.json b/patchwork-tags/src/main/resources/data/forge/tags/items/feathers.json new file mode 100644 index 00000000..015b7099 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/feathers.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:feather" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/fence_gates.json b/patchwork-tags/src/main/resources/data/forge/tags/items/fence_gates.json new file mode 100644 index 00000000..4fc8670f --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/fence_gates.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "#forge:fence_gates/wooden" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/fence_gates/wooden.json b/patchwork-tags/src/main/resources/data/forge/tags/items/fence_gates/wooden.json new file mode 100644 index 00000000..7b803077 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/fence_gates/wooden.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "values": [ + "minecraft:oak_fence_gate", + "minecraft:spruce_fence_gate", + "minecraft:birch_fence_gate", + "minecraft:jungle_fence_gate", + "minecraft:acacia_fence_gate", + "minecraft:dark_oak_fence_gate" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/fences.json b/patchwork-tags/src/main/resources/data/forge/tags/items/fences.json new file mode 100644 index 00000000..a7e4edcd --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/fences.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#forge:fences/nether_brick", + "#forge:fences/wooden" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/fences/nether_brick.json b/patchwork-tags/src/main/resources/data/forge/tags/items/fences/nether_brick.json new file mode 100644 index 00000000..49884116 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/fences/nether_brick.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:nether_brick_fence" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/fences/wooden.json b/patchwork-tags/src/main/resources/data/forge/tags/items/fences/wooden.json new file mode 100644 index 00000000..af77edde --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/fences/wooden.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "values": [ + "minecraft:oak_fence", + "minecraft:spruce_fence", + "minecraft:birch_fence", + "minecraft:jungle_fence", + "minecraft:acacia_fence", + "minecraft:dark_oak_fence" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/gems.json b/patchwork-tags/src/main/resources/data/forge/tags/items/gems.json new file mode 100644 index 00000000..8c554ef4 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/gems.json @@ -0,0 +1,10 @@ +{ + "replace": false, + "values": [ + "#forge:gems/diamond", + "#forge:gems/emerald", + "#forge:gems/lapis", + "#forge:gems/prismarine", + "#forge:gems/quartz" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/gems/diamond.json b/patchwork-tags/src/main/resources/data/forge/tags/items/gems/diamond.json new file mode 100644 index 00000000..0b7f5224 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/gems/diamond.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:diamond" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/gems/emerald.json b/patchwork-tags/src/main/resources/data/forge/tags/items/gems/emerald.json new file mode 100644 index 00000000..0f3bdf92 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/gems/emerald.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:emerald" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/gems/lapis.json b/patchwork-tags/src/main/resources/data/forge/tags/items/gems/lapis.json new file mode 100644 index 00000000..d0146f68 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/gems/lapis.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:lapis_lazuli" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/gems/prismarine.json b/patchwork-tags/src/main/resources/data/forge/tags/items/gems/prismarine.json new file mode 100644 index 00000000..965cd0ab --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/gems/prismarine.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:prismarine_crystals" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/gems/quartz.json b/patchwork-tags/src/main/resources/data/forge/tags/items/gems/quartz.json new file mode 100644 index 00000000..585fa559 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/gems/quartz.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:quartz" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass.json new file mode 100644 index 00000000..6df0a8cb --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#forge:glass/colorless", + "#forge:stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/black.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/black.json new file mode 100644 index 00000000..38eddb25 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/black.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:black_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/blue.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/blue.json new file mode 100644 index 00000000..5e20981a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/blue.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:blue_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/brown.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/brown.json new file mode 100644 index 00000000..760da354 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/brown.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:brown_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/colorless.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/colorless.json new file mode 100644 index 00000000..8e580743 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/colorless.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/cyan.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/cyan.json new file mode 100644 index 00000000..75159cfc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/cyan.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:cyan_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/gray.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/gray.json new file mode 100644 index 00000000..7da37999 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/gray.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gray_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/green.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/green.json new file mode 100644 index 00000000..88560a1b --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/green.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:green_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/light_blue.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/light_blue.json new file mode 100644 index 00000000..0cad485f --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/light_blue.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:light_blue_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/light_gray.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/light_gray.json new file mode 100644 index 00000000..155b462b --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/light_gray.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:light_gray_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/lime.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/lime.json new file mode 100644 index 00000000..83f297e0 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/lime.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:lime_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/magenta.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/magenta.json new file mode 100644 index 00000000..1a05a1a4 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/magenta.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:magenta_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/orange.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/orange.json new file mode 100644 index 00000000..dce95254 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/orange.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:orange_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/pink.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/pink.json new file mode 100644 index 00000000..736709fb --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/pink.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:pink_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/purple.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/purple.json new file mode 100644 index 00000000..25891652 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/purple.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:purple_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/red.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/red.json new file mode 100644 index 00000000..5b4e58b0 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/red.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:red_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/white.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/white.json new file mode 100644 index 00000000..7da94883 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/white.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:white_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass/yellow.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/yellow.json new file mode 100644 index 00000000..45bb137b --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass/yellow.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:yellow_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes.json new file mode 100644 index 00000000..175f6a66 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#forge:glass_panes/colorless", + "#forge:stained_glass_panes" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/black.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/black.json new file mode 100644 index 00000000..ab7ea540 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/black.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:black_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/blue.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/blue.json new file mode 100644 index 00000000..3e454494 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/blue.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:blue_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/brown.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/brown.json new file mode 100644 index 00000000..b0fc36bb --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/brown.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:brown_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/colorless.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/colorless.json new file mode 100644 index 00000000..e312863d --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/colorless.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/cyan.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/cyan.json new file mode 100644 index 00000000..a50053ff --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/cyan.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:cyan_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/gray.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/gray.json new file mode 100644 index 00000000..4469029f --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/gray.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gray_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/green.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/green.json new file mode 100644 index 00000000..4a7ed223 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/green.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:green_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/light_blue.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/light_blue.json new file mode 100644 index 00000000..82605a7d --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/light_blue.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:light_blue_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/light_gray.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/light_gray.json new file mode 100644 index 00000000..267d4e6a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/light_gray.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:light_gray_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/lime.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/lime.json new file mode 100644 index 00000000..54389292 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/lime.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:lime_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/magenta.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/magenta.json new file mode 100644 index 00000000..94c5d119 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/magenta.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:magenta_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/orange.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/orange.json new file mode 100644 index 00000000..c51a3f3e --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/orange.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:orange_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/pink.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/pink.json new file mode 100644 index 00000000..34085961 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/pink.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:pink_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/purple.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/purple.json new file mode 100644 index 00000000..5c9fe059 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/purple.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:purple_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/red.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/red.json new file mode 100644 index 00000000..11bca5b1 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/red.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:red_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/white.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/white.json new file mode 100644 index 00000000..9a2f1c27 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/white.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:white_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/yellow.json b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/yellow.json new file mode 100644 index 00000000..7fe639cc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/glass_panes/yellow.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:yellow_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/gravel.json b/patchwork-tags/src/main/resources/data/forge/tags/items/gravel.json new file mode 100644 index 00000000..47a682ab --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/gravel.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gravel" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/gunpowder.json b/patchwork-tags/src/main/resources/data/forge/tags/items/gunpowder.json new file mode 100644 index 00000000..8b45ecf5 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/gunpowder.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gunpowder" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/heads.json b/patchwork-tags/src/main/resources/data/forge/tags/items/heads.json new file mode 100644 index 00000000..0ce68d9e --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/heads.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "values": [ + "minecraft:creeper_head", + "minecraft:dragon_head", + "minecraft:player_head", + "minecraft:skeleton_skull", + "minecraft:wither_skeleton_skull", + "minecraft:zombie_head" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ingots.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ingots.json new file mode 100644 index 00000000..00914e8a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ingots.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "#forge:ingots/iron", + "#forge:ingots/gold", + "#forge:ingots/brick", + "#forge:ingots/nether_brick" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/brick.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/brick.json new file mode 100644 index 00000000..5bca236a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/brick.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:brick" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/gold.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/gold.json new file mode 100644 index 00000000..74a63e9c --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/gold.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gold_ingot" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/iron.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/iron.json new file mode 100644 index 00000000..5e89bbc7 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/iron.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:iron_ingot" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/nether_brick.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/nether_brick.json new file mode 100644 index 00000000..d13b0207 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ingots/nether_brick.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:nether_brick" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/leather.json b/patchwork-tags/src/main/resources/data/forge/tags/items/leather.json new file mode 100644 index 00000000..118f5c9e --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/leather.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:leather" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/mushrooms.json b/patchwork-tags/src/main/resources/data/forge/tags/items/mushrooms.json new file mode 100644 index 00000000..bbb4511c --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/mushrooms.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "minecraft:brown_mushroom", + "minecraft:red_mushroom" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/music_discs.json b/patchwork-tags/src/main/resources/data/forge/tags/items/music_discs.json new file mode 100644 index 00000000..03bdf1c0 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/music_discs.json @@ -0,0 +1,17 @@ +{ + "replace": false, + "values": [ + "minecraft:music_disc_13", + "minecraft:music_disc_cat", + "minecraft:music_disc_blocks", + "minecraft:music_disc_chirp", + "minecraft:music_disc_far", + "minecraft:music_disc_mall", + "minecraft:music_disc_mellohi", + "minecraft:music_disc_stal", + "minecraft:music_disc_strad", + "minecraft:music_disc_ward", + "minecraft:music_disc_11", + "minecraft:music_disc_wait" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/nether_stars.json b/patchwork-tags/src/main/resources/data/forge/tags/items/nether_stars.json new file mode 100644 index 00000000..d29efd42 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/nether_stars.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:nether_star" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/netherrack.json b/patchwork-tags/src/main/resources/data/forge/tags/items/netherrack.json new file mode 100644 index 00000000..9d430375 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/netherrack.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:netherrack" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/nuggets.json b/patchwork-tags/src/main/resources/data/forge/tags/items/nuggets.json new file mode 100644 index 00000000..66a7ecba --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/nuggets.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#forge:nuggets/iron", + "#forge:nuggets/gold" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/nuggets/gold.json b/patchwork-tags/src/main/resources/data/forge/tags/items/nuggets/gold.json new file mode 100644 index 00000000..3c978eae --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/nuggets/gold.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gold_nugget" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/nuggets/iron.json b/patchwork-tags/src/main/resources/data/forge/tags/items/nuggets/iron.json new file mode 100644 index 00000000..2e6bd5ff --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/nuggets/iron.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:iron_nugget" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/obsidian.json b/patchwork-tags/src/main/resources/data/forge/tags/items/obsidian.json new file mode 100644 index 00000000..129433a5 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/obsidian.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:obsidian" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ores.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ores.json new file mode 100644 index 00000000..bc2cb711 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ores.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "#forge:ores/coal", + "#forge:ores/diamond", + "#forge:ores/emerald", + "#forge:ores/gold", + "#forge:ores/iron", + "#forge:ores/lapis", + "#forge:ores/redstone", + "#forge:ores/quartz" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ores/coal.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/coal.json new file mode 100644 index 00000000..7c71d780 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/coal.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:coal_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ores/diamond.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/diamond.json new file mode 100644 index 00000000..072563e4 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/diamond.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:diamond_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ores/emerald.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/emerald.json new file mode 100644 index 00000000..3972c84a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/emerald.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:emerald_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ores/gold.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/gold.json new file mode 100644 index 00000000..ebbc8168 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/gold.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gold_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ores/iron.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/iron.json new file mode 100644 index 00000000..dd2164d9 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/iron.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:iron_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ores/lapis.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/lapis.json new file mode 100644 index 00000000..68d1a961 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/lapis.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:lapis_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ores/quartz.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/quartz.json new file mode 100644 index 00000000..46ba0451 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/quartz.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:nether_quartz_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/ores/redstone.json b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/redstone.json new file mode 100644 index 00000000..c88b6802 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/ores/redstone.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:redstone_ore" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/rods.json b/patchwork-tags/src/main/resources/data/forge/tags/items/rods.json new file mode 100644 index 00000000..ddd4ad92 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/rods.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#forge:rods/blaze", + "#forge:rods/wooden" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/rods/blaze.json b/patchwork-tags/src/main/resources/data/forge/tags/items/rods/blaze.json new file mode 100644 index 00000000..96bb55e9 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/rods/blaze.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:blaze_rod" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/rods/wooden.json b/patchwork-tags/src/main/resources/data/forge/tags/items/rods/wooden.json new file mode 100644 index 00000000..90f5f079 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/rods/wooden.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:stick" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/sand.json b/patchwork-tags/src/main/resources/data/forge/tags/items/sand.json new file mode 100644 index 00000000..a011fb45 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/sand.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#forge:sand/colorless", + "#forge:sand/red" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/sand/colorless.json b/patchwork-tags/src/main/resources/data/forge/tags/items/sand/colorless.json new file mode 100644 index 00000000..78337d59 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/sand/colorless.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:sand" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/sand/red.json b/patchwork-tags/src/main/resources/data/forge/tags/items/sand/red.json new file mode 100644 index 00000000..5b5bbef0 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/sand/red.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:red_sand" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/sandstone.json b/patchwork-tags/src/main/resources/data/forge/tags/items/sandstone.json new file mode 100644 index 00000000..b497d97f --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/sandstone.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "minecraft:sandstone", + "minecraft:cut_sandstone", + "minecraft:chiseled_sandstone", + "minecraft:smooth_sandstone", + "minecraft:red_sandstone", + "minecraft:cut_red_sandstone", + "minecraft:chiseled_red_sandstone", + "minecraft:smooth_red_sandstone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/seeds.json b/patchwork-tags/src/main/resources/data/forge/tags/items/seeds.json new file mode 100644 index 00000000..163853da --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/seeds.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "#forge:seeds/beetroot", + "#forge:seeds/melon", + "#forge:seeds/pumpkin", + "#forge:seeds/wheat" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/beetroot.json b/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/beetroot.json new file mode 100644 index 00000000..a95a5c2d --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/beetroot.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:beetroot_seeds" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/melon.json b/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/melon.json new file mode 100644 index 00000000..17bd6c8f --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/melon.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:melon_seeds" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/pumpkin.json b/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/pumpkin.json new file mode 100644 index 00000000..2ec15eb1 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/pumpkin.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:pumpkin_seeds" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/wheat.json b/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/wheat.json new file mode 100644 index 00000000..fa806cac --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/seeds/wheat.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:wheat_seeds" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/slimeballs.json b/patchwork-tags/src/main/resources/data/forge/tags/items/slimeballs.json new file mode 100644 index 00000000..e471d6fe --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/slimeballs.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:slime_ball" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/stained_glass.json b/patchwork-tags/src/main/resources/data/forge/tags/items/stained_glass.json new file mode 100644 index 00000000..a36fc9bc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/stained_glass.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_stained_glass", + "minecraft:orange_stained_glass", + "minecraft:magenta_stained_glass", + "minecraft:light_blue_stained_glass", + "minecraft:yellow_stained_glass", + "minecraft:lime_stained_glass", + "minecraft:pink_stained_glass", + "minecraft:gray_stained_glass", + "minecraft:light_gray_stained_glass", + "minecraft:cyan_stained_glass", + "minecraft:purple_stained_glass", + "minecraft:blue_stained_glass", + "minecraft:brown_stained_glass", + "minecraft:green_stained_glass", + "minecraft:red_stained_glass", + "minecraft:black_stained_glass" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/stained_glass_panes.json b/patchwork-tags/src/main/resources/data/forge/tags/items/stained_glass_panes.json new file mode 100644 index 00000000..3ae1826a --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/stained_glass_panes.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_stained_glass_pane", + "minecraft:orange_stained_glass_pane", + "minecraft:magenta_stained_glass_pane", + "minecraft:light_blue_stained_glass_pane", + "minecraft:yellow_stained_glass_pane", + "minecraft:lime_stained_glass_pane", + "minecraft:pink_stained_glass_pane", + "minecraft:gray_stained_glass_pane", + "minecraft:light_gray_stained_glass_pane", + "minecraft:cyan_stained_glass_pane", + "minecraft:purple_stained_glass_pane", + "minecraft:blue_stained_glass_pane", + "minecraft:brown_stained_glass_pane", + "minecraft:green_stained_glass_pane", + "minecraft:red_stained_glass_pane", + "minecraft:black_stained_glass_pane" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/stone.json b/patchwork-tags/src/main/resources/data/forge/tags/items/stone.json new file mode 100644 index 00000000..108b95dc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/stone.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "minecraft:andesite", + "minecraft:diorite", + "minecraft:granite", + "minecraft:infested_stone", + "minecraft:stone", + "minecraft:polished_andesite", + "minecraft:polished_diorite", + "minecraft:polished_granite" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks.json b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks.json new file mode 100644 index 00000000..e3658b35 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "#forge:storage_blocks/coal", + "#forge:storage_blocks/diamond", + "#forge:storage_blocks/emerald", + "#forge:storage_blocks/gold", + "#forge:storage_blocks/iron", + "#forge:storage_blocks/lapis", + "#forge:storage_blocks/quartz", + "#forge:storage_blocks/redstone" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/coal.json b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/coal.json new file mode 100644 index 00000000..8654a02c --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/coal.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:coal_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/diamond.json b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/diamond.json new file mode 100644 index 00000000..34d42073 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/diamond.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:diamond_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/emerald.json b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/emerald.json new file mode 100644 index 00000000..e7cca299 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/emerald.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:emerald_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/gold.json b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/gold.json new file mode 100644 index 00000000..5cde185e --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/gold.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:gold_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/iron.json b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/iron.json new file mode 100644 index 00000000..7b50a707 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/iron.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:iron_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/lapis.json b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/lapis.json new file mode 100644 index 00000000..b3e7a3e2 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/lapis.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:lapis_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/quartz.json b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/quartz.json new file mode 100644 index 00000000..2bf357b8 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/quartz.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:quartz_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/redstone.json b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/redstone.json new file mode 100644 index 00000000..4b3277bc --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/storage_blocks/redstone.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:redstone_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/string.json b/patchwork-tags/src/main/resources/data/forge/tags/items/string.json new file mode 100644 index 00000000..88fcd5dd --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/string.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:string" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/supports_beacon.json b/patchwork-tags/src/main/resources/data/forge/tags/items/supports_beacon.json new file mode 100644 index 00000000..e58d0693 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/supports_beacon.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "minecraft:emerald_block", + "minecraft:gold_block", + "minecraft:diamond_block", + "minecraft:iron_block" + ] +} diff --git a/patchwork-tags/src/main/resources/data/forge/tags/items/supports_conduit.json b/patchwork-tags/src/main/resources/data/forge/tags/items/supports_conduit.json new file mode 100644 index 00000000..81680e19 --- /dev/null +++ b/patchwork-tags/src/main/resources/data/forge/tags/items/supports_conduit.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "minecraft:prismarine", + "minecraft:prismarine_bricks", + "minecraft:sea_lantern", + "minecraft:dark_prismarine" + ] +} diff --git a/patchwork-tags/src/main/resources/fabric.mod.json b/patchwork-tags/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..6ffa7c17 --- /dev/null +++ b/patchwork-tags/src/main/resources/fabric.mod.json @@ -0,0 +1,24 @@ +{ + "schemaVersion": 1, + "id": "patchwork-tags", + "name": "Patchwork Tags", + "version": "${version}", + "license": "LGPL-2.1-only", + "icon": "assets/patchwork-tags/icon.png", + "contact": { + "issues": "https://github.com/PatchworkMC/patchwork-api/issues", + "sources": "https://github.com/PatchworkMC/patchwork-api" + }, + "authors": [ + "PatchworkMC" + ], + "accessWidener": "patchwork-tags.accesswidener", + "depends": { + "patchwork-api-base": "*" + }, + "description": "Implementation of the Forge Tags API.", + "custom": { + "modmenu:api": true, + "modmenu:parent": "patchwork" + } +} diff --git a/patchwork-tags/src/main/resources/patchwork-tags.accesswidener b/patchwork-tags/src/main/resources/patchwork-tags.accesswidener new file mode 100644 index 00000000..0a65a94e --- /dev/null +++ b/patchwork-tags/src/main/resources/patchwork-tags.accesswidener @@ -0,0 +1,3 @@ +accessWidener v1 named + +accessible class net/minecraft/tag/BlockTags$CachingTag diff --git a/patchwork-tooltype/build.gradle b/patchwork-tooltype/build.gradle index 670bfabc..e82d4976 100644 --- a/patchwork-tooltype/build.gradle +++ b/patchwork-tooltype/build.gradle @@ -1,3 +1,6 @@ archivesBaseName = "patchwork-tooltype" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') +} diff --git a/patchwork-tooltype/src/main/resources/fabric.mod.json b/patchwork-tooltype/src/main/resources/fabric.mod.json index bcd2b0c6..a6c45979 100644 --- a/patchwork-tooltype/src/main/resources/fabric.mod.json +++ b/patchwork-tooltype/src/main/resources/fabric.mod.json @@ -15,8 +15,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4", - "fabric": "*" + "patchwork-api-base": "*" }, "custom": { "modmenu:api": true, diff --git a/patchwork-vanilla-patches/build.gradle b/patchwork-vanilla-patches/build.gradle index 9281f2cd..55da2b7e 100644 --- a/patchwork-vanilla-patches/build.gradle +++ b/patchwork-vanilla-patches/build.gradle @@ -1,2 +1,6 @@ archivesBaseName = "patchwork-vanilla-patches" -version = getSubprojectVersion(project, "0.2.0") +version = getSubprojectVersion(project, "0.3.0") + +dependencies { + implementation project(path: ':patchwork-api-base', configuration: 'dev') +} diff --git a/patchwork-vanilla-patches/src/main/resources/fabric.mod.json b/patchwork-vanilla-patches/src/main/resources/fabric.mod.json index 272756cd..95e57bda 100644 --- a/patchwork-vanilla-patches/src/main/resources/fabric.mod.json +++ b/patchwork-vanilla-patches/src/main/resources/fabric.mod.json @@ -13,7 +13,7 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4" + "patchwork-api-base": "*" }, "description": "A collection of Forge's small patches and bugfixes that change vanilla behavior.", "mixins": [ diff --git a/settings.gradle b/settings.gradle index 444b4685..da38dba2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,6 +12,7 @@ pluginManagement { rootProject.name = "patchwork-api" +include 'patchwork-api-base' include 'patchwork-biomes' include 'patchwork-capabilities' include 'patchwork-data-generators' @@ -24,18 +25,23 @@ include 'patchwork-events-rendering' include 'patchwork-events-world' include 'patchwork-extensions' include 'patchwork-extensions-block' +include 'patchwork-extensions-blockentity' include 'patchwork-extensions-item' include 'patchwork-extensions-shearing' +include 'patchwork-fake-players' include 'patchwork-fml' include 'patchwork-god-classes' include 'patchwork-gui' +include 'patchwork-items' +include 'patchwork-key-bindings' include 'patchwork-level-generators' include 'patchwork-loot' +include 'patchwork-model-loader' include 'patchwork-networking' include 'patchwork-networking-messages' include 'patchwork-recipes' include 'patchwork-registries' include 'patchwork-resource' +include 'patchwork-tags' include 'patchwork-tooltype' include 'patchwork-vanilla-patches' - diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 41e75d59..efb4adb4 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -13,7 +13,10 @@ "PatchworkMC" ], "depends": { - "fabricloader": ">=0.8.4" + // This is copied over from patchwork-api-base until Loader/201 is resolved properly. + // Loader/204 exists as a bandage fix, but not a real solution. + "fabricloader": ">=0.10.0", + "fabric": ">=0.15.1" }, "description": "Implementation of Minecraft Forge for Fabric.", "custom": {