diff --git a/.gitignore b/.gitignore index d4865f3..63467d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,30 @@ -# General use .gitignore for Minecraft modding. +# Eclipse +*.launch -# Directories -/.gradle/ -/build/ -/eclipse/ -/run/ -/out/ -/asm/ -/bin/ +# JetBrains IDEs /.idea/ -/.metadata/ -/.settings/ -/libs/ - -# File Extensions -*.psd *.iml *.ipr *.iws -*.classpath -*.project -*.class -# Specific files -Thumbs.db +# Visual Studio Code +/.settings/ +/.vscode/ +/bin/ +.classpath +.project + +# macOS +*.DS_Store + +# Gradle +/.gradle/ +/build/ +/out/ +/classes/ + +# Loom +/run/ + +# Data Generation +/src/main/generated/.cache/ diff --git a/build.gradle b/build.gradle index 4578fcd..438e353 100644 --- a/build.gradle +++ b/build.gradle @@ -1,152 +1,230 @@ plugins { - id "maven-publish" - alias libs.plugins.quilt.loom - alias libs.plugins.chenille + id 'idea' + id 'maven-publish' + alias libs.plugins.fabric.loom } def ENV = System.getenv() def buildTime = ENV.BUILD_TIME ?: new Date().format("yyyyMMddHHmmss") -// Minecraft 1.18 (1.18-pre2) upwards uses Java 17. -project.ext.javaVersion = 17 +def javaVersion = 17 -archivesBaseName = "Soulbound" -group = "dev.upcraft" +group = project.maven_group_id boolean isPreviewBuild = !ENV.TAG || ENV.TAG.matches(".+-.+") def buildNumber = !ENV.TAG ? ("${ENV.BUILD_NUMBER ? "build.${ENV.BUILD_NUMBER}" : buildTime}-${libs.versions.minecraft.get()}") : "" version = (ENV.TAG ?: "development") + ((isPreviewBuild && !ENV.TAG) ? "+${buildNumber}" : "") -println("Building ${project.name} ${version}") - -//FIXME workaround for quilt-loom bug -project.configurations.configureEach { - exclude(group: "net.fabricmc", module: "fabric-loader") - exclude(group: "net.fabricmc.fabric-api") -} - -chenille { - javaVersion = project.ext.javaVersion - repositories { - ladysnake() - modrinth() - terraformers() - } +base { + archivesName.set(project.mod_id) } repositories { - maven { - name = "ParchmentMC" - url = "https://maven.parchmentmc.org" - } + mavenCentral() + maven { + name = 'TerraformersMC' + url = 'https://maven.terraformersmc.com' + } maven { - name = "Up-Mods" - url = "https://maven.uuid.gg/releases" + name = 'Up-Mods' + url = 'https://maven.uuid.gg/releases' + } + maven { + name = 'Ladysnake' + url = 'https://maven.ladysnake.org/releases' + } + maven { + name = 'TeamResourceful' + url = 'https://maven.teamresourceful.com/repository/maven-public' } maven { // needed for polymer - name = "NucleoidMC" - url = "https://maven.nucleoid.xyz" + name = 'NucleoidMC' + url = 'https://maven.nucleoid.xyz' } maven { // needed for fabric permissions api - name = "Sonatype Snapshots" - url = "https://oss.sonatype.org/content/repositories/snapshots" + name = 'SonatypeSnapshots' + url = 'https://oss.sonatype.org/content/repositories/snapshots' + } + maven { + name = 'ParchmentMC' + url = 'https://maven.parchmentmc.org' + content { + includeGroupAndSubgroups('org.parchmentmc') + } + } + exclusiveContent { + forRepository { + maven { + name = 'Modrinth' + url = 'https://api.modrinth.com/maven' + } + } + filter { + includeGroup('maven.modrinth') + } + } + exclusiveContent { + forRepository { + maven { + name = 'Curseforge' + url = 'https://cursemaven.com' + } + } + filter { + includeGroup('curse.maven') + } } } -// All the dependencies are declared at gradle/libs.version.toml and referenced with "libs." -// See https://docs.gradle.org/current/userguide/platforms.html for information on how version catalogs work. dependencies { - minecraft libs.minecraft - mappings(loom.layered { - it.mappings variantOf(libs.quilt.mappings) { classifier 'intermediary-v2' } - it.parchment("${libs.parchment.mappings.get()}@zip") - it.officialMojangMappings { nameSyntheticMembers = false } - }) - modImplementation libs.quilt.loader - - // QSL is not a complete API; You will need Quilted Fabric API to fill in the gaps. - // Quilted Fabric API will automatically pull in the correct QSL version. - modImplementation libs.quilted.fabric.api - modLocalRuntime libs.quilted.fabric.api.deprecated + minecraft libs.minecraft + mappings(loom.layered { + it.officialMojangMappings() + it.parchment("org.parchmentmc.data:parchment-${libs.versions.parchment.minecraft.get()}:${libs.versions.parchment.mappings.get()}@zip") + }) + + compileOnly libs.jetbrains.annotations + + modImplementation libs.fabric.loader + modImplementation libs.fabric.api modImplementation libs.sparkweave - modImplementation libs.midnightlib + modCompileOnly libs.modmenu + modLocalRuntime libs.modmenu - annotationProcessor libs.mixinextras - modIncludeApi libs.mixinextras + modImplementation libs.resourceful.config - modLocalImplementation libs.emi + modCompileOnly variantOf(libs.emi) { classifier 'api' } + modLocalRuntime libs.emi - modLocalRuntime libs.modmenu + modCompileOnly libs.universal.graves + modLocalRuntime libs.bundles.universal.graves.runtime - modLocalImplementation libs.universal.graves - modLocalRuntime libs.bundles.universal.graves.dependencies + modCompileOnly libs.trinkets + modLocalRuntime libs.trinkets +} + +fabricApi { + configureDataGeneration() +} - modLocalImplementation libs.trinkets +loom { + mods { + "${mod_id}" { + // Tell Loom about each source set used by your mod here. This ensures that your mod's classes are properly transformed by Loader. + sourceSet("main") + } + } - modLocalRuntime libs.lazydfu +// accessWidenerPath.set(project.file("src/main/resources/${mod_id}.accesswidener")) + + runs { + client { + ideConfigGenerated(true) + runDir("run") + + if (project.hasProperty('mc_uuid')) { + programArg("--uuid=${project.findProperty('mc_uuid')}") + } + + if (project.hasProperty('mc_username')) { + programArg("--username=${project.findProperty('mc_username')}") + } + + if (project.hasProperty('mc_java_agent_path')) { + vmArg("-javaagent:${project.findProperty('mc_java_agent_path')}") + } + } + } } processResources { - inputs.property "version", version - - filesMatching("quilt.mod.json") { - expand "version": version - } + filteringCharset = "UTF-8" + + def expandProps = [ + "version" : version, + "maven_group_id" : maven_group_id, + "mod_id" : mod_id, + "mod_display_name" : mod_display_name, + "mod_description" : mod_description, + "sources_url" : sources_url, + "issues_url" : issues_url, + "license_url" : license_url, + "discord_url" : discord_url, + "homepage_url" : homepage_url, + + "minecraft_version" : libs.versions.minecraft.get(), + "fabric_loader_version": libs.versions.fabric.loader.get(), + "java_version": "${javaVersion}", + ] + + filesMatching(['pack.mcmeta', '*.mod.json', '*.mixins.json']) { + expand expandProps + } + inputs.properties(expandProps) } -tasks.withType(JavaCompile).configureEach { - it.options.encoding = "UTF-8" - it.options.release.set(project.ext.javaVersion) +tasks.withType(JavaCompile) { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + options.encoding = "UTF-8" + + options.release.set(javaVersion) } java { - // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task if it is present. - withSourcesJar() - withJavadocJar() - - toolchain { - languageVersion = JavaLanguageVersion.of(javaVersion) - vendor = JvmVendorSpec.MICROSOFT - } + withSourcesJar() + + toolchain { + languageVersion.set(JavaLanguageVersion.of(javaVersion)) + vendor = JvmVendorSpec.MICROSOFT + } } -// If you plan to use a different file for the license, don't forget to change the file name here! jar { - from(layout.projectDirectory) { - include "LICENSE.md" - rename { "LICENSE_${archivesBaseName}.md" } - } - - manifest.mainAttributes( - "Implementation-Title": project.archivesBaseName, - "Implementation-Version": project.version, - "Maven-Artifact": "${project.group.toLowerCase(Locale.ROOT)}:${project.archivesBaseName.toLowerCase(Locale.ROOT)}:${project.version}", - "Built-On-Minecraft": libs.versions.minecraft.get(), - "Built-On-Java": "${System.getProperty("java.vm.version")} (${System.getProperty("java.vm.vendor")})" - ) + from(layout.projectDirectory) { + include "LICENSE.md" + rename { "LICENSE_${project.mod_id}.md" } + } + + manifest.mainAttributes( + "Implementation-Title": project.mod_id, + "Implementation-Version": project.version, + "Maven-Artifact": "${project.group}:${rootProject.name}-Fabric:${project.version}", + "Built-On-Minecraft": libs.versions.minecraft.get(), + "Built-On-Java": "${System.getProperty("java.vm.version")} (${System.getProperty("java.vm.vendor")})" + ) } -// Configure the maven publication publishing { - publications { - mavenJava(MavenPublication) { - from components.java - } - } - - repositories { - if (ENV.MAVEN_UPLOAD_URL) { - maven { - url = ENV.MAVEN_UPLOAD_URL - credentials { - username = ENV.MAVEN_UPLOAD_USERNAME - password = ENV.MAVEN_UPLOAD_PASSWORD - } - } - } - } + publications { + mavenJava(MavenPublication) { + artifactId "${rootProject.name}-Fabric" + from components.java + } + } + + repositories { + if (ENV.MAVEN_UPLOAD_URL) { + maven { + url = ENV.MAVEN_UPLOAD_URL + credentials { + username = ENV.MAVEN_UPLOAD_USERNAME + password = ENV.MAVEN_UPLOAD_PASSWORD + } + } + } + } +} + +// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior. +idea { + module { + downloadSources = true + downloadJavadoc = true + } } diff --git a/gradle.properties b/gradle.properties index 635d55b..eec0fa2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,18 @@ # Gradle Properties org.gradle.jvmargs=-Xmx2G org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configuration-cache=true + +# Metadata +mod_id=soulbound +mod_description=An enchantment that will be retain items upon death +mod_display_name=Soulbound +sources_url=https://github.com/Up-Mods/Soulbound +issues_url=https://github.com/Up-Mods/Soulbound/issues +license_url=https://github.com/Up-Mods/Soulbound/blob/HEAD/LICENSE.md +discord_url=https://mods.upcraft.dev/discord +homepage_url=https://www.curseforge.com/projects/618682 +maven_group_id=dev.upcraft.soulbound # Dependencies are managed at gradle/libs.versions.toml diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 899d450..87bdfee 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,19 +1,24 @@ [versions] # The latest versions are available at https://lambdaurora.dev/tools/import_quilt.html chenille = "0.11.+" -quilt_loom = "1.3.+" +fabric_loom = "1.9.2" minecraft = "1.20.1" -quilt_mappings = "1.20.1+build.23" -quilt_loader = "0.20.2" +fabric_loader = "0.16.10" -parchment = "2023.09.03" -emi = "1.0.19+1.20.1" +# https://parchmentmc.org/docs/getting-started +parchment_minecraft = "1.20.1" +parchment_mappings = "2023.09.03" + +jetbrains_annotations = "26.0.1" + +fabric_api = "0.92.2+1.20.1" + +cca = "5.2.3" +emi = "1.1.18+1.20.1" modmenu = "7.2.2" -midnightlib = "1.4.1-quilt" -lazydfu = "0.1.3" -mixinextras = "0.2.0-rc.4" -sparkweave = "0.3.0" +resourceful_config = "2.1.2" +sparkweave = "0.7.0" universal_graves = "3.0.0+1.20.1" sgui = "1.2.2+1.20" @@ -26,23 +31,17 @@ fabric_permissions_api = "0.2-SNAPSHOT" trinkets = "3.7.1" -quilted_fabric_api = "7.1.2+0.87.0-1.20.1" - [libraries] minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } -quilt_mappings = { module = "org.quiltmc:quilt-mappings", version.ref = "quilt_mappings" } -parchment_mappings = { module = "org.parchmentmc.data:parchment-1.20.1", version.ref = "parchment" } -quilt_loader = { module = "org.quiltmc:quilt-loader", version.ref = "quilt_loader" } -mixinextras = { module = "io.github.llamalad7:mixinextras-fabric", version.ref = "mixinextras" } -sparkweave = { module = "dev.upcraft:Sparkweave", version.ref = "sparkweave" } +fabric_loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric_loader" } +jetbrains_annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains_annotations" } -quilted_fabric_api = { module = "org.quiltmc.quilted-fabric-api:quilted-fabric-api", version.ref = "quilted_fabric_api" } -quilted_fabric_api_deprecated = { module = "org.quiltmc.quilted-fabric-api:quilted-fabric-api-deprecated", version.ref = "quilted_fabric_api" } +fabric_api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric_api" } emi = { module = "dev.emi:emi-fabric", version.ref = "emi" } modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" } -midnightlib = { module = "maven.modrinth:midnightlib", version.ref = "midnightlib" } -lazydfu = { module = "maven.modrinth:lazydfu", version.ref = "lazydfu" } +resourceful_config = { module = "com.teamresourceful.resourcefulconfig:resourcefulconfig-fabric-1.20.1", version.ref = "resourceful_config" } +sparkweave = { module = "dev.upcraft.sparkweave:Sparkweave-Fabric", version.ref = "sparkweave" } universal_graves = { module = "maven.modrinth:universal-graves", version.ref = "universal_graves" } sgui = { module = "eu.pb4:sgui", version.ref = "sgui" } @@ -57,11 +56,8 @@ fabric_permissions_api = { module = "me.lucko:fabric-permissions-api", version.r trinkets = { module = "dev.emi:trinkets", version.ref = "trinkets" } -# If you have multiple similar dependencies, you can declare a dependency bundle and reference it on the build script with "libs.bundles.example". [bundles] -quilted_fabric_api = ["quilted_fabric_api", "quilted_fabric_api_deprecated"] -universal_graves_dependencies = ["sgui", "palceholder_api", "server_translations_api", "polymer_core", "polymer_resource_pack", "polymer_virtual_entity", "predicate_api", "common_protection_api", "fabric_permissions_api"] +universal_graves_runtime = ["universal_graves", "sgui", "palceholder_api", "server_translations_api", "polymer_core", "polymer_resource_pack", "polymer_virtual_entity", "predicate_api", "common_protection_api", "fabric_permissions_api"] [plugins] -quilt_loom = { id = "org.quiltmc.loom", version.ref = "quilt_loom" } -chenille = { id = "io.github.ladysnake.chenille", version.ref = "chenille" } +fabric_loom = { id = "fabric-loom", version.ref = "fabric_loom" } diff --git a/settings.gradle b/settings.gradle index 1ffcce3..3d38cc7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,14 +1,30 @@ pluginManagement { - repositories { - maven { - name = 'Quilt' - url = 'https://maven.quiltmc.org/repository/release' - } - // Currently needed for Intermediary and other temporary dependencies - maven { - name = 'Fabric' - url = 'https://maven.fabricmc.net/' - } - gradlePluginPortal() - } + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net' + } + gradlePluginPortal() + } +} + +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' +} + +rootProject.name = 'Soulbound' + +def ENV = System.getenv() + +buildCache { + remote(HttpBuildCache) { + url = "https://ci-cache.uuid.gg/cache" + if (ENV.CI && ENV.GRADLE_BUILD_CACHE_USER && ENV.GRADLE_BUILD_CACHE_TOKEN) { + push = true + credentials { + username = ENV.GRADLE_BUILD_CACHE_USER + password = ENV.GRADLE_BUILD_CACHE_TOKEN + } + } + } } diff --git a/src/main/generated/assets/soulbound/lang/en_us.json b/src/main/generated/assets/soulbound/lang/en_us.json new file mode 100644 index 0000000..03e8067 --- /dev/null +++ b/src/main/generated/assets/soulbound/lang/en_us.json @@ -0,0 +1,9 @@ +{ + "config.soulbound.soulbound_is_treasure_enchantment": "Soulbound is a Treasure-Enchantment", + "config.soulbound.soulbound_reduction_rate": "Chance to reduce Soulbound enchantment Level by 1", + "config.soulbound.soulbound_success_chance": "Chance to apply Soulbound enchantment effect", + "enchantment.soulbound.soulbound": "Soulbound", + "enchantment.soulbound.soulbound.desc": "Retains items upon death", + "tag.enchantment.soulbound.soulbound_incompatible_with": "Incompatible with Soulbound", + "tag.item.soulbound.soulbound_cannot_apply_to": "Cannot apply Soulbound enchantment" +} \ No newline at end of file diff --git a/src/main/resources/assets/soulbound/lang/zh_cn.json b/src/main/generated/assets/soulbound/lang/zh_cn.json similarity index 96% rename from src/main/resources/assets/soulbound/lang/zh_cn.json rename to src/main/generated/assets/soulbound/lang/zh_cn.json index 9898b26..a037900 100644 --- a/src/main/resources/assets/soulbound/lang/zh_cn.json +++ b/src/main/generated/assets/soulbound/lang/zh_cn.json @@ -1,3 +1,3 @@ { "enchantment.soulbound.soulbound": "灵魂绑定" -} +} \ No newline at end of file diff --git a/src/main/generated/data/soulbound/tags/enchantment/soulbound_incompatible_with.json b/src/main/generated/data/soulbound/tags/enchantment/soulbound_incompatible_with.json new file mode 100644 index 0000000..5e8aecc --- /dev/null +++ b/src/main/generated/data/soulbound/tags/enchantment/soulbound_incompatible_with.json @@ -0,0 +1,4 @@ +{ + "replace": false, + "values": [] +} \ No newline at end of file diff --git a/src/main/generated/data/soulbound/tags/items/soulbound_cannot_apply_to.json b/src/main/generated/data/soulbound/tags/items/soulbound_cannot_apply_to.json new file mode 100644 index 0000000..5e8aecc --- /dev/null +++ b/src/main/generated/data/soulbound/tags/items/soulbound_cannot_apply_to.json @@ -0,0 +1,4 @@ +{ + "replace": false, + "values": [] +} \ No newline at end of file diff --git a/src/main/java/dev/upcraft/soulbound/Soulbound.java b/src/main/java/dev/upcraft/soulbound/Soulbound.java index 85b5ff7..45f2f92 100644 --- a/src/main/java/dev/upcraft/soulbound/Soulbound.java +++ b/src/main/java/dev/upcraft/soulbound/Soulbound.java @@ -1,46 +1,38 @@ package dev.upcraft.soulbound; -import dev.upcraft.soulbound.api.SoulboundApi; +import com.teamresourceful.resourcefulconfig.common.config.Configurator; import dev.upcraft.soulbound.api.inventory.SoulboundContainer; -import dev.upcraft.soulbound.api.inventory.SoulboundContainerProvider; import dev.upcraft.soulbound.compat.SoulboundCompat; import dev.upcraft.soulbound.compat.trinkets.TrinketsIntegration; import dev.upcraft.soulbound.compat.universalgraves.UniversalGravesCompat; -import dev.upcraft.soulbound.core.SoulboundConfig; -import dev.upcraft.soulbound.core.SoulboundHooks; -import dev.upcraft.soulbound.core.SoulboundPersistentState; -import dev.upcraft.soulbound.core.inventory.PlayerInventoryContainer; -import dev.upcraft.soulbound.core.inventory.PlayerInventoryContainerProvider; import dev.upcraft.soulbound.init.SoulboundContainerProviders; import dev.upcraft.soulbound.init.SoulboundEnchantments; +import dev.upcraft.soulbound.init.SoulboundRegistries; import dev.upcraft.sparkweave.api.registry.RegistryService; -import eu.midnightdust.lib.config.MidnightConfig; -import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; -import net.fabricmc.fabric.api.event.registry.RegistryAttribute; -import net.minecraft.core.Registry; +import dev.upcraft.sparkweave.api.util.logging.SparkweaveLoggerFactory; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; -import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; -import org.quiltmc.loader.api.ModContainer; -import org.quiltmc.qsl.base.api.entrypoint.ModInitializer; -import org.quiltmc.qsl.entity.event.api.ServerPlayerEntityCopyCallback; import java.util.Map; public class Soulbound implements ModInitializer { - public static final String MODID = "soulbound"; - public static final Logger LOGGER = LogManager.getLogger("Soulbound"); - public static final SoulboundContainerProvider PLAYER_CONTAINER_PROVIDER = new PlayerInventoryContainerProvider(); - public static Registry> CONTAINER_PROVIDERS; + public static final String MODID = "soulbound"; + public static final Logger LOGGER = SparkweaveLoggerFactory.getLogger(); + public static final Configurator CONFIGURATOR = new Configurator(); - @Override - public void onInitialize(ModContainer mod) { - MidnightConfig.init(MODID, SoulboundConfig.class); - CONTAINER_PROVIDERS = FabricRegistryBuilder.createSimple(SoulboundApi.CONTAINER_PROVIDER_REGISTRY).attribute(RegistryAttribute.PERSISTED).buildAndRegister(); + public static ResourceLocation id(String path) { + return new ResourceLocation(MODID, path); + } + + @Override + public void onInitialize() { + CONFIGURATOR.registerConfig(SoulboundConfig.class); // this must run before the registry handlers SoulboundCompat.TRINKETS.ifEnabled(() -> TrinketsIntegration::load); @@ -50,21 +42,22 @@ public void onInitialize(ModContainer mod) { SoulboundEnchantments.ENCHANTMENTS.accept(service); SoulboundContainerProviders.CONTAINER_PROVIDERS.accept(service); - ServerPlayerEntityCopyCallback.EVENT.register((copy, original, wasDeath) -> { - if (wasDeath) { - SoulboundPersistentState persistentState = SoulboundPersistentState.get(copy); - Map saveData = persistentState.restorePlayer(original); - if (saveData != null) { - saveData.forEach((id, data) -> CONTAINER_PROVIDERS.getOptional(id).ifPresentOrElse(provider -> { - @Nullable SoulboundContainer container = provider.getContainer(copy); - if (container != null) { - container.restoreFromNbt(data, SoulboundHooks.createItemProcessor(container)); - } else { - Soulbound.LOGGER.warn("tried to deserialize null container for provider {}", id); - } - }, () -> Soulbound.LOGGER.error("tried to deserialize unknown provider {} for player {}", id, copy.getGameProfile().getName()))); - } - } - }); - } + + ServerPlayerEvents.COPY_FROM.register((oldPlayer, newPlayer, stillAlive) -> { + if (!stillAlive) { + SoulboundPersistentState persistentState = SoulboundPersistentState.get(newPlayer); + Map saveData = persistentState.restorePlayer(oldPlayer); + if (saveData != null) { + saveData.forEach((id, data) -> SoulboundRegistries.CONTAINER_PROVIDERS_REGISTRY.getOptional(id).ifPresentOrElse(provider -> { + @Nullable SoulboundContainer container = provider.getContainer(newPlayer); + if (container != null) { + container.restoreFromNbt(data, SoulboundHooks.createItemProcessor(container)); + } else { + Soulbound.LOGGER.warn("tried to deserialize null container for provider {}", id); + } + }, () -> Soulbound.LOGGER.error("tried to deserialize unknown provider {} for player {}", id, newPlayer.getGameProfile().getName()))); + } + } + }); + } } diff --git a/src/main/java/dev/upcraft/soulbound/SoulboundConfig.java b/src/main/java/dev/upcraft/soulbound/SoulboundConfig.java new file mode 100644 index 0000000..4bb55da --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/SoulboundConfig.java @@ -0,0 +1,21 @@ +package dev.upcraft.soulbound; + +import com.teamresourceful.resourcefulconfig.common.annotations.Config; +import com.teamresourceful.resourcefulconfig.common.annotations.ConfigEntry; +import com.teamresourceful.resourcefulconfig.common.annotations.DoubleRange; +import com.teamresourceful.resourcefulconfig.common.config.EntryType; + +@Config(Soulbound.MODID) +public final class SoulboundConfig { + + @DoubleRange(min = 0.0F, max = 1.0F) + @ConfigEntry(id = "soulbound_reduction_rate", type = EntryType.DOUBLE, translation = "config.soulbound.soulbound_reduction_rate") + public static double levelReductionRate = 0.0F; + + @DoubleRange(min = 0.0F, max = 1.0F) + @ConfigEntry(id = "soulbound_success_chance", type = EntryType.DOUBLE, translation = "config.soulbound.soulbound_success_chance") + public static double soulboundSuccessChance = 1.0F; + + @ConfigEntry(id = "soulbound_is_treasure_enchantment", type = EntryType.BOOLEAN, translation = "config.soulbound.soulbound_is_treasure_enchantment") + public static boolean soulboundIsTreasureEnchantment = false; +} diff --git a/src/main/java/dev/upcraft/soulbound/SoulboundHooks.java b/src/main/java/dev/upcraft/soulbound/SoulboundHooks.java new file mode 100644 index 0000000..699e9c8 --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/SoulboundHooks.java @@ -0,0 +1,121 @@ +package dev.upcraft.soulbound; + +import dev.upcraft.soulbound.api.event.SoulboundItemCallback; +import dev.upcraft.soulbound.api.inventory.SoulboundContainer; +import dev.upcraft.soulbound.init.SoulboundEnchantments; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.fabricmc.fabric.api.util.TriState; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; + +import java.util.function.UnaryOperator; + +public class SoulboundHooks { + + public static UnaryOperator createItemProcessor(SoulboundContainer container) { + RandomSource random = container.getEntity().getRandom(); + return stack -> { + SoulboundItemCallback.Context ctx = new SoulboundItemCallback.Context(container, stack.copy(), SoulboundConfig.levelReductionRate); + if (SoulboundItemCallback.EVENT.invoker().apply(ctx) != TriState.FALSE) { + ItemStack itemStack = ctx.getStack(); + + if (random.nextDouble() < ctx.getLevelReductionChance()) { + var ench = SoulboundEnchantments.SOULBOUND.get(); + var map = EnchantmentHelper.getEnchantments(itemStack); + int newLevel = map.getOrDefault(ench, 0) - 1; + if (newLevel > 0) { + map.put(ench, newLevel); + } else { + map.remove(ench); + } + EnchantmentHelper.setEnchantments(map, itemStack); + } + + return itemStack; + } + + return ItemStack.EMPTY; + }; + } + + public static ListTag getFilteredItemList(NonNullList items, RandomSource random) { + ListTag list = new ListTag(); + for (int i = 0; i < items.size(); i++) { + ItemStack stack = items.get(i); + if (shouldKeepStack(stack, random)) { + CompoundTag tag = new CompoundTag(); + tag.put("item", stack.save(new CompoundTag())); + tag.putInt("slot", i); + list.add(tag); + items.set(i, ItemStack.EMPTY); + } + } + return list; + } + + public static boolean shouldKeepStack(ItemStack stack, RandomSource random) { + return EnchantmentHelper.getItemEnchantmentLevel(SoulboundEnchantments.SOULBOUND.get(), stack) > 0 && random.nextDouble() < SoulboundConfig.soulboundSuccessChance; + } + + public static Int2ObjectMap readItemList(ListTag list) { + Int2ObjectMap value = new Int2ObjectOpenHashMap<>(); + for (int i = 0; i < list.size(); i++) { + CompoundTag tag = list.getCompound(i); + ItemStack stack = ItemStack.of(tag.getCompound("item")); + value.put(tag.getInt("slot"), stack); + } + return value; + } + + public static void processPlayerDrops(Player player, NonNullList targetInv, Int2ObjectMap items, UnaryOperator itemProcessor) { + items.int2ObjectEntrySet().forEach(e -> { + ItemStack stack = itemProcessor.apply(e.getValue().copy()); + if (stack.isEmpty()) { + ItemStack drop = e.getValue(); + drop.setCount(1); + ItemEntity itemEntity = player.drop(drop, false); + if (itemEntity != null) { + itemEntity.makeFakeItem(); + } + } else { + int slot = e.getIntKey(); + if (slot > 0 && slot < targetInv.size() && targetInv.get(slot).isEmpty()) { + targetInv.set(slot, stack); + } else { + addItemToPlayer(player, stack); + } + } + }); + } + + public static void addItemToPlayer(Player player, ItemStack stack) { + boolean inserted = player.getInventory().add(stack); + ItemEntity itemEntity; + if (inserted && stack.isEmpty()) { + stack.setCount(1); + itemEntity = player.drop(stack, false); + if (itemEntity != null) { + itemEntity.makeFakeItem(); + } + + player.level().playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.ITEM_PICKUP, SoundSource.PLAYERS, 0.2F, ((player.getRandom().nextFloat() - player.getRandom().nextFloat()) * 0.7F + 1.0F) * 2.0F); + player.containerMenu.broadcastChanges(); + } else { + itemEntity = player.drop(stack, false); + if (itemEntity != null) { + itemEntity.setNoPickUpDelay(); + itemEntity.setThrower(player.getUUID()); + } + } + } + +} diff --git a/src/main/java/dev/upcraft/soulbound/SoulboundPersistentState.java b/src/main/java/dev/upcraft/soulbound/SoulboundPersistentState.java new file mode 100644 index 0000000..6e4503f --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/SoulboundPersistentState.java @@ -0,0 +1,97 @@ +package dev.upcraft.soulbound; + +import com.google.common.collect.Maps; +import dev.upcraft.soulbound.api.inventory.SoulboundContainer; +import dev.upcraft.soulbound.init.SoulboundRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.saveddata.SavedData; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class SoulboundPersistentState extends SavedData { + + private static final String PERSISTENT_ID = "soulbound_persisted_items"; + private final Map> persistedData = Maps.newHashMap(); + + public static SoulboundPersistentState get(ServerPlayer player) { + return player.server.overworld().getDataStorage().computeIfAbsent(SoulboundPersistentState::fromNbt, SoulboundPersistentState::new, PERSISTENT_ID); + } + + private static SoulboundPersistentState fromNbt(CompoundTag tag) { + SoulboundPersistentState value = new SoulboundPersistentState(); + ListTag playerTags = tag.getList("players", Tag.TAG_COMPOUND); + for (int j = 0; j < playerTags.size(); j++) { + CompoundTag playerTag = playerTags.getCompound(j); + UUID uuid = playerTag.getUUID("uuid"); + Map map = new HashMap<>(); + ListTag inventories = playerTag.getList("inventories", Tag.TAG_COMPOUND); + for (int i = 0; i < inventories.size(); i++) { + CompoundTag inv = inventories.getCompound(i); + ResourceLocation id = new ResourceLocation(inv.getString("id")); + if (SoulboundRegistries.CONTAINER_PROVIDERS_REGISTRY.containsKey(id)) { + map.put(id, inv.getCompound("data")); + } else { + Soulbound.LOGGER.error("unable to read data for unknown provider {} for player {}", id, uuid); + } + } + value.persistedData.put(uuid, map); + } + return value; + } + + private SoulboundPersistentState() { + super(); + } + + public void storePlayer(ServerPlayer player) { + Map data = new HashMap<>(); + SoulboundRegistries.CONTAINER_PROVIDERS_REGISTRY.entrySet().forEach(entry -> { + var id = entry.getKey().location(); + var provider = entry.getValue(); + SoulboundContainer container = provider.getContainer(player); + if (container != null) { + CompoundTag containerData = new CompoundTag(); + container.storeToNbt(containerData); + if (!containerData.isEmpty()) { + data.put(id, containerData); + } + } + }); + this.persistedData.put(player.getGameProfile().getId(), data); + setDirty(); + } + + public Map restorePlayer(ServerPlayer player) { + Map value = persistedData.remove(player.getGameProfile().getId()); + if (value != null) { + setDirty(); + } + return value; + } + + @Override + public CompoundTag save(CompoundTag tag) { + ListTag playerTags = new ListTag(); + persistedData.forEach((uuid, map) -> { + CompoundTag playerTag = new CompoundTag(); + playerTag.putUUID("uuid", uuid); + ListTag inventories = new ListTag(); + map.forEach((identifier, data) -> { + CompoundTag inv = new CompoundTag(); + inv.putString("id", identifier.toString()); + inv.put("data", data); + inventories.add(inv); + }); + playerTag.put("inventories", inventories); + playerTags.add(playerTag); + }); + tag.put("players", playerTags); + return tag; + } +} diff --git a/src/main/java/dev/upcraft/soulbound/api/SoulboundApi.java b/src/main/java/dev/upcraft/soulbound/api/SoulboundApi.java index 0a8e2db..df09e09 100644 --- a/src/main/java/dev/upcraft/soulbound/api/SoulboundApi.java +++ b/src/main/java/dev/upcraft/soulbound/api/SoulboundApi.java @@ -1,12 +1,11 @@ package dev.upcraft.soulbound.api; -import dev.upcraft.soulbound.Soulbound; import dev.upcraft.soulbound.api.inventory.SoulboundContainerProvider; +import dev.upcraft.soulbound.init.SoulboundRegistries; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; public interface SoulboundApi { - ResourceKey>> CONTAINER_PROVIDER_REGISTRY = ResourceKey.createRegistryKey(new ResourceLocation(Soulbound.MODID, "container_provider")); + ResourceKey>> CONTAINER_PROVIDER_REGISTRY = SoulboundRegistries.CONTAINER_PROVIDERS; } diff --git a/src/main/java/dev/upcraft/soulbound/api/event/SoulboundFakePlayerCallback.java b/src/main/java/dev/upcraft/soulbound/api/event/SoulboundFakePlayerCallback.java deleted file mode 100644 index fc4d86b..0000000 --- a/src/main/java/dev/upcraft/soulbound/api/event/SoulboundFakePlayerCallback.java +++ /dev/null @@ -1,25 +0,0 @@ -package dev.upcraft.soulbound.api.event; - -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.server.level.ServerPlayer; - -import java.util.function.Predicate; - -public interface SoulboundFakePlayerCallback extends Predicate { - - Event EVENT = EventFactory.createArrayBacked(SoulboundFakePlayerCallback.class, listeners -> player -> { - for (SoulboundFakePlayerCallback listener : listeners) { - if (!listener.test(player)) { - return false; - } - } - return true; - }); - - /** - * @return whether the player is a fake player and should not apply soulbound enchantment effects - */ - @Override - boolean test(ServerPlayer player); -} diff --git a/src/main/java/dev/upcraft/soulbound/api/event/SoulboundItemCallback.java b/src/main/java/dev/upcraft/soulbound/api/event/SoulboundItemCallback.java index 39861fa..bea6fbe 100644 --- a/src/main/java/dev/upcraft/soulbound/api/event/SoulboundItemCallback.java +++ b/src/main/java/dev/upcraft/soulbound/api/event/SoulboundItemCallback.java @@ -3,53 +3,53 @@ import dev.upcraft.soulbound.api.inventory.SoulboundContainer; import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.EventFactory; +import net.fabricmc.fabric.api.util.TriState; import net.minecraft.world.item.ItemStack; -import org.quiltmc.qsl.base.api.util.TriState; public interface SoulboundItemCallback { - Event EVENT = EventFactory.createArrayBacked(SoulboundItemCallback.class, callbacks -> ctx -> { - for (SoulboundItemCallback callback : callbacks) { - TriState value = callback.apply(ctx); - if (value != TriState.DEFAULT) { - return value; - } - } - return TriState.DEFAULT; - }); - - TriState apply(Context ctx); - - class Context { - - final SoulboundContainer container; - ItemStack stack; - double levelPreservationChance; - - public Context(SoulboundContainer container, ItemStack stack, double levelPreservationChance) { - this.container = container; - this.stack = stack; - this.levelPreservationChance = levelPreservationChance; - } - - public SoulboundContainer getContainer() { - return container; - } - - public ItemStack getStack() { - return stack; - } - - public void setStack(ItemStack stack) { - this.stack = stack; - } - - public double getLevelPreservationChance() { - return levelPreservationChance; - } - - public void setLevelPreservationChance(double levelPreservationChance) { - this.levelPreservationChance = levelPreservationChance; - } - } + Event EVENT = EventFactory.createArrayBacked(SoulboundItemCallback.class, callbacks -> ctx -> { + for (SoulboundItemCallback callback : callbacks) { + TriState value = callback.apply(ctx); + if (value != TriState.DEFAULT) { + return value; + } + } + return TriState.DEFAULT; + }); + + TriState apply(Context ctx); + + class Context { + + private final SoulboundContainer container; + private ItemStack stack; + private double levelReductionChance; + + public Context(SoulboundContainer container, ItemStack stack, double levelReductionChance) { + this.container = container; + this.stack = stack; + this.levelReductionChance = levelReductionChance; + } + + public SoulboundContainer getContainer() { + return container; + } + + public ItemStack getStack() { + return stack; + } + + public void setStack(ItemStack stack) { + this.stack = stack; + } + + public double getLevelReductionChance() { + return levelReductionChance; + } + + public void setLevelReductionChance(double levelReductionChance) { + this.levelReductionChance = levelReductionChance; + } + } } diff --git a/src/main/java/dev/upcraft/soulbound/api/inventory/SoulboundContainer.java b/src/main/java/dev/upcraft/soulbound/api/inventory/SoulboundContainer.java index 5078c70..c706ec1 100644 --- a/src/main/java/dev/upcraft/soulbound/api/inventory/SoulboundContainer.java +++ b/src/main/java/dev/upcraft/soulbound/api/inventory/SoulboundContainer.java @@ -2,6 +2,7 @@ import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.RandomSource; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; @@ -9,11 +10,13 @@ public interface SoulboundContainer { - SoulboundContainerProvider getProvider(); + SoulboundContainerProvider getProvider(); - LivingEntity getEntity(); + LivingEntity getEntity(); - void storeToNbt(CompoundTag nbt); + RandomSource getRandom(); - void restoreFromNbt(CompoundTag nbt, UnaryOperator itemProcessor); + void storeToNbt(CompoundTag nbt); + + void restoreFromNbt(CompoundTag nbt, UnaryOperator itemProcessor); } diff --git a/src/main/java/dev/upcraft/soulbound/api/inventory/SoulboundContainerProvider.java b/src/main/java/dev/upcraft/soulbound/api/inventory/SoulboundContainerProvider.java index a05d02e..e1c82e6 100644 --- a/src/main/java/dev/upcraft/soulbound/api/inventory/SoulboundContainerProvider.java +++ b/src/main/java/dev/upcraft/soulbound/api/inventory/SoulboundContainerProvider.java @@ -5,5 +5,5 @@ public interface SoulboundContainerProvider { - @Nullable T getContainer(LivingEntity entity); + @Nullable T getContainer(LivingEntity entity); } diff --git a/src/main/java/dev/upcraft/soulbound/compat/SoulboundCompat.java b/src/main/java/dev/upcraft/soulbound/compat/SoulboundCompat.java index 9494807..b107dbc 100644 --- a/src/main/java/dev/upcraft/soulbound/compat/SoulboundCompat.java +++ b/src/main/java/dev/upcraft/soulbound/compat/SoulboundCompat.java @@ -4,6 +4,7 @@ public class SoulboundCompat { + public static final CompatHelper MODMENU = new CompatHelper("modmenu"); public static final CompatHelper TRINKETS = new CompatHelper("trinkets"); public static final CompatHelper UNIVERSAL_GRAVES = new CompatHelper("universal-graves"); } diff --git a/src/main/java/dev/upcraft/soulbound/compat/modmenu/ModmenuCompat.java b/src/main/java/dev/upcraft/soulbound/compat/modmenu/ModmenuCompat.java new file mode 100644 index 0000000..fa7ec38 --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/compat/modmenu/ModmenuCompat.java @@ -0,0 +1,20 @@ +package dev.upcraft.soulbound.compat.modmenu; + +import com.teamresourceful.resourcefulconfig.client.ConfigScreen; +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; +import dev.upcraft.soulbound.Soulbound; +import dev.upcraft.soulbound.SoulboundConfig; +import org.jetbrains.annotations.Nullable; + +public class ModmenuCompat implements ModMenuApi { + + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return parent -> { + @Nullable var config = Soulbound.CONFIGURATOR.getConfig(SoulboundConfig.class); + + return config != null ? new ConfigScreen(null, config) : null; + }; + } +} diff --git a/src/main/java/dev/upcraft/soulbound/compat/trinkets/DummyTrinketsContainerProvider.java b/src/main/java/dev/upcraft/soulbound/compat/trinkets/DummyTrinketsContainerProvider.java index fabd6d5..fb69c64 100644 --- a/src/main/java/dev/upcraft/soulbound/compat/trinkets/DummyTrinketsContainerProvider.java +++ b/src/main/java/dev/upcraft/soulbound/compat/trinkets/DummyTrinketsContainerProvider.java @@ -5,6 +5,7 @@ import dev.upcraft.soulbound.api.inventory.SoulboundContainer; import dev.upcraft.soulbound.api.inventory.SoulboundContainerProvider; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.RandomSource; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.Nullable; @@ -30,6 +31,11 @@ public LivingEntity getEntity() { return component().getEntity(); } + @Override + public RandomSource getRandom() { + return getEntity().getRandom(); + } + @Override public void storeToNbt(CompoundTag nbt) { // NO-OP diff --git a/src/main/java/dev/upcraft/soulbound/compat/trinkets/TrinketsIntegration.java b/src/main/java/dev/upcraft/soulbound/compat/trinkets/TrinketsIntegration.java index 65029fe..106e91a 100644 --- a/src/main/java/dev/upcraft/soulbound/compat/trinkets/TrinketsIntegration.java +++ b/src/main/java/dev/upcraft/soulbound/compat/trinkets/TrinketsIntegration.java @@ -4,7 +4,7 @@ import dev.emi.trinkets.api.event.TrinketDropCallback; import dev.upcraft.soulbound.api.inventory.SoulboundContainer; import dev.upcraft.soulbound.api.inventory.SoulboundContainerProvider; -import dev.upcraft.soulbound.core.SoulboundHooks; +import dev.upcraft.soulbound.SoulboundHooks; import dev.upcraft.soulbound.init.SoulboundContainerProviders; import dev.upcraft.sparkweave.api.registry.RegistrySupplier; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/dev/upcraft/soulbound/core/SoulboundConfig.java b/src/main/java/dev/upcraft/soulbound/core/SoulboundConfig.java deleted file mode 100644 index 28ec2d7..0000000 --- a/src/main/java/dev/upcraft/soulbound/core/SoulboundConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package dev.upcraft.soulbound.core; - -import eu.midnightdust.lib.config.MidnightConfig; - -public class SoulboundConfig extends MidnightConfig { - - @Entry(min = 0.0F, max = 1.0F) - public static double soulboundPreservationRate = 1.0F; - - @Entry(min = 0.0F, max = 1.0F) - public static double soulboundDropChance = 0.0F; - - @Entry - public static boolean soulboundIsTreasureEnchantment = false; -} diff --git a/src/main/java/dev/upcraft/soulbound/core/SoulboundHooks.java b/src/main/java/dev/upcraft/soulbound/core/SoulboundHooks.java deleted file mode 100644 index 4497906..0000000 --- a/src/main/java/dev/upcraft/soulbound/core/SoulboundHooks.java +++ /dev/null @@ -1,124 +0,0 @@ -package dev.upcraft.soulbound.core; - -import dev.upcraft.soulbound.api.event.SoulboundFakePlayerCallback; -import dev.upcraft.soulbound.api.event.SoulboundItemCallback; -import dev.upcraft.soulbound.api.inventory.SoulboundContainer; -import dev.upcraft.soulbound.init.SoulboundEnchantments; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.fabricmc.fabric.api.entity.FakePlayer; -import net.minecraft.core.NonNullList; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.RandomSource; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.enchantment.EnchantmentHelper; -import org.quiltmc.qsl.base.api.util.TriState; - -import java.util.function.UnaryOperator; - -public class SoulboundHooks { - - public static boolean isRealPlayer(ServerPlayer player) { - return !(player instanceof FakePlayer) && SoulboundFakePlayerCallback.EVENT.invoker().test(player); - } - - public static UnaryOperator createItemProcessor(SoulboundContainer container) { - RandomSource random = container.getEntity().getRandom(); - return stack -> { - SoulboundItemCallback.Context ctx = new SoulboundItemCallback.Context(container, stack, SoulboundConfig.soulboundPreservationRate); - if (SoulboundItemCallback.EVENT.invoker().apply(ctx) != TriState.FALSE) { - ItemStack itemStack = ctx.getStack(); - if (ctx.getLevelPreservationChance() < random.nextDouble()) { - var map = EnchantmentHelper.getEnchantments(itemStack); - int newLevel = map.getOrDefault(SoulboundEnchantments.SOULBOUND.get(), 0) - 1; - if (newLevel > 0) { - map.put(SoulboundEnchantments.SOULBOUND.get(), newLevel); - } else { - map.remove(SoulboundEnchantments.SOULBOUND.get()); - } - EnchantmentHelper.setEnchantments(map, itemStack); - } - return itemStack; - } - return ItemStack.EMPTY; - }; - } - - public static ListTag getFilteredItemList(NonNullList items, RandomSource random) { - ListTag list = new ListTag(); - for (int i = 0; i < items.size(); i++) { - ItemStack stack = items.get(i); - if (shouldKeepStack(stack, random)) { - CompoundTag tag = new CompoundTag(); - tag.put("item", stack.save(new CompoundTag())); - tag.putInt("slot", i); - list.add(tag); - items.set(i, ItemStack.EMPTY); - } - } - return list; - } - - public static boolean shouldKeepStack(ItemStack stack, RandomSource random) { - return EnchantmentHelper.getItemEnchantmentLevel(SoulboundEnchantments.SOULBOUND.get(), stack) > 0 && SoulboundConfig.soulboundDropChance < random.nextDouble(); - } - - public static Int2ObjectMap readItemList(ListTag list) { - Int2ObjectMap value = new Int2ObjectOpenHashMap<>(); - for (int i = 0; i < list.size(); i++) { - CompoundTag tag = list.getCompound(i); - ItemStack stack = ItemStack.of(tag.getCompound("item")); - value.put(tag.getInt("slot"), stack); - } - return value; - } - - public static void processPlayerDrops(Player player, NonNullList targetInv, Int2ObjectMap items, UnaryOperator itemProcessor) { - items.int2ObjectEntrySet().forEach(e -> { - ItemStack stack = itemProcessor.apply(e.getValue().copy()); - if (stack.isEmpty()) { - ItemStack drop = e.getValue(); - drop.setCount(1); - ItemEntity itemEntity = player.drop(drop, false); - if (itemEntity != null) { - itemEntity.makeFakeItem(); - } - } else { - int slot = e.getIntKey(); - if (slot > 0 && slot < targetInv.size() && targetInv.get(slot).isEmpty()) { - targetInv.set(slot, stack); - } else { - addItemToPlayer(player, stack); - } - } - }); - } - - public static void addItemToPlayer(Player player, ItemStack stack) { - boolean inserted = player.getInventory().add(stack); - ItemEntity itemEntity; - if (inserted && stack.isEmpty()) { - stack.setCount(1); - itemEntity = player.drop(stack, false); - if (itemEntity != null) { - itemEntity.makeFakeItem(); - } - - player.level().playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.ITEM_PICKUP, SoundSource.PLAYERS, 0.2F, ((player.getRandom().nextFloat() - player.getRandom().nextFloat()) * 0.7F + 1.0F) * 2.0F); - player.containerMenu.broadcastChanges(); - } else { - itemEntity = player.drop(stack, false); - if (itemEntity != null) { - itemEntity.setNoPickUpDelay(); - itemEntity.setThrower(player.getUUID()); - } - } - } - -} diff --git a/src/main/java/dev/upcraft/soulbound/core/SoulboundPersistentState.java b/src/main/java/dev/upcraft/soulbound/core/SoulboundPersistentState.java deleted file mode 100644 index 57e3ae2..0000000 --- a/src/main/java/dev/upcraft/soulbound/core/SoulboundPersistentState.java +++ /dev/null @@ -1,97 +0,0 @@ -package dev.upcraft.soulbound.core; - -import com.google.common.collect.Maps; -import dev.upcraft.soulbound.Soulbound; -import dev.upcraft.soulbound.api.inventory.SoulboundContainer; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.saveddata.SavedData; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -public class SoulboundPersistentState extends SavedData { - - private static final String PERSISTENT_ID = "soulbound_persisted_items"; - private final Map> persistedData = Maps.newHashMap(); - - public static SoulboundPersistentState get(ServerPlayer player) { - return player.server.overworld().getDataStorage().computeIfAbsent(SoulboundPersistentState::fromNbt, SoulboundPersistentState::new, PERSISTENT_ID); - } - - private static SoulboundPersistentState fromNbt(CompoundTag tag) { - SoulboundPersistentState value = new SoulboundPersistentState(); - ListTag playerTags = tag.getList("players", Tag.TAG_COMPOUND); - for (int j = 0; j < playerTags.size(); j++) { - CompoundTag playerTag = playerTags.getCompound(j); - UUID uuid = playerTag.getUUID("uuid"); - Map map = new HashMap<>(); - ListTag inventories = playerTag.getList("inventories", Tag.TAG_COMPOUND); - for (int i = 0; i < inventories.size(); i++) { - CompoundTag inv = inventories.getCompound(i); - ResourceLocation id = new ResourceLocation(inv.getString("id")); - if (Soulbound.CONTAINER_PROVIDERS.containsKey(id)) { - map.put(id, inv.getCompound("data")); - } else { - Soulbound.LOGGER.error("unable to read data for unknown provider {} for player {}", id, uuid); - } - } - value.persistedData.put(uuid, map); - } - return value; - } - - private SoulboundPersistentState() { - super(); - } - - public void storePlayer(ServerPlayer player) { - Map data = new HashMap<>(); - Soulbound.CONTAINER_PROVIDERS.entrySet().forEach(entry -> { - var id = entry.getKey().location(); - var provider = entry.getValue(); - SoulboundContainer container = provider.getContainer(player); - if (container != null) { - CompoundTag containerData = new CompoundTag(); - container.storeToNbt(containerData); - if (!containerData.isEmpty()) { - data.put(id, containerData); - } - } - }); - this.persistedData.put(player.getGameProfile().getId(), data); - setDirty(); - } - - public Map restorePlayer(ServerPlayer player) { - Map value = persistedData.remove(player.getGameProfile().getId()); - if (value != null) { - setDirty(); - } - return value; - } - - @Override - public CompoundTag save(CompoundTag tag) { - ListTag playerTags = new ListTag(); - persistedData.forEach((uuid, map) -> { - CompoundTag playerTag = new CompoundTag(); - playerTag.putUUID("uuid", uuid); - ListTag inventories = new ListTag(); - map.forEach((identifier, data) -> { - CompoundTag inv = new CompoundTag(); - inv.putString("id", identifier.toString()); - inv.put("data", data); - inventories.add(inv); - }); - playerTag.put("inventories", inventories); - playerTags.add(playerTag); - }); - tag.put("players", playerTags); - return tag; - } -} diff --git a/src/main/java/dev/upcraft/soulbound/core/mixin/PlayerMixin.java b/src/main/java/dev/upcraft/soulbound/core/mixin/PlayerMixin.java deleted file mode 100644 index 77b597e..0000000 --- a/src/main/java/dev/upcraft/soulbound/core/mixin/PlayerMixin.java +++ /dev/null @@ -1,68 +0,0 @@ -package dev.upcraft.soulbound.core.mixin; - -import dev.upcraft.soulbound.Soulbound; -import dev.upcraft.soulbound.api.inventory.SoulboundContainer; -import dev.upcraft.soulbound.api.inventory.SoulboundContainerProvider; -import dev.upcraft.soulbound.core.SoulboundHooks; -import dev.upcraft.soulbound.core.SoulboundPersistentState; -import dev.upcraft.soulbound.core.inventory.PlayerInventoryContainer; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import org.spongepowered.asm.mixin.Implements; -import org.spongepowered.asm.mixin.Interface; -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 java.util.function.UnaryOperator; - -@Mixin(value = Player.class, priority = 6969) -@Implements(@Interface(iface = PlayerInventoryContainer.class, prefix = "sb$")) -public abstract class PlayerMixin extends LivingEntity { - - @Shadow - public abstract Inventory getInventory(); - - private PlayerMixin(EntityType entityType, Level level) { - super(entityType, level); - throw new UnsupportedOperationException("mixin not transformed"); - } - - @Inject(method = "dropEquipment", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Inventory;dropAll()V")) - private void soulbound_dropInventory(CallbackInfo callbackInfo) { - //noinspection ConstantConditions - if ((Object) this instanceof ServerPlayer serverPlayer && SoulboundHooks.isRealPlayer(serverPlayer)) { - SoulboundPersistentState persistentState = SoulboundPersistentState.get(serverPlayer); - persistentState.storePlayer(serverPlayer); - } - } - - public SoulboundContainerProvider sb$getProvider() { - return Soulbound.PLAYER_CONTAINER_PROVIDER; - } - - public LivingEntity sb$getEntity() { - return this; - } - - public void sb$storeToNbt(CompoundTag nbt) { - nbt.put("main", SoulboundHooks.getFilteredItemList(this.getInventory().items, this.getRandom())); - nbt.put("off_hand", SoulboundHooks.getFilteredItemList(this.getInventory().offhand, this.getRandom())); - nbt.put("armor", SoulboundHooks.getFilteredItemList(this.getInventory().armor, this.getRandom())); - } - - public void sb$restoreFromNbt(CompoundTag nbt, UnaryOperator itemProcessor) { - SoulboundHooks.processPlayerDrops((Player) (Object) this, this.getInventory().items, SoulboundHooks.readItemList(nbt.getList("main", Tag.TAG_COMPOUND)), itemProcessor); - SoulboundHooks.processPlayerDrops((Player) (Object) this, this.getInventory().offhand, SoulboundHooks.readItemList(nbt.getList("off_hand", Tag.TAG_COMPOUND)), itemProcessor); - SoulboundHooks.processPlayerDrops((Player) (Object) this, this.getInventory().armor, SoulboundHooks.readItemList(nbt.getList("armor", Tag.TAG_COMPOUND)), itemProcessor); - } -} diff --git a/src/main/java/dev/upcraft/soulbound/data/SoulboundEnchantmentTags.java b/src/main/java/dev/upcraft/soulbound/data/SoulboundEnchantmentTags.java new file mode 100644 index 0000000..f8e3122 --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/data/SoulboundEnchantmentTags.java @@ -0,0 +1,11 @@ +package dev.upcraft.soulbound.data; + +import dev.upcraft.soulbound.Soulbound; +import net.minecraft.core.registries.Registries; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.enchantment.Enchantment; + +public class SoulboundEnchantmentTags { + + public static final TagKey SOULBOUND_INCOMPATIBLE_WITH = TagKey.create(Registries.ENCHANTMENT, Soulbound.id("soulbound_incompatible_with")); +} diff --git a/src/main/java/dev/upcraft/soulbound/data/SoulboundItemTags.java b/src/main/java/dev/upcraft/soulbound/data/SoulboundItemTags.java new file mode 100644 index 0000000..2b06a15 --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/data/SoulboundItemTags.java @@ -0,0 +1,11 @@ +package dev.upcraft.soulbound.data; + +import dev.upcraft.soulbound.Soulbound; +import net.minecraft.core.registries.Registries; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; + +public class SoulboundItemTags { + + public static final TagKey SOULBOUND_CANNOT_APPLY_TO = TagKey.create(Registries.ITEM, Soulbound.id("soulbound_cannot_apply_to")); +} diff --git a/src/main/java/dev/upcraft/soulbound/datagen/SoulboundDataGenerator.java b/src/main/java/dev/upcraft/soulbound/datagen/SoulboundDataGenerator.java new file mode 100644 index 0000000..2d26260 --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/datagen/SoulboundDataGenerator.java @@ -0,0 +1,28 @@ +package dev.upcraft.soulbound.datagen; + +import dev.upcraft.soulbound.datagen.client.SoulboundChineseLanguageProvider; +import dev.upcraft.soulbound.datagen.client.SoulboundEnglishLanguageProvider; +import dev.upcraft.soulbound.datagen.common.SoulboundEnchantmentTagsProvider; +import dev.upcraft.soulbound.datagen.common.SoulboundItemTagsProvider; +import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; +import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; +import net.minecraft.core.RegistrySetBuilder; + +public class SoulboundDataGenerator implements DataGeneratorEntrypoint { + + @Override + public void onInitializeDataGenerator(FabricDataGenerator generator) { + var pack = generator.createPack(); + + pack.addProvider(SoulboundItemTagsProvider::new); + pack.addProvider(SoulboundEnchantmentTagsProvider::new); + + pack.addProvider(SoulboundEnglishLanguageProvider::new); + pack.addProvider(SoulboundChineseLanguageProvider::new); + } + + @Override + public void buildRegistry(RegistrySetBuilder registryBuilder) { + DataGeneratorEntrypoint.super.buildRegistry(registryBuilder); + } +} diff --git a/src/main/java/dev/upcraft/soulbound/datagen/client/SoulboundChineseLanguageProvider.java b/src/main/java/dev/upcraft/soulbound/datagen/client/SoulboundChineseLanguageProvider.java new file mode 100644 index 0000000..88d901a --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/datagen/client/SoulboundChineseLanguageProvider.java @@ -0,0 +1,17 @@ +package dev.upcraft.soulbound.datagen.client; + +import dev.upcraft.soulbound.init.SoulboundEnchantments; +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider; + +public class SoulboundChineseLanguageProvider extends FabricLanguageProvider { + + public SoulboundChineseLanguageProvider(FabricDataOutput dataOutput) { + super(dataOutput, "zh_cn"); + } + + @Override + public void generateTranslations(TranslationBuilder translationBuilder) { + translationBuilder.add(SoulboundEnchantments.SOULBOUND.get(), "灵魂绑定"); + } +} diff --git a/src/main/java/dev/upcraft/soulbound/datagen/client/SoulboundEnglishLanguageProvider.java b/src/main/java/dev/upcraft/soulbound/datagen/client/SoulboundEnglishLanguageProvider.java new file mode 100644 index 0000000..4f505df --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/datagen/client/SoulboundEnglishLanguageProvider.java @@ -0,0 +1,37 @@ +package dev.upcraft.soulbound.datagen.client; + +import dev.upcraft.soulbound.data.SoulboundEnchantmentTags; +import dev.upcraft.soulbound.data.SoulboundItemTags; +import dev.upcraft.soulbound.init.SoulboundEnchantments; +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider; +import net.minecraft.Util; +import net.minecraft.locale.Language; +import net.minecraft.tags.TagKey; + +public class SoulboundEnglishLanguageProvider extends FabricLanguageProvider { + + public SoulboundEnglishLanguageProvider(FabricDataOutput dataOutput) { + super(dataOutput, Language.DEFAULT); + } + + @Override + public void generateTranslations(TranslationBuilder translationBuilder) { + + translationBuilder.add(SoulboundEnchantments.SOULBOUND.get(), "Soulbound"); + translationBuilder.add(SoulboundEnchantments.SOULBOUND.get().getDescriptionId() + ".desc", "Retains items upon death"); + + tag(translationBuilder, SoulboundItemTags.SOULBOUND_CANNOT_APPLY_TO, "Cannot apply Soulbound enchantment"); + tag(translationBuilder, SoulboundEnchantmentTags.SOULBOUND_INCOMPATIBLE_WITH, "Incompatible with Soulbound"); + + translationBuilder.add("config.soulbound.soulbound_reduction_rate", "Chance to reduce Soulbound enchantment Level by 1"); + translationBuilder.add("config.soulbound.soulbound_success_chance", "Chance to apply Soulbound enchantment effect"); + translationBuilder.add("config.soulbound.soulbound_is_treasure_enchantment", "Soulbound is a Treasure-Enchantment"); + } + + public static void tag(TranslationBuilder builder, TagKey tag, String translation) { + var registryName = tag.registry().location().toShortLanguageKey().replace('/', '.'); + var tagName = Util.makeDescriptionId("tag." + registryName, tag.location()); + builder.add(tagName, translation); + } +} diff --git a/src/main/java/dev/upcraft/soulbound/datagen/common/SoulboundEnchantmentTagsProvider.java b/src/main/java/dev/upcraft/soulbound/datagen/common/SoulboundEnchantmentTagsProvider.java new file mode 100644 index 0000000..6948a08 --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/datagen/common/SoulboundEnchantmentTagsProvider.java @@ -0,0 +1,20 @@ +package dev.upcraft.soulbound.datagen.common; + +import dev.upcraft.soulbound.data.SoulboundEnchantmentTags; +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider; +import net.minecraft.core.HolderLookup; + +import java.util.concurrent.CompletableFuture; + +public class SoulboundEnchantmentTagsProvider extends FabricTagProvider.EnchantmentTagProvider { + + public SoulboundEnchantmentTagsProvider(FabricDataOutput output, CompletableFuture completableFuture) { + super(output, completableFuture); + } + + @Override + protected void addTags(HolderLookup.Provider arg) { + getOrCreateRawBuilder(SoulboundEnchantmentTags.SOULBOUND_INCOMPATIBLE_WITH); + } +} diff --git a/src/main/java/dev/upcraft/soulbound/datagen/common/SoulboundItemTagsProvider.java b/src/main/java/dev/upcraft/soulbound/datagen/common/SoulboundItemTagsProvider.java new file mode 100644 index 0000000..ed5bab4 --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/datagen/common/SoulboundItemTagsProvider.java @@ -0,0 +1,20 @@ +package dev.upcraft.soulbound.datagen.common; + +import dev.upcraft.soulbound.data.SoulboundItemTags; +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider; +import net.minecraft.core.HolderLookup; + +import java.util.concurrent.CompletableFuture; + +public class SoulboundItemTagsProvider extends FabricTagProvider.ItemTagProvider { + + public SoulboundItemTagsProvider(FabricDataOutput output, CompletableFuture completableFuture) { + super(output, completableFuture); + } + + @Override + protected void addTags(HolderLookup.Provider arg) { + getOrCreateRawBuilder(SoulboundItemTags.SOULBOUND_CANNOT_APPLY_TO); + } +} diff --git a/src/main/java/dev/upcraft/soulbound/enchantment/SoulboundEnchantment.java b/src/main/java/dev/upcraft/soulbound/enchantment/SoulboundEnchantment.java index 9749d44..fd8150e 100644 --- a/src/main/java/dev/upcraft/soulbound/enchantment/SoulboundEnchantment.java +++ b/src/main/java/dev/upcraft/soulbound/enchantment/SoulboundEnchantment.java @@ -1,7 +1,7 @@ package dev.upcraft.soulbound.enchantment; -import dev.upcraft.soulbound.core.SoulboundConfig; +import dev.upcraft.soulbound.SoulboundConfig; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.enchantment.Enchantment; @@ -10,38 +10,38 @@ public class SoulboundEnchantment extends Enchantment { - public SoulboundEnchantment() { - // Anybody who directly checks the target field is bad - super(Rarity.VERY_RARE, EnchantmentCategory.BREAKABLE, EquipmentSlot.values()); - } + public SoulboundEnchantment() { + // Anybody who directly checks the target field is bad + super(Rarity.VERY_RARE, EnchantmentCategory.BREAKABLE, EquipmentSlot.values()); + } - @Override - public int getMaxLevel() { - return 1; - } + @Override + public int getMaxLevel() { + return 1; + } - @Override - public int getMinCost(int level) { - return 15; - } + @Override + public int getMinCost(int level) { + return 15; + } - @Override - public int getMaxCost(int level) { - return super.getMaxCost(level) + 50; - } + @Override + public int getMaxCost(int level) { + return super.getMaxCost(level) + 50; + } @Override - protected boolean checkCompatibility(Enchantment other) { - return super.checkCompatibility(other) && other != Enchantments.VANISHING_CURSE && other != Enchantments.BINDING_CURSE; - } + protected boolean checkCompatibility(Enchantment other) { + return super.checkCompatibility(other) && other != Enchantments.VANISHING_CURSE && other != Enchantments.BINDING_CURSE; + } - @Override - public boolean canEnchant(ItemStack stack) { - return true; - } + @Override + public boolean canEnchant(ItemStack stack) { + return true; + } @Override - public boolean isTreasureOnly() { - return SoulboundConfig.soulboundIsTreasureEnchantment; - } + public boolean isTreasureOnly() { + return SoulboundConfig.soulboundIsTreasureEnchantment; + } } diff --git a/src/main/java/dev/upcraft/soulbound/init/SoulboundContainerProviders.java b/src/main/java/dev/upcraft/soulbound/init/SoulboundContainerProviders.java index 07f420e..06eb94e 100644 --- a/src/main/java/dev/upcraft/soulbound/init/SoulboundContainerProviders.java +++ b/src/main/java/dev/upcraft/soulbound/init/SoulboundContainerProviders.java @@ -2,8 +2,8 @@ import dev.upcraft.soulbound.api.SoulboundApi; import dev.upcraft.soulbound.api.inventory.SoulboundContainerProvider; -import dev.upcraft.soulbound.core.inventory.PlayerInventoryContainer; -import dev.upcraft.soulbound.core.inventory.PlayerInventoryContainerProvider; +import dev.upcraft.soulbound.inventory.PlayerInventoryContainer; +import dev.upcraft.soulbound.inventory.PlayerInventoryContainerProvider; import dev.upcraft.sparkweave.api.registry.RegistryHandler; import dev.upcraft.sparkweave.api.registry.RegistrySupplier; diff --git a/src/main/java/dev/upcraft/soulbound/init/SoulboundRegistries.java b/src/main/java/dev/upcraft/soulbound/init/SoulboundRegistries.java new file mode 100644 index 0000000..853a16b --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/init/SoulboundRegistries.java @@ -0,0 +1,14 @@ +package dev.upcraft.soulbound.init; + +import dev.upcraft.soulbound.Soulbound; +import dev.upcraft.soulbound.api.inventory.SoulboundContainerProvider; +import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; +import net.fabricmc.fabric.api.event.registry.RegistryAttribute; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; + +public class SoulboundRegistries { + + public static final ResourceKey>> CONTAINER_PROVIDERS = ResourceKey.createRegistryKey(Soulbound.id("container_provider")); + public static final Registry> CONTAINER_PROVIDERS_REGISTRY = FabricRegistryBuilder.createSimple(CONTAINER_PROVIDERS).attribute(RegistryAttribute.PERSISTED).buildAndRegister(); +} diff --git a/src/main/java/dev/upcraft/soulbound/core/inventory/PlayerInventoryContainer.java b/src/main/java/dev/upcraft/soulbound/inventory/PlayerInventoryContainer.java similarity index 75% rename from src/main/java/dev/upcraft/soulbound/core/inventory/PlayerInventoryContainer.java rename to src/main/java/dev/upcraft/soulbound/inventory/PlayerInventoryContainer.java index 89f4108..ae09625 100644 --- a/src/main/java/dev/upcraft/soulbound/core/inventory/PlayerInventoryContainer.java +++ b/src/main/java/dev/upcraft/soulbound/inventory/PlayerInventoryContainer.java @@ -1,4 +1,4 @@ -package dev.upcraft.soulbound.core.inventory; +package dev.upcraft.soulbound.inventory; import dev.upcraft.soulbound.api.inventory.SoulboundContainer; diff --git a/src/main/java/dev/upcraft/soulbound/core/inventory/PlayerInventoryContainerProvider.java b/src/main/java/dev/upcraft/soulbound/inventory/PlayerInventoryContainerProvider.java similarity index 52% rename from src/main/java/dev/upcraft/soulbound/core/inventory/PlayerInventoryContainerProvider.java rename to src/main/java/dev/upcraft/soulbound/inventory/PlayerInventoryContainerProvider.java index a7883a8..65af67a 100644 --- a/src/main/java/dev/upcraft/soulbound/core/inventory/PlayerInventoryContainerProvider.java +++ b/src/main/java/dev/upcraft/soulbound/inventory/PlayerInventoryContainerProvider.java @@ -1,12 +1,12 @@ -package dev.upcraft.soulbound.core.inventory; +package dev.upcraft.soulbound.inventory; import dev.upcraft.soulbound.api.inventory.SoulboundContainerProvider; import net.minecraft.world.entity.LivingEntity; import org.jetbrains.annotations.Nullable; public class PlayerInventoryContainerProvider implements SoulboundContainerProvider { - @Override - public @Nullable PlayerInventoryContainer getContainer(LivingEntity entity) { - return entity instanceof PlayerInventoryContainer ? (PlayerInventoryContainer) entity : null; - } + @Override + public @Nullable PlayerInventoryContainer getContainer(LivingEntity entity) { + return entity instanceof PlayerInventoryContainer ? (PlayerInventoryContainer) entity : null; + } } diff --git a/src/main/java/dev/upcraft/soulbound/mixin/PlayerMixin.java b/src/main/java/dev/upcraft/soulbound/mixin/PlayerMixin.java new file mode 100644 index 0000000..7ac313e --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/mixin/PlayerMixin.java @@ -0,0 +1,75 @@ +package dev.upcraft.soulbound.mixin; + +import dev.upcraft.soulbound.api.inventory.SoulboundContainer; +import dev.upcraft.soulbound.api.inventory.SoulboundContainerProvider; +import dev.upcraft.soulbound.SoulboundHooks; +import dev.upcraft.soulbound.SoulboundPersistentState; +import dev.upcraft.soulbound.inventory.PlayerInventoryContainer; +import dev.upcraft.soulbound.init.SoulboundContainerProviders; +import dev.upcraft.sparkweave.api.util.fakeplayer.FakePlayerHelper; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import org.spongepowered.asm.mixin.Implements; +import org.spongepowered.asm.mixin.Interface; +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 java.util.function.UnaryOperator; + +@Mixin(value = Player.class, priority = 6969) +@Implements(@Interface(iface = PlayerInventoryContainer.class, prefix = "sb$")) +public abstract class PlayerMixin extends LivingEntity { + + private PlayerMixin(EntityType entityType, Level level) { + super(entityType, level); + throw new UnsupportedOperationException("mixin not transformed"); + } + + @Inject(method = "dropEquipment", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/player/Inventory;dropAll()V")) + private void soulbound_dropInventory(CallbackInfo callbackInfo) { + //noinspection ConstantConditions + if ((Object) this instanceof ServerPlayer serverPlayer && !FakePlayerHelper.isFakePlayer(serverPlayer)) { + SoulboundPersistentState persistentState = SoulboundPersistentState.get(serverPlayer); + persistentState.storePlayer(serverPlayer); + } + } + + public SoulboundContainerProvider sb$getProvider() { + return SoulboundContainerProviders.PLAYER_INVENTORY.get(); + } + + public LivingEntity sb$getEntity() { + return this; + } + + public RandomSource sb$getRandom() { + return this.random; + } + + public void sb$storeToNbt(CompoundTag nbt) { + nbt.put("main", SoulboundHooks.getFilteredItemList(this.getInventory().items, this.getRandom())); + nbt.put("off_hand", SoulboundHooks.getFilteredItemList(this.getInventory().offhand, this.getRandom())); + nbt.put("armor", SoulboundHooks.getFilteredItemList(this.getInventory().armor, this.getRandom())); + } + + @Shadow + public abstract Inventory getInventory(); + + public void sb$restoreFromNbt(CompoundTag nbt, UnaryOperator itemProcessor) { + var self = (Player) (Object) this; + SoulboundHooks.processPlayerDrops(self, this.getInventory().items, SoulboundHooks.readItemList(nbt.getList("main", Tag.TAG_COMPOUND)), itemProcessor); + SoulboundHooks.processPlayerDrops(self, this.getInventory().offhand, SoulboundHooks.readItemList(nbt.getList("off_hand", Tag.TAG_COMPOUND)), itemProcessor); + SoulboundHooks.processPlayerDrops(self, this.getInventory().armor, SoulboundHooks.readItemList(nbt.getList("armor", Tag.TAG_COMPOUND)), itemProcessor); + } +} diff --git a/src/main/java/dev/upcraft/soulbound/package-info.java b/src/main/java/dev/upcraft/soulbound/package-info.java new file mode 100644 index 0000000..5a96ee4 --- /dev/null +++ b/src/main/java/dev/upcraft/soulbound/package-info.java @@ -0,0 +1,4 @@ +@Mod.Context(Soulbound.MODID) +package dev.upcraft.soulbound; + +import dev.upcraft.sparkweave.api.annotation.Mod; diff --git a/src/main/resources/assets/soulbound/lang/en_us.json b/src/main/resources/assets/soulbound/lang/en_us.json deleted file mode 100644 index 7a4b4e1..0000000 --- a/src/main/resources/assets/soulbound/lang/en_us.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "enchantment.soulbound.soulbound": "Soulbound", - "enchantment.soulbound.soulbound.desc": "Retains items upon death", - "soulbound.midnightconfig.title": "Soulbound", - "soulbound.midnightconfig.soulboundPreservationRate": "Soulbound Enchantment Preservation Rate", - "soulbound.midnightconfig.soulboundDropChance": "Item Drop Chance", - "soulbound.midnightconfig.soulboundIsTreasureEnchantment": "Soulbound is a Treasure-Enchantment" -} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..45c4161 --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,71 @@ +{ + "schemaVersion": 1, + "id": "${mod_id}", + "version": "${version}", + "name": "${mod_display_name}", + "description": "${mod_description}", + "authors": [ + "Up" + ], + "contributors": [ + "TehNut" + ], + "contact": { + "homepage": "${homepage_url}", + "sources": "${sources_url}", + "issues": "${issues_url}" + }, + "license": "${license_url}", + "icon": "icon.png", + "environment": "*", + "custom": { + "mc-publish": { + "curseforge": "618682", + "modrinth": "9QyjzbTh", + "dependencies": [ + "fabric-api(required){curseforge:306612}{modrinth:P7dR8mSH}", + "resourcefulconfig(required){modrinth:M1953qlQ}{curseforge:714059}", + "sparkweave(required){modrinth:nf68xfAw}{curseforge:911456}" + ], + "loaders": [ + "fabric", + "quilt" + ], + "java": [ + "${java_version}" + ] + }, + "modmenu": { + "links": { + "modmenu.discord": "${discord_url}" + } + }, + "loom:injected_interfaces": { + } + }, + "depends": { + "fabricloader": ">=${fabric_loader_version}", + "minecraft": "=${minecraft_version}", + "java": ">=${java_version}", + "fabric-api": "*", + "resourcefulconfig": "*", + "sparkweave": ">=0.7.0 <0.100.0" + }, + "entrypoints": { + "main": [ + "dev.upcraft.soulbound.Soulbound" + ], + "client": [ + "dev.upcraft.sparkweave.entrypoint.Client" + ], + "fabric-datagen": [ + "dev.upcraft.soulbound.datagen.SoulboundDataGenerator" + ], + "modmenu": [ + "dev.upcraft.soulbound.compat.modmenu.ModmenuCompat" + ] + }, + "mixins": [ + "${mod_id}.mixins.json" + ] +} diff --git a/src/main/resources/assets/soulbound/icon.png b/src/main/resources/icon.png similarity index 100% rename from src/main/resources/assets/soulbound/icon.png rename to src/main/resources/icon.png diff --git a/src/main/resources/quilt.mod.json b/src/main/resources/quilt.mod.json deleted file mode 100644 index fbd32a4..0000000 --- a/src/main/resources/quilt.mod.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "schema_version": 1, - "quilt_loader": { - "group": "dev.upcraft", - "id": "soulbound", - "version": "${version}", - "metadata": { - "name": "Soulbound", - "description": "An enchantment that will be retain items upon death", - "contributors": { - "Up": "Owner", - "TehNut": "Former Author" - }, - "contact": { - "homepage": "https://upcraft.dev", - "sources": "https://github.com/Up-Mods/Soulbound", - "issues": "https://github.com/Up-Mods/Soulbound/issues" - }, - "icon": "assets/soulbound/icon.png" - }, - "intermediate_mappings": "net.fabricmc:intermediary", - "entrypoints": { - "init": [ - "dev.upcraft.soulbound.Soulbound" - ], - "client_init": [ - "dev.upcraft.sparkweave.entrypoint.Client" - ], - "pre_launch": [ - "dev.upcraft.sparkweave.entrypoint.PreLaunch" - ] - }, - "depends": [ - { - "id": "quilt_loader", - "versions": ">=0.20.0-" - }, - { - "id": "quilted_fabric_api", - "versions": ">=5.0.0-", - "mc-publish": { - "modrinth": "qvIfYCYJ", - "curseforge": "634179" - } - }, - { - "id": "sparkweave", - "mc-publish": { - "modrinth": "nf68xfAw", - "curseforge": "911456" - } - }, - { - "id": "midnightlib", - "mc-publish": { - "modrinth": "codAaoxh", - "curseforge": "488090" - } - }, - { - "id": "minecraft", - "versions": "=1.20.1" - } - ] - }, - "mixin": [ - "soulbound.mixins.json" - ], - "mc-publish": { - "curseforge": "618682", - "modrinth": "9QyjzbTh", - "loaders": [ - "quilt" - ] - } -} diff --git a/src/main/resources/soulbound.mixins.json b/src/main/resources/soulbound.mixins.json index 008b742..26377ff 100644 --- a/src/main/resources/soulbound.mixins.json +++ b/src/main/resources/soulbound.mixins.json @@ -1,8 +1,8 @@ { "required": true, "minVersion": "0.8", - "package": "dev.upcraft.soulbound.core.mixin", - "compatibilityLevel": "JAVA_16", + "package": "dev.upcraft.soulbound.mixin", + "compatibilityLevel": "JAVA_17", "mixins": [ "PlayerMixin" ],