From 3c06f93450458a6abb08266ebf603e0a4f88445c Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 08:31:04 +0200 Subject: [PATCH 01/28] Updated build env --- build.gradle | 24 +-- fabric.gradle | 138 ++++++++++++++++++ gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew.bat | 178 +++++++++++------------ 4 files changed, 242 insertions(+), 100 deletions(-) create mode 100644 fabric.gradle diff --git a/build.gradle b/build.gradle index c2cb2d0..583d3da 100644 --- a/build.gradle +++ b/build.gradle @@ -2,28 +2,29 @@ import com.modrinth.minotaur.TaskModrinthUpload plugins { id "java-library" - id "fabric-loom" version "0.5-SNAPSHOT" + id "fabric-loom" version "0.8-SNAPSHOT" id "com.matthewprenger.cursegradle" version "1.4.0" - id "com.modrinth.minotaur" version "1.1.0" + id "com.modrinth.minotaur" version "1.2.1" } ext { commonGradleBranch = "master" - minecraftVersion = "1.16.5" - yarnMappings = "1.16.5+build.1" - loaderVersion = "0.11.1" - fabricAPIVersion = "0.29.3+1.16" - modMenuVersion = "1.14.13+build.19" - clothConfigVersion = "4.8.3" + minecraftVersion = "1.17" + yarnMappings = "1.17+build.10" + loaderVersion = "0.11.6" + fabricAPIVersion = "0.35.2+1.17" + modMenuVersion = "2.0.2" + clothConfigVersion = "5.0.34" autoConfigVersion = "3.3.1" autoConfigTOMLVersion = "autoconfig-3.x.x-fabric-SNAPSHOT" } -version = "2.0.6-fabric" +version = "2.1.0-fabric" group = "com.therandomlabs.vanilladeathchest" archivesBaseName = "vanilladeathchest" -apply from: "https://raw.githubusercontent.com/TheRandomLabs/Common-Gradle/${project.commonGradleBranch}/fabric.gradle" +//apply from: "https://raw.githubusercontent.com/TheRandomLabs/Common-Gradle/${project.commonGradleBranch}/fabric.gradle" +apply from: "fabric.gradle" if (project.hasProperty("curseForgeAPIKey")) { curseforge { @@ -33,9 +34,11 @@ if (project.hasProperty("curseForgeAPIKey")) { id = "393000" addGameVersion "Fabric" + addGameVersion "Java 16" addGameVersion "Java 10" addGameVersion "Java 9" addGameVersion "Java 8" + addGameVersion "1.17" addGameVersion "1.16.5" addGameVersion "1.16.4" addGameVersion "1.16.3" @@ -83,6 +86,7 @@ if (project.hasProperty("modrinthToken")) { changelog = new File("changelog.md").getText() uploadFile = remapJar releaseType = "alpha" + addGameVersion("1.17") addGameVersion("1.16.5") addGameVersion("1.16.4") addGameVersion("1.16.3") diff --git a/fabric.gradle b/fabric.gradle new file mode 100644 index 0000000..05b919b --- /dev/null +++ b/fabric.gradle @@ -0,0 +1,138 @@ +def branch = project.findProperty("commonGradleBranch") ?: "master" +def includeClothConfig = !project.hasProperty("includeClothConfig") || project.includeClothConfig +def testMod = project.findProperty("testMod") + +ext { + useSpotBugs = false + testing = false + defaultCompileDependencies = false + includeLicense = false + buildSourcesJar = false + registerDefaultMavenPublication = false +} + +apply from: "https://raw.githubusercontent.com/TheRandomLabs/Common-Gradle/$branch/build.gradle" + +if (testMod) { + sourceSets { + testmod { + compileClasspath += main.compileClasspath + runtimeClasspath += main.runtimeClasspath + } + } +} + +repositories { + if (project.hasProperty("clothConfigVersion")) { + maven { url "https://maven.shedaniel.me/" } + } + + if (project.hasProperty("autoConfigVersion")) { + maven { + url "https://dl.bintray.com/shedaniel/autoconfig1u" + } + } + + if (project.hasProperty("autoConfigTOMLVersion")) { + maven { + url "https://jitpack.io" + } + } + + if (project.hasProperty("modMenuVersion")) { + maven { + url "https://maven.terraformersmc.com/releases/" + } + } +} + +dependencies { + minecraft "com.mojang:minecraft:${project.minecraftVersion}" + mappings "net.fabricmc:yarn:${project.yarnMappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loaderVersion}" + + if (project.hasProperty("fabricAPIVersion")) { + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabricAPIVersion}" + } + + if (project.hasProperty("modMenuVersion")) { + modImplementation "com.terraformersmc:modmenu:${modMenuVersion}" + } + + if (project.hasProperty("clothConfigVersion")) { + modApi("me.shedaniel.cloth:cloth-config-fabric:${project.clothConfigVersion}") { + exclude(group: "net.fabricmc.fabric-api") + } + + if (includeClothConfig) { + include "me.shedaniel.cloth:cloth-config-fabric:${project.clothConfigVersion}" + } + } + + if (project.hasProperty("autoConfigVersion")) { + modImplementation("me.sargunvohra.mcmods:autoconfig1u:${project.autoConfigVersion}") { + exclude(module: "fabric-api") + } + + if (includeClothConfig) { + include "me.sargunvohra.mcmods:autoconfig1u:${project.autoConfigVersion}" + } + } + + if (project.hasProperty("autoConfigTOMLVersion")) { + modImplementation "com.github.TheRandomLabs:AutoConfig-TOML:${project.autoConfigTOMLVersion}" + include "com.github.TheRandomLabs:AutoConfig-TOML:${project.autoConfigTOMLVersion}" + } + + if (testMod) { + afterEvaluate { + testmodImplementation(sourceSets.main.output) + } + } +} + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +java { + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { + "${it}_${project.archivesBaseName}" + } + } +} + +if (testMod) { + runClient { + classpath(sourceSets.testmod.runtimeClasspath) + } + + runServer { + classpath(sourceSets.testmod.runtimeClasspath) + } +} + +publishing { + publications { + mavenJava(MavenPublication) { + artifact(remapJar) { + builtBy remapJar + } + + artifact(sourcesJar) { + builtBy remapSourcesJar + } + + artifact javadocJar + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4d9ca16..0f80bbf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew.bat b/gradlew.bat index 107acd3..ac1b06f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,89 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 77f535c0f52fd6fd7907f469f5299318eb8248a3 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 08:31:13 +0200 Subject: [PATCH 02/28] Updated Mappings --- .../vanilladeathchest/VDCConfig.java | 581 ++++++++++++++++++ .../VDCModMenuEntryPoint.java | 41 ++ .../vanilladeathchest/VanillaDeathChest.java | 120 ++++ .../vanilladeathchest/command/VDCCommand.java | 177 ++++++ .../command/package-info.java | 27 + .../deathchest/DeathChest.java | 301 +++++++++ .../deathchest/DeathChestAutoRemover.java | 93 +++ .../deathchest/DeathChestInteractions.java | 181 ++++++ .../deathchest/DeathChestLocationFinder.java | 223 +++++++ .../deathchest/DeathChestPlacer.java | 426 +++++++++++++ .../deathchest/package-info.java | 27 + .../vanilladeathchest/mixin/BlockMixin.java | 81 +++ .../mixin/ChestBlockEntityMixin.java | 44 ++ .../mixin/ExplosionMixin.java | 58 ++ .../mixin/LivingEntityMixin.java | 241 ++++++++ .../LockableContainerBlockEntityMixin.java | 94 +++ .../mixin/PlayerEntityMixin.java | 48 ++ .../ServerPlayerInteractionManagerMixin.java | 65 ++ .../mixin/ShulkerBoxBlockEntityMixin.java | 43 ++ .../mixin/ShulkerBoxBlockMixin.java | 89 +++ .../mixin/WorldAccessor.java | 37 ++ .../vanilladeathchest/package-info.java | 27 + .../util/DeathChestBlockEntity.java | 46 ++ .../util/DeathChestDefenseEntity.java | 38 ++ .../vanilladeathchest/util/DropsList.java | 40 ++ .../vanilladeathchest/util/ViewerCount.java | 36 ++ .../vanilladeathchest/util/package-info.java | 27 + .../world/DeathChestsState.java | 225 +++++++ .../vanilladeathchest/world/package-info.java | 27 + 29 files changed, 3463 insertions(+) create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/VDCConfig.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/command/VDCCommand.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/command/package-info.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestLocationFinder.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/package-info.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ExplosionMixin.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/PlayerEntityMixin.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockEntityMixin.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/package-info.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestBlockEntity.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestDefenseEntity.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/util/DropsList.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/util/ViewerCount.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/util/package-info.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java create mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/world/package-info.java diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/VDCConfig.java b/remappedSrc/com/therandomlabs/vanilladeathchest/VDCConfig.java new file mode 100644 index 0000000..3298550 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/VDCConfig.java @@ -0,0 +1,581 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; + +import com.electronwill.nightconfig.core.conversion.SpecDoubleInRange; +import com.electronwill.nightconfig.core.conversion.SpecIntInRange; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.therandomlabs.autoconfigtoml.TOMLConfigSerializer; +import me.sargunvohra.mcmods.autoconfig1u.ConfigData; +import me.sargunvohra.mcmods.autoconfig1u.annotation.Config; +import me.sargunvohra.mcmods.autoconfig1u.annotation.ConfigEntry; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.item.Item; +import net.minecraft.item.Items; +import net.minecraft.nbt.StringNbtReader; +import net.minecraft.util.DyeColor; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.World; +import org.checkerframework.checker.nullness.qual.Nullable; + +@SuppressWarnings("CanBeFinal") +@TOMLConfigSerializer.Comment("VanillaDeathChest configuration.") +@Config(name = VanillaDeathChest.MOD_ID) +public final class VDCConfig implements ConfigData { + public static final class Spawning implements ConfigData { + @TOMLConfigSerializer.Comment({ + "The death chest container type.", + "SINGLE_CHEST: Only single chests.", + "SINGLE_OR_DOUBLE_CHEST: Single or double chests.", + "SINGLE_SHULKER_BOX: Single shulker boxes.", + "SINGLE_OR_DOUBLE_SHULKER_BOX: Single or double shulker boxes." + }) + @ConfigEntry.Gui.Tooltip + public ContainerType containerType = ContainerType.SINGLE_OR_DOUBLE_CHEST; + + @TOMLConfigSerializer.Comment({ + "The color of the shulker box if the container type is a shulker box.", + "WHITE: White.", + "ORANGE: Orange.", + "MAGENTA: Magenta.", + "LIGHT_BLUE: Light blue.", + "YELLOW: Yellow.", + "LIME: Lime.", + "PINK: Pink.", + "GRAY: Gray.", + "LIGHT_GRAY: Light gray.", + "CYAN: Cyan.", + "PURPLE: Purple.", + "BLUE: Blue.", + "BROWN: Brown.", + "GREEN: Green.", + "RED: Red.", + "BLACK: Black.", + "RANDOM: Random color." + }) + @ConfigEntry.Gui.Tooltip + public ShulkerBoxColor shulkerBoxColor = ShulkerBoxColor.WHITE; + + @TOMLConfigSerializer.Comment( + "The dimensions that death chests should or should not spawn in." + ) + @ConfigEntry.Gui.Tooltip + public List dimensions = new ArrayList<>(); + + @TOMLConfigSerializer.Comment({ + "Whether the dimensions list should be a blacklist or a whitelist.", + "BLACKLIST: blacklist.", + "WHITELIST: whitelist." + }) + @ConfigEntry.Gui.Tooltip + public DimensionListBehavior dimensionsBehavior = DimensionListBehavior.BLACKLIST; + + @SpecIntInRange(min = 1, max = Integer.MAX_VALUE) + @TOMLConfigSerializer.Comment( + "The radius around the location of a player's death in which a suitable death " + + "chest placement location should be searched for." + ) + @ConfigEntry.Gui.Tooltip + public int locationSearchRadius = 8; + + @TOMLConfigSerializer.Comment( + "Causes a death chest to be forcibly placed at the location of a player's death " + + "if no suitable locations are found nearby." + ) + @ConfigEntry.Gui.Tooltip + public boolean forcePlacementIfNoSuitableLocation = true; + + @TOMLConfigSerializer.Comment("Requires death chest placement to be on solid blocks.") + @ConfigEntry.Gui.Tooltip + public boolean requirePlacementOnSolidBlocks; + + @TOMLConfigSerializer.Comment( + "A regular expression that matches the registry names of items that can be " + + "placed in death chests." + ) + @ConfigEntry.Gui.Tooltip + public String registryNameRegex = ".+"; + + @TOMLConfigSerializer.Comment({ + "Causes death chests to only be spawned if the necessary container is in the " + + "player's inventory.", + "If this is enabled, the container is consumed if it is found." + }) + @ConfigEntry.Gui.Tooltip + public boolean useContainerInInventory; + + @TOMLConfigSerializer.Comment({ + "The display name of the death chest container.", + "Set this to an empty string to cause a custom display name to not be used." + }) + @ConfigEntry.Gui.Tooltip + public String containerDisplayName = "Death Chest"; + + @TOMLConfigSerializer.Comment({ + "The message sent to a player after a death chest is placed when they die.", + "The X, Y and Z coordinates are provided as arguments.", + "Set this to an empty string to disable this message." + }) + @ConfigEntry.Gui.Tooltip + public String spawnMessage = "Death chest spawned at [%s, %s, %s]"; + + @Nullable + @ConfigEntry.Gui.Excluded + private Set dimensionIdentifiers; + + /** + * {@inheritDoc} + */ + @Override + public void validatePostLoad() { + dimensionIdentifiers = dimensions.stream(). + map(Identifier::new). + collect(Collectors.toSet()); + dimensions = dimensionIdentifiers.stream(). + map(Identifier::toString). + collect(Collectors.toList()); + } + + /** + * Returns whether death chest spawning is enabled in the specified world's dimension. + * + * @param world a {@link World}. + * @return {@code true} if death chest spawning is enabled in the specified dimension, + * or otherwise {@code false}. + */ + @SuppressWarnings({"ConstantConditions", "NullAway"}) + public boolean isDimensionEnabled(World world) { + final Identifier identifier = world.getRegistryManager(). + get(Registry.DIMENSION_TYPE_KEY). + getId(world.getDimension()); + + if (identifier == null) { + VanillaDeathChest.logger.error( + "Failed to determine dimension", new RuntimeException() + ); + return dimensionsBehavior == DimensionListBehavior.BLACKLIST; + } + + final boolean anyMatch = dimensionIdentifiers.stream().anyMatch(identifier::equals); + + if (dimensionsBehavior == DimensionListBehavior.BLACKLIST) { + return !anyMatch; + } + + return anyMatch; + } + } + + public static final class KeyItem implements ConfigData { + @TOMLConfigSerializer.Comment({ + "The registry name of the key item.", + "A player must be holding this item in their main hand to unlock a death chest.", + "Set this to an empty string to allow death chests to be unlocked without an item." + }) + @ConfigEntry.Gui.Tooltip + public String registryName = ""; + + @SpecIntInRange(min = 0, max = Short.MAX_VALUE) + @TOMLConfigSerializer.Comment({ + "The meta value of the key item.", + "Set this to " + Short.MAX_VALUE + " to not require a specific meta value." + }) + @ConfigEntry.Gui.Tooltip + public int meta = Short.MAX_VALUE; + + @TOMLConfigSerializer.Comment({ + "The key consumption behavior.", + "CONSUME: Consume the item.", + "DAMAGE: Damage the item." + }) + @ConfigEntry.Gui.Tooltip + public KeyConsumptionBehavior consumptionBehavior = KeyConsumptionBehavior.CONSUME; + + @SpecIntInRange(min = 0, max = Short.MAX_VALUE) + @TOMLConfigSerializer.Comment({ + "The amount by which the key item should be consumed.", + "If the key item cannot be consumed this many times, the death chest will not " + + "be unlocked.", + "Players in creative mode will not have their key item consumed." + }) + @ConfigEntry.Gui.Tooltip + public int amountToConsume = 1; + + @TOMLConfigSerializer.Comment({ + "The message that is sent to the player when they fail to unlock a death chest.", + "This string takes the required amount (%1$s) and display name (%2$s) of the " + + "item as arguments.", + "Set this to an empty string to disable this message." + }) + @ConfigEntry.Gui.Tooltip + public String unlockFailureMessage = "You need %s of %s to retrieve your items"; + + @TOMLConfigSerializer.Comment( + "Whether the unlock failed message should be sent as a status message rather " + + "than a chat message." + ) + @ConfigEntry.Gui.Tooltip + public boolean unlockFailureStatusMessage = true; + + @Nullable + @ConfigEntry.Gui.Excluded + public Item item; + + /** + * {@inheritDoc} + */ + @Override + public void validatePostLoad() { + if (!registryName.isEmpty()) { + item = Registry.ITEM.get(new Identifier(registryName)); + + if (item == Items.AIR) { + registryName = ""; + item = null; + } else { + registryName = new Identifier(registryName).toString(); + } + } + } + } + + public static final class DefenseEntities implements ConfigData { + @TOMLConfigSerializer.Comment({ + "The registry name of the defense entity.", + "If the defense entity is a living entity, it will not automatically despawn.", + "If the defense entity can have a revenge target, then the revenge target will " + + "be set to its player.", + "Set this to an empty string to disable defense entities." + }) + @ConfigEntry.Gui.Tooltip + public String registryName = ""; + + @TOMLConfigSerializer.Comment("Custom NBT data for defense entities in JSON format.") + @ConfigEntry.Gui.Tooltip + public String nbtTag = "{}"; + + @TOMLConfigSerializer.Comment("Causes defense entities to drop experience.") + @ConfigEntry.Gui.Tooltip + public boolean dropExperience; + + @TOMLConfigSerializer.Comment("Causes defense entities to drop items.") + @ConfigEntry.Gui.Tooltip + public boolean dropItems; + + @SpecIntInRange(min = 1, max = Integer.MAX_VALUE) + @TOMLConfigSerializer.Comment( + "The number of defense entities that are spawned when a death chest is placed." + ) + @ConfigEntry.Gui.Tooltip + public int spawnCount = 3; + + @SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) + @TOMLConfigSerializer.Comment({ + "The maximum squared distance that a defense entity can be from its chest when " + + "a player is not nearby.", + "Set this to 0.0 to disable the limit so that defense entities are not " + + "teleported back to their death chest." + }) + @ConfigEntry.Gui.Tooltip + public double maxSquaredDistanceFromChest = 64.0; + + @SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) + @TOMLConfigSerializer.Comment({ + "The maximum squared distance that a defense entity can be from its player when" + + "its chest is not within the maximum squared distance.", + "Set this to 0.0 to disable the limit so that defense entities are teleported " + + "back to their death chest regardless of their distance from the player." + }) + @ConfigEntry.Gui.Tooltip + public double maxSquaredDistanceFromPlayer = 64.0; + + @Nullable + @ConfigEntry.Gui.Excluded + public EntityType entityType; + + /** + * {@inheritDoc} + */ + @Override + public void validatePostLoad() { + if (!registryName.isEmpty()) { + final Optional> optional = + Registry.ENTITY_TYPE.getOrEmpty(new Identifier(registryName)); + + if (optional.isPresent()) { + registryName = new Identifier(registryName).toString(); + entityType = optional.get(); + } else { + registryName = ""; + entityType = null; + } + } + + try { + StringNbtReader.parse(nbtTag); + } catch (CommandSyntaxException ex) { + nbtTag = "{}"; + } + } + } + + public static final class Protection { + @TOMLConfigSerializer.Comment({ + "Enables death chest protection.", + "When a death chest is protected, it can only be unlocked by its owner." + }) + @ConfigEntry.Gui.Tooltip + public boolean enable = true; + + @SpecIntInRange(min = 0, max = Integer.MAX_VALUE) + @TOMLConfigSerializer.Comment( + "The required permission level to bypass death chest protection." + ) + @ConfigEntry.Gui.Tooltip + public int bypassPermissionLevel = 3; + + @TOMLConfigSerializer.Comment( + "Causes players in creative mode to be able to bypass death chest protection." + ) + @ConfigEntry.Gui.Tooltip + public boolean bypassInCreativeMode = true; + + @SpecIntInRange(min = 0, max = Integer.MAX_VALUE) + @TOMLConfigSerializer.Comment({ + "The length of death chest protection in ticks.", + "120000 ticks is five in-game days.", + "Set this to 0 to cause death chests to be protected indefinitely." + }) + @ConfigEntry.Gui.Tooltip + public int period = 120000; + } + + public static final class Misc { + @TOMLConfigSerializer.Comment("Causes death chests to be removed when they are emptied.") + @ConfigEntry.Gui.Tooltip + public boolean removeEmptyDeathChests = true; + + @TOMLConfigSerializer.Comment( + "Whether empty death chests should only be removed when they are closed." + ) + @ConfigEntry.Gui.Tooltip + public boolean onlyRemoveClosedEmptyDeathChests; + + @TOMLConfigSerializer.Comment("Causes death chests to be dropped when they are broken.") + @ConfigEntry.Gui.Tooltip + public boolean dropDeathChests; + + @TOMLConfigSerializer.Comment({ + "The name of the game rule that controls whether death chests should be spawned.", + "Set this to an empty string to disable the game rule.", + "Changes to this option are applied after a game restart." + }) + @ConfigEntry.Gui.Tooltip + public String gameRuleName = "spawnDeathChests"; + + @TOMLConfigSerializer.Comment({ + "The name of the command that reloads this configuration from disk.", + "Set this to an empty string to disable the command.", + "Changes to this option are applied when a server is loaded." + }) + @ConfigEntry.Gui.Tooltip + public String configReloadCommand = "vdcconfigreload"; + } + + /** + * The death chest container type. + */ + public enum ContainerType { + /** + * Only single chests. + */ + SINGLE_CHEST, + /** + * Single or double chests. + */ + SINGLE_OR_DOUBLE_CHEST, + /** + * Only single shulker boxes. + */ + SINGLE_SHULKER_BOX, + /** + * Single or double shulker boxes. + */ + SINGLE_OR_DOUBLE_SHULKER_BOX + } + + /** + * The shulker box color. + */ + public enum ShulkerBoxColor { + /** + * White. + */ + WHITE, + /** + * Orange. + */ + ORANGE, + /** + * Magenta. + */ + MAGENTA, + /** + * Light blue. + */ + LIGHT_BLUE, + /** + * Yellow. + */ + YELLOW, + /** + * Lime. + */ + LIME, + /** + * Pink. + */ + PINK, + /** + * Gray. + */ + GRAY, + /** + * Light gray. + */ + LIGHT_GRAY, + /** + * Cyan. + */ + CYAN, + /** + * Purple. + */ + PURPLE, + /** + * Blue. + */ + BLUE, + /** + * Brown. + */ + BROWN, + /** + * Green. + */ + GREEN, + /** + * Red. + */ + RED, + /** + * Black. + */ + BLACK, + /** + * Random color. + */ + RANDOM; + + private static final Random random = new Random(); + + @Nullable + private final DyeColor color; + + ShulkerBoxColor() { + color = "RANDOM".equals(name()) ? null : DyeColor.valueOf(name()); + } + + /** + * Returns this shulker box color as a {@link DyeColor}. + * + * @return this shulker box color as a {@link DyeColor}. + */ + public DyeColor get() { + return color == null ? DyeColor.byId(random.nextInt(16)) : color; + } + } + + /** + * Dimension list behaviors. + */ + public enum DimensionListBehavior { + /** + * Blacklist. + */ + BLACKLIST, + /** + * Whitelist. + */ + WHITELIST + } + + /** + * Key item consumption behaviors. + */ + public enum KeyConsumptionBehavior { + /** + * Consume the item. + */ + CONSUME, + /** + * Damage the item. + */ + DAMAGE + } + + @TOMLConfigSerializer.Comment("Options related to death chest spawning.") + @ConfigEntry.Category("spawning") + @ConfigEntry.Gui.TransitiveObject + public Spawning spawning = new Spawning(); + + @TOMLConfigSerializer.Comment("Options related to the death chest key item.") + @ConfigEntry.Category("key_item") + @ConfigEntry.Gui.TransitiveObject + public KeyItem keyItem = new KeyItem(); + + @TOMLConfigSerializer.Comment("Options related to death chest defense entities.") + @ConfigEntry.Category("defense_entities") + @ConfigEntry.Gui.TransitiveObject + public DefenseEntities defenseEntities = new DefenseEntities(); + + @TOMLConfigSerializer.Comment("Options related to death chest protection.") + @ConfigEntry.Category("protection") + @ConfigEntry.Gui.TransitiveObject + public Protection protection = new Protection(); + + @TOMLConfigSerializer.Comment("Miscellaneous options.") + @ConfigEntry.Category("misc") + @ConfigEntry.Gui.TransitiveObject + public Misc misc = new Misc(); +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java b/remappedSrc/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java new file mode 100644 index 0000000..de3d3bd --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java @@ -0,0 +1,41 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest; + +import io.github.prospector.modmenu.api.ConfigScreenFactory; +import io.github.prospector.modmenu.api.ModMenuApi; +import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; + +/** + * The Mod Menu entry point for VanillaDeathChest. + */ +public final class VDCModMenuEntryPoint implements ModMenuApi { + /** + * {@inheritDoc} + */ + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return parent -> AutoConfig.getConfigScreen(VDCConfig.class, parent).get(); + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java b/remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java new file mode 100644 index 0000000..f73ba74 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java @@ -0,0 +1,120 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest; + +import com.therandomlabs.autoconfigtoml.TOMLConfigSerializer; +import com.therandomlabs.vanilladeathchest.command.VDCCommand; +import com.therandomlabs.vanilladeathchest.deathchest.DeathChestAutoRemover; +import com.therandomlabs.vanilladeathchest.deathchest.DeathChestInteractions; +import com.therandomlabs.vanilladeathchest.deathchest.DeathChestPlacer; +import com.therandomlabs.vanilladeathchest.world.DeathChestsState; +import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory; +import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry; +import net.minecraft.world.GameRules; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * The main VanillaDeathChest class. + */ +public final class VanillaDeathChest implements ModInitializer { + /** + * The VanillaDeathChest mod ID. + */ + public static final String MOD_ID = "vanilladeathchest"; + + /** + * The VanillaDeathChest logger. This should only be used by VanillaDeathChest. + */ + public static final Logger logger = LogManager.getLogger(MOD_ID); + + /** + * The game rule that controls whether death chests should be spawned. + */ + public static final GameRules.@Nullable Key SPAWN_DEATH_CHESTS; + + @SuppressWarnings("PMD.NonThreadSafeSingleton") + @Nullable + private static TOMLConfigSerializer serializer; + + static { + final String gameRuleName = VanillaDeathChest.config().misc.gameRuleName; + + if (gameRuleName.isEmpty()) { + SPAWN_DEATH_CHESTS = null; + } else { + SPAWN_DEATH_CHESTS = GameRuleRegistry.register( + gameRuleName, GameRules.Category.DROPS, GameRuleFactory.createBooleanRule(true) + ); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onInitialize() { + reloadConfig(); + CommandRegistrationCallback.EVENT.register(VDCCommand::register); + ServerTickEvents.START_WORLD_TICK.register(DeathChestPlacer::placeQueued); + ServerTickEvents.END_WORLD_TICK.register(DeathChestAutoRemover::removeEmpty); + UseBlockCallback.EVENT.register(DeathChestInteractions::interact); + ServerBlockEntityEvents.BLOCK_ENTITY_UNLOAD.register(DeathChestsState::onBlockEntityUnload); + } + + /** + * Returns the VanillaDeathChest configuration. + * + * @return a {@link VDCConfig} object. + */ + @SuppressWarnings("NullAway") + public static VDCConfig config() { + if (serializer == null) { + reloadConfig(); + } + + return serializer.getConfig(); + } + + /** + * Reloads the VanillaDeathChest configuration from disk. + */ + public static void reloadConfig() { + if (serializer == null) { + AutoConfig.register(VDCConfig.class, (definition, configClass) -> { + serializer = new TOMLConfigSerializer<>(definition, configClass); + return serializer; + }); + } else { + serializer.reloadFromDisk(); + } + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/command/VDCCommand.java b/remappedSrc/com/therandomlabs/vanilladeathchest/command/VDCCommand.java new file mode 100644 index 0000000..2bab94d --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/command/VDCCommand.java @@ -0,0 +1,177 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.command; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.UUID; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.tree.LiteralCommandNode; +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; +import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; +import com.therandomlabs.vanilladeathchest.deathchest.DeathChestPlacer; +import com.therandomlabs.vanilladeathchest.world.DeathChestsState; +import net.minecraft.command.CommandSource; +import net.minecraft.command.argument.EntityArgumentType; +import net.minecraft.command.argument.UuidArgumentType; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.LiteralText; +import net.minecraft.util.math.BlockPos; + +/** + * The command that reloads the VanillaDeathChest configuration. + */ +public final class VDCCommand { + private static final SuggestionProvider SUGGESTION_PROVIDER = + (context, builder) -> CommandSource.suggestMatching( + DeathChestsState.get( + context.getSource().getWorld() + ).getDeathChestIdentifierStrings(), builder + ); + + private static final SimpleCommandExceptionType INVALID_IDENTIFIER_EXCEPTION = + new SimpleCommandExceptionType(new LiteralText("Invalid death chest identifier")); + + private VDCCommand() {} + + /** + * Registers the command that reloads the VanillaDeathChest configuration. + * + * @param dispatcher the {@link CommandDispatcher}. + * @param dedicated whether the server is dedicated. + */ + public static void register( + CommandDispatcher dispatcher, boolean dedicated + ) { + final LiteralCommandNode commandNode = dispatcher.register( + CommandManager.literal("vanilladeathchest"). + then(CommandManager.literal("reloadconfig"). + requires(source -> source.hasPermissionLevel(4)). + executes( + context -> executeReloadConfig(context.getSource()) + ) + ). + then(CommandManager.literal("restoreinventory"). + requires(source -> source.hasPermissionLevel(2)). + then( + CommandManager.argument( + "identifier", UuidArgumentType.uuid() + ).suggests(SUGGESTION_PROVIDER).executes( + context -> executeRestoreInventory( + context.getSource(), getDeathChest(context) + ) + ).then( + CommandManager.argument( + "targets", EntityArgumentType.players() + ).executes( + context -> executeRestoreInventory( + context.getSource(), + getDeathChest(context), + EntityArgumentType.getPlayers( + context, "targets" + ) + ) + ) + ) + ) + ). + then(CommandManager.literal("place"). + requires(source -> source.hasPermissionLevel(2)). + then( + CommandManager.argument( + "identifier", UuidArgumentType.uuid() + ).suggests(SUGGESTION_PROVIDER).executes( + context -> executePlace( + context.getSource(), getDeathChest(context) + ) + ) + ) + ) + ); + dispatcher.register(CommandManager.literal("vdc").redirect(commandNode)); + } + + private static int executeReloadConfig(ServerCommandSource source) { + VanillaDeathChest.reloadConfig(); + source.sendFeedback(new LiteralText("VanillaDeathChest configuration reloaded!"), true); + return Command.SINGLE_SUCCESS; + } + + private static int executeRestoreInventory(ServerCommandSource source, DeathChest deathChest) + throws CommandSyntaxException { + return executeRestoreInventory( + source, deathChest, Collections.singleton(source.getPlayer()) + ); + } + + private static int executeRestoreInventory( + ServerCommandSource source, DeathChest deathChest, + Collection players + ) throws CommandSyntaxException { + for (ServerPlayerEntity player : players) { + final PlayerInventory inventory = player.inventory; + + for (int i = 0; i < inventory.size(); i++) { + inventory.setStack(i, deathChest.getInventory().getStack(i).copy()); + } + } + + source.sendFeedback(new LiteralText("Inventory restored!"), true); + return Command.SINGLE_SUCCESS; + } + + private static int executePlace(ServerCommandSource source, DeathChest deathChest) { + DeathChestPlacer.placeAndFillContainer(deathChest); + DeathChestsState.get(source.getWorld()).addDeathChest(deathChest); + final BlockPos pos = deathChest.getPos(); + source.sendFeedback(new LiteralText(String.format( + "Death chest placed at [%s, %s, %s]", pos.getX(), pos.getY(), pos.getZ() + )), true); + return Command.SINGLE_SUCCESS; + } + + @SuppressWarnings("NullAway") + private static DeathChest getDeathChest(CommandContext context) + throws CommandSyntaxException { + final DeathChestsState state = DeathChestsState.get(context.getSource().getWorld()); + final Set identifiers = state.getDeathChestIdentifiers(); + final UUID identifier = context.getArgument("identifier", UUID.class); + + if (identifiers.contains(identifier)) { + return state.getDeathChest(identifier); + } + + throw INVALID_IDENTIFIER_EXCEPTION.create(); + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/command/package-info.java b/remappedSrc/com/therandomlabs/vanilladeathchest/command/package-info.java new file mode 100644 index 0000000..821f407 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/command/package-info.java @@ -0,0 +1,27 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Command-related classes for VanillaDeathChest. + */ +package com.therandomlabs.vanilladeathchest.command; diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java new file mode 100644 index 0000000..a680b5a --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java @@ -0,0 +1,301 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.deathchest; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import com.therandomlabs.vanilladeathchest.VDCConfig; +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; +import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; +import com.therandomlabs.vanilladeathchest.world.DeathChestsState; +import net.fabricmc.fabric.api.util.NbtType; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtHelper; +import net.minecraft.nbt.NbtList; +import net.minecraft.server.OperatorEntry; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; + +/** + * Represents a death chest. + */ +public final class DeathChest { + private final UUID identifier; + private final ServerWorld world; + private final UUID playerUUID; + @SuppressWarnings("PMD.LooseCoupling") + private final ArrayList items; + private final PlayerInventory inventory; + private final long creationTime; + private final BlockPos pos; + private final boolean isDoubleChest; + private boolean locked; + + /** + * Constructs a {@link DeathChest} with the specified properties. + * + * @param identifier a {@link UUID}. + * @param world a {@link ServerWorld}. + * @param playerUUID a player UUID. + * @param items the items. + * @param inventory the player inventory at the time of death. + * @param creationTime a creation time. + * @param pos a position. If this is a double chest, this is the position of the west block. + * @param isDoubleChest whether the chest is a double chest. + * @param locked whether the chest is locked. + */ + public DeathChest( + UUID identifier, ServerWorld world, UUID playerUUID, List items, + PlayerInventory inventory, long creationTime, BlockPos pos, boolean isDoubleChest, + boolean locked + ) { + this.identifier = identifier; + this.world = world; + this.playerUUID = playerUUID; + this.items = new ArrayList<>(items); + this.inventory = inventory; + this.creationTime = creationTime; + this.pos = pos; + this.isDoubleChest = isDoubleChest; + this.locked = locked; + } + + /** + * Returns this death chest's identifier. + * + * @return a {@link UUID}. + */ + public UUID getIdentifier() { + return identifier; + } + + /** + * Returns this death chest's world. + * + * @return this death chest's world. + */ + public ServerWorld getWorld() { + return world; + } + + /** + * Returns this death chest's player UUID. + * + * @return this death chest's player UUID. + */ + public UUID getPlayerUUID() { + return playerUUID; + } + + /** + * Returns a mutable list containing this death chest's items. + * + * @return a mutable list containing this death chest's items. + */ + public List getItems() { + return items; + } + + /** + * Returns a cloned mutable list containing this death chest's items. + * + * @return a cloned mutable list containing this death chest's items. + */ + @SuppressWarnings("unchecked") + public List cloneItems() { + return (List) items.clone(); + } + + /** + * Returns the player inventory at the time of death. + * + * @return the {@link PlayerInventory} at the time of death. + */ + public PlayerInventory getInventory() { + return inventory; + } + + /** + * Returns this death chest's creation time. + * + * @return this death chest's creation time. + */ + public long getCreationTime() { + return creationTime; + } + + /** + * Returns this death chest's position. + * If this is a double chest, this is the position of the west block. + * + * @return this death chest's position. + */ + public BlockPos getPos() { + return pos; + } + + /** + * Returns whether this death chest is a double chest. + * This may be {@code true} even if one half of the double chest has been destroyed. + * + * @return {@code true} if this death chest is a double chest, or otherwise {@code false}. + */ + public boolean isDoubleChest() { + return isDoubleChest; + } + + /** + * Returns whether this death chest is locked. + * + * @return {@code true} if this death chest is locked, or otherwise {@code false}. + */ + public boolean isLocked() { + return locked; + } + + /** + * Sets whether this death chest is locked. + * + * @param flag {@code true} if this death chest should be locked, + * or {@code false} if this death chest should be unlocked. + */ + public void setLocked(boolean flag) { + locked = flag; + DeathChestsState.get(world).markDirty(); + } + + /** + * Returns whether this death chest exists in the world. + * + * @return {@code true} if this death chest exists, or otherwise {@code false}. + */ + public boolean exists() { + //We don't bother checking the east block, as it is impossible to only break one + //block in a double death chest. + if (!world.getBlockState(pos).getBlock().hasBlockEntity()) { + return false; + } + + final BlockEntity blockEntity = world.getBlockEntity(pos); + return blockEntity instanceof DeathChestBlockEntity && + equals(((DeathChestBlockEntity) blockEntity).getDeathChest()); + } + + /** + * Returns whether death chest protection prevents the specified player from opening this + * death chest. + * + * @param player a player. + * @return {@code true} if this death chest is protected from the specified player, + * or otherwise {@code false}. + */ + @SuppressWarnings("ConstantConditions") + public boolean isProtectedFrom(PlayerEntity player) { + final VDCConfig.Protection config = VanillaDeathChest.config().protection; + + if (!config.enable || playerUUID.equals(player.getUuid()) || + (config.bypassInCreativeMode && player.abilities.creativeMode)) { + return false; + } + + final OperatorEntry entry = + player.getServer().getPlayerManager().getOpList().get(player.getGameProfile()); + + if (entry != null && entry.getPermissionLevel() >= config.bypassPermissionLevel) { + return false; + } + + return config.period == 0 || + player.getEntityWorld().getTime() - creationTime <= config.period; + } + + /** + * Serializes this death chest to a {@link NbtCompound}. + * + * @param tag a {@link NbtCompound}. + * @return the {@link NbtCompound}. + */ + public NbtCompound toTag(NbtCompound tag) { + tag.put("Identifier", NbtHelper.fromUuid(identifier)); + tag.put("PlayerUUID", NbtHelper.fromUuid(playerUUID)); + + final NbtList itemsList = new NbtList(); + + for (ItemEntity item : items) { + final NbtCompound itemTag = item.writeNbt(new NbtCompound()); + item.writeCustomDataToNbt(itemTag); + itemsList.add(itemTag); + } + + tag.put("Items", itemsList); + + final NbtList inventoryList = new NbtList(); + inventory.writeNbt(inventoryList); + tag.put("Inventory", inventoryList); + + tag.putLong("CreationTime", creationTime); + tag.put("Pos", NbtHelper.fromBlockPos(pos)); + tag.putBoolean("IsDoubleChest", isDoubleChest); + tag.putBoolean("Locked", locked); + return tag; + } + + /** + * Deserializes a death chest from a {@link NbtCompound}. + * + * @param world a {@link ServerWorld}. + * @param tag a {@link NbtCompound}. + * @return the deserialized {@link DeathChest}. + */ + @SuppressWarnings("ConstantConditions") + public static DeathChest fromTag(ServerWorld world, NbtCompound tag) { + final List items = new ArrayList<>(); + + for (NbtElement itemTag : tag.getList("Items", NbtType.COMPOUND)) { + final ItemEntity item = new ItemEntity(EntityType.ITEM, world); + item.readNbt((NbtCompound) itemTag); + item.readCustomDataFromNbt((NbtCompound) itemTag); + items.add(item); + } + + //We can pass in a null player here because deserialize doesn't use the player. + final PlayerInventory inventory = new PlayerInventory(null); + inventory.readNbt(tag.getList("Inventory", NbtType.COMPOUND)); + + return new DeathChest( + NbtHelper.toUuid(tag.get("Identifier")), world, + NbtHelper.toUuid(tag.get("PlayerUUID")), items, inventory, + tag.getLong("CreationTime"), NbtHelper.toBlockPos(tag.getCompound("Pos")), + tag.getBoolean("IsDoubleChest"), tag.getBoolean("Locked") + ); + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java new file mode 100644 index 0000000..c1eba18 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java @@ -0,0 +1,93 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.deathchest; + +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; +import com.therandomlabs.vanilladeathchest.util.ViewerCount; +import com.therandomlabs.vanilladeathchest.world.DeathChestsState; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.LockableContainerBlockEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; + +/** + * Handles the automatic removal of empty death chests. + */ +public final class DeathChestAutoRemover { + private DeathChestAutoRemover() {} + + /** + * Removes all empty death chests in loaded chunks. + * This is called at the end of every world tick. + * + * @param world a {@link ServerWorld}. + */ + public static void removeEmpty(ServerWorld world) { + if (!VanillaDeathChest.config().misc.removeEmptyDeathChests) { + return; + } + + DeathChestsState.get(world).getExistingDeathChests(). + forEach(DeathChestAutoRemover::removeIfEmpty); + } + + private static void removeIfEmpty(DeathChest deathChest) { + final ServerWorld world = deathChest.getWorld(); + final BlockPos pos = deathChest.getPos(); + + //Don't unnecessarily load any chunks. + if (!world.getChunkManager().isChunkLoaded(pos.getX() >> 4, pos.getZ() >> 4)) { + return; + } + + final BlockEntity blockEntity = world.getBlockEntity(pos); + + if (!(blockEntity instanceof LockableContainerBlockEntity) || + !((LockableContainerBlockEntity) blockEntity).isEmpty()) { + return; + } + + final boolean isDoubleChest = deathChest.isDoubleChest(); + + if (isDoubleChest) { + final BlockEntity eastBlockEntity = world.getBlockEntity(pos.east()); + + if (!(eastBlockEntity instanceof LockableContainerBlockEntity) || + !((LockableContainerBlockEntity) eastBlockEntity).isEmpty()) { + return; + } + } + + if (!VanillaDeathChest.config().misc.onlyRemoveClosedEmptyDeathChests || + !(blockEntity instanceof ViewerCount) || + ((ViewerCount) blockEntity).getViewerCount() == 0) { + world.setBlockState(pos, Blocks.AIR.getDefaultState()); + + if (isDoubleChest) { + world.setBlockState(pos.east(), Blocks.AIR.getDefaultState()); + } + } + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java new file mode 100644 index 0000000..c3dcdb5 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java @@ -0,0 +1,181 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.deathchest; + +import com.therandomlabs.vanilladeathchest.VDCConfig; +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; +import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Handles death chest interactions. + */ +public final class DeathChestInteractions { + @Nullable + private static DeathChest ignoreDeathChest; + + private DeathChestInteractions() {} + + /** + * Called when a block is right-clicked. + * + * @param player the player. + * @param world the world. + * @param hand the hand. + * @param blockHitResult the {@link BlockHitResult}. + * @return {@link ActionResult#PASS} if the interaction can occur, + * or {@link ActionResult#SUCCESS} if it cannot. + */ + public static ActionResult interact( + PlayerEntity player, World world, Hand hand, BlockHitResult blockHitResult + ) { + if (!(world instanceof ServerWorld)) { + return ActionResult.PASS; + } + + if (!world.getBlockState(blockHitResult.getBlockPos()).getBlock().hasBlockEntity()) { + return ActionResult.PASS; + } + + final BlockEntity blockEntity = world.getBlockEntity(blockHitResult.getBlockPos()); + + if (blockEntity instanceof DeathChestBlockEntity) { + final DeathChest deathChest = ((DeathChestBlockEntity) blockEntity).getDeathChest(); + return deathChest == null || attemptInteract(deathChest, (ServerPlayerEntity) player) ? + ActionResult.PASS : ActionResult.SUCCESS; + } + + return ActionResult.PASS; + } + + /** + * If the specified death chest is locked, an unlock attempt is performed. + * The method then returns whether the specified player can unlock the death chest. + * + * @param deathChest a death chest. + * @param player a player. + * @return {@code true} if the player can interact with the death chest, + * or otherwise {@code false}. + */ + @SuppressWarnings("PMD.CompareObjectsWithEquals") + public static boolean attemptInteract(DeathChest deathChest, ServerPlayerEntity player) { + if (deathChest.isProtectedFrom(player)) { + return false; + } + + final VDCConfig.KeyItem config = VanillaDeathChest.config().keyItem; + + if (config.item == null) { + deathChest.setLocked(false); + return true; + } + + if (!deathChest.isLocked()) { + return true; + } + + final ItemStack stack = player.getStackInHand(player.getActiveHand()); + final int amount = config.amountToConsume; + + if (stack.getItem() == config.item) { + if (amount == 0 || player.abilities.creativeMode) { + deathChest.setLocked(false); + return true; + } + + if (config.consumptionBehavior == VDCConfig.KeyConsumptionBehavior.DAMAGE) { + if (stack.isDamageable() && stack.getDamage() + amount <= stack.getMaxDamage()) { + stack.damage( + amount, player, + breaker -> breaker.sendEquipmentBreakStatus(EquipmentSlot.MAINHAND) + ); + deathChest.setLocked(false); + return true; + } + } else if (stack.getCount() >= amount) { + stack.decrement(amount); + deathChest.setLocked(false); + return true; + } + } + + final String message = config.unlockFailureMessage; + + if (!message.isEmpty()) { + final Text component = new LiteralText(String.format( + message, + amount, new TranslatableText(config.item.getTranslationKey()).getString() + )); + + player.sendMessage(component, config.unlockFailureStatusMessage); + } + + return false; + } + + /** + * Called when a player attempts to break a death chest. + * Returns whether the death chest should be broken. + * If this is {@code true} and the death chest is a double chest, the other death chest + * block is automatically destroyed. + * + * @param pos a position. + * @param deathChest a death chest. + * @param player a player. + * @return {@code true} if the death chest should be broken, or otherwise {@code false}. + */ + public static boolean attemptBreak( + BlockPos pos, DeathChest deathChest, ServerPlayerEntity player + ) { + if (deathChest.equals(ignoreDeathChest)) { + return true; + } + + if (!attemptInteract(deathChest, player)) { + return false; + } + + if (deathChest.isDoubleChest()) { + ignoreDeathChest = deathChest; + final BlockPos west = deathChest.getPos(); + player.interactionManager.tryBreakBlock(pos.equals(west) ? west.east() : west); + } + + return true; + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestLocationFinder.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestLocationFinder.java new file mode 100644 index 0000000..2d32569 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestLocationFinder.java @@ -0,0 +1,223 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.deathchest; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.google.common.collect.ImmutableList; +import com.therandomlabs.vanilladeathchest.VDCConfig; +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemPlacementContext; +import net.minecraft.item.ItemUsageContext; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Finds suitable locations for the placement of death chests. + */ +public final class DeathChestLocationFinder { + /** + * A death chest location. + */ + public static final class Location { + private final BlockPos pos; + private final boolean isDoubleChest; + + private Location(BlockPos pos, boolean isDoubleChest) { + this.pos = pos; + this.isDoubleChest = isDoubleChest; + } + + /** + * Returns the {@link BlockPos} of this location. + * If this is a double chest, this is the position of the west block. + * + * @return the {@link BlockPos} of this location. + */ + public BlockPos getPos() { + return pos; + } + + /** + * Returns whether a double chest should be placed at this location. + * + * @return {@code true} if a double chest should be placed at this location, + * or otherwise {@code false}. + */ + public boolean isDoubleChest() { + return isDoubleChest; + } + } + + private static final class SearchOrder implements Iterable { + private final int size; + private List translations; + + private SearchOrder(int size) { + this.size = size; + + translations = new ArrayList<>(); + + for (int x = 0; x <= size; x++) { + addTranslations(x); + addTranslations(-x); + } + + translations = ImmutableList.copyOf(translations); + } + + @NonNull + @Override + public Iterator iterator() { + return translations.iterator(); + } + + private void addTranslations(int x) { + for (int y = 0; y <= size; y++) { + addTranslations(x, y); + addTranslations(x, -y); + } + } + + private void addTranslations(int x, int y) { + for (int z = 0; z <= size; z++) { + translations.add(new BlockPos(x, y, z)); + translations.add(new BlockPos(x, y, -z)); + } + } + } + + @Nullable + private static SearchOrder searchOrder; + + private DeathChestLocationFinder() {} + + /** + * Finds the most suitable location to place a queued death chest. + * + * @param deathChest a queued {@link DeathChest}. + * @param doubleChest whether a double chest is preferred. + * @return a {@link Location} that describes the most suitable location to place the specified + * death chest. + */ + @Nullable + public static Location find(DeathChest deathChest, boolean doubleChest) { + final World world = deathChest.getWorld(); + final PlayerEntity player = world.getPlayerByUuid(deathChest.getPlayerUUID()); + final BlockPos pos = deathChest.getPos(); + + final VDCConfig.Spawning config = VanillaDeathChest.config().spawning; + + final BlockPos searchPos = new BlockPos( + pos.getX(), Math.min(256, Math.max(1, pos.getY())), pos.getZ() + ); + + BlockPos singleChestPos = null; + + for (BlockPos translation : getSearchOrder(config.locationSearchRadius)) { + final BlockPos potentialPos = searchPos.add(translation); + + if (!canPlace(world, player, potentialPos)) { + continue; + } + + if (!doubleChest || canPlace(world, player, potentialPos.east())) { + return new Location(potentialPos, doubleChest); + } + + if (singleChestPos == null) { + singleChestPos = potentialPos; + } + } + + if (singleChestPos != null) { + return new Location(singleChestPos, false); + } + + return config.forcePlacementIfNoSuitableLocation ? new Location(pos, doubleChest) : null; + } + + @SuppressWarnings("PMD.NonThreadSafeSingleton") + private static Iterable getSearchOrder(int size) { + if (searchOrder == null || searchOrder.size != size) { + searchOrder = new SearchOrder(size); + } + + return searchOrder; + } + + private static boolean canPlace( + World world, PlayerEntity player, BlockPos pos, boolean doubleChest + ) { + if (doubleChest) { + return canPlace(world, player, pos) && canPlace(world, player, pos.east()); + } + + return canPlace(world, player, pos); + } + + private static boolean canPlace(World world, PlayerEntity player, BlockPos pos) { + if (!world.canPlayerModifyAt(player, pos) || + (VanillaDeathChest.config().spawning.requirePlacementOnSolidBlocks && + !world.isTopSolid(pos.down(), player))) { + return false; + } + + final ItemPlacementContext context = new ItemPlacementContext(new ItemUsageContext( + player, Hand.MAIN_HAND, + new BlockHitResult(new Vec3d(0.0, 0.0, 0.0), Direction.DOWN, pos, false) + )); + + if (isReplaceable(world, pos, context) && isReplaceable(world, pos.up(), context)) { + return isNotChest(world, pos.north()) && isNotChest(world, pos.east()) && + isNotChest(world, pos.south()) && isNotChest(world, pos.west()); + } + + return false; + } + + private static boolean isReplaceable(World world, BlockPos pos, ItemPlacementContext context) { + if (pos.getY() < 1 || pos.getY() > world.getHeight()) { + return false; + } + + final BlockState state = world.getBlockState(pos); + return state.isAir() || state.canReplace(context); + } + + private static boolean isNotChest(World world, BlockPos pos) { + return world.getBlockState(pos).getBlock() != Blocks.CHEST; + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java new file mode 100644 index 0000000..cb8a6b9 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java @@ -0,0 +1,426 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.deathchest; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.regex.Pattern; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.therandomlabs.vanilladeathchest.VDCConfig; +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; +import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; +import com.therandomlabs.vanilladeathchest.util.DeathChestDefenseEntity; +import com.therandomlabs.vanilladeathchest.world.DeathChestsState; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.ChestBlock; +import net.minecraft.block.ShulkerBoxBlock; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.block.enums.ChestType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.SpawnReason; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventories; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.StringNbtReader; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.LiteralText; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.World; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Handles death chest placement. + */ +public final class DeathChestPlacer { + private enum ContainerConsumptionResult { + FAILED, + SINGLE, + DOUBLE + } + + private DeathChestPlacer() {} + + /** + * Places all queued death chests that are ready to be placed in the specified world. + * This is called at the end of every world tick. + * + * @param world a {@link ServerWorld}. + */ + @SuppressWarnings("ConstantConditions") + public static void placeQueued(ServerWorld world) { + final DeathChestsState state = DeathChestsState.get(world); + final Queue queue = state.getQueuedDeathChests(); + + //We wait two ticks to prevent conflicts with other mods that place things after death. + if (queue.isEmpty() || world.getTime() - queue.peek().getCreationTime() < 2L) { + return; + } + + while (!queue.isEmpty() && world.getTime() - queue.peek().getCreationTime() >= 2L) { + placeAndDropRemaining(queue.poll()); + } + + state.markDirty(); + } + + /** + * Places and fills the specified death chest. + * + * @param deathChest a death chest. + * @return {@code true} if the placement is successful, or otherwise {@code false}. + */ + @SuppressWarnings("NullAway") + public static boolean placeAndFillContainer(DeathChest deathChest) { + final VDCConfig.Spawning config = VanillaDeathChest.config().spawning; + final BlockPos pos = deathChest.getPos(); + final List items = deathChest.getItems(); + final boolean doubleChest = deathChest.isDoubleChest(); + + final Block block; + + if (config.containerType == VDCConfig.ContainerType.SINGLE_SHULKER_BOX || + config.containerType == VDCConfig.ContainerType.SINGLE_OR_DOUBLE_SHULKER_BOX) { + block = ShulkerBoxBlock.get(config.shulkerBoxColor.get()); + } else { + block = Blocks.CHEST; + } + + final BlockPos east = pos.east(); + final World world = deathChest.getWorld(); + final BlockState state = block.getDefaultState(); + + if (doubleChest) { + if (block == Blocks.CHEST) { + world.setBlockState(pos, state.with(ChestBlock.CHEST_TYPE, ChestType.LEFT)); + world.setBlockState(east, state.with(ChestBlock.CHEST_TYPE, ChestType.RIGHT)); + } else { + world.setBlockState(pos, state); + world.setBlockState(east, state); + } + } else { + world.setBlockState(pos, state); + } + + final BlockEntity blockEntity = world.getBlockEntity(pos); + final BlockEntity eastBlockEntity = doubleChest ? world.getBlockEntity(east) : null; + + if (!(blockEntity instanceof LootableContainerBlockEntity) || + (doubleChest && !(eastBlockEntity instanceof LootableContainerBlockEntity))) { + VanillaDeathChest.logger.warn( + "Failed to place death chest at [{}] due to invalid block entity", pos + ); + return false; + } + + final LootableContainerBlockEntity container = + (LootableContainerBlockEntity) (doubleChest ? eastBlockEntity : blockEntity); + + for (int i = 0; i < items.size() && i < 27; i++) { + container.setStack(i, items.get(i).getStack().copy()); + } + + ((DeathChestBlockEntity) container).markAsDeathChest(); + + if (!config.containerDisplayName.isEmpty()) { + container.setCustomName(new LiteralText(config.containerDisplayName)); + } + + if (doubleChest) { + final LootableContainerBlockEntity westContainer = + (LootableContainerBlockEntity) blockEntity; + + for (int i = 27; i < items.size(); i++) { + westContainer.setStack(i - 27, items.get(i).getStack().copy()); + } + + ((DeathChestBlockEntity) westContainer).markAsDeathChest(); + + if (!config.containerDisplayName.isEmpty()) { + westContainer.setCustomName(new LiteralText(config.containerDisplayName)); + } + } else if (items.size() > 27) { + items.subList(27, items.size()).clear(); + } + + return true; + } + + /** + * Spawns the defense entities for the specified death chest. + * + * @param deathChest a death chest. + */ + public static void spawnDefenseEntities(DeathChest deathChest) { + final VDCConfig.DefenseEntities config = VanillaDeathChest.config().defenseEntities; + + if (config.entityType == null) { + return; + } + + final ServerWorld world = deathChest.getWorld(); + final BlockPos pos = deathChest.getPos(); + final double x = pos.getX() + 0.5; + final double y = pos.getY() + 1.0; + final double z = pos.getZ() + 0.5; + + for (int i = 0; i < config.spawnCount; i++) { + //The following spawn logic has been taken from SummonCommand. + NbtCompound tag; + + try { + tag = StringNbtReader.parse(config.nbtTag); + } catch (CommandSyntaxException ex) { + //This should not happen. + tag = new NbtCompound(); + } + + final boolean emptyTag = tag.isEmpty(); + tag.putString("id", config.registryName); + + final Entity entity = EntityType.loadEntityWithPassengers( + tag, world, spawnedEntity -> { + spawnedEntity.refreshPositionAndAngles( + x, y, z, spawnedEntity.yaw, spawnedEntity.pitch + ); + return spawnedEntity; + } + ); + + if (entity instanceof DeathChestDefenseEntity) { + ((DeathChestDefenseEntity) entity).setDeathChest(deathChest); + + if (emptyTag && entity instanceof MobEntity) { + ((MobEntity) entity).initialize( + world, world.getLocalDifficulty(pos), SpawnReason.EVENT, null, null + ); + } + } + + world.spawnEntityAndPassengers(entity); + } + } + + private static void placeAndDropRemaining(DeathChest deathChest) { + final List allItems = deathChest.cloneItems(); + + final DeathChest newDeathChest = place(allItems, deathChest); + final List items = + newDeathChest == null ? Collections.emptyList() : newDeathChest.getItems(); + + final World world = deathChest.getWorld(); + + for (ItemEntity drop : allItems) { + if (!items.contains(drop)) { + if (drop.removed) { + world.spawnEntity(new ItemEntity( + world, drop.getX(), drop.getY(), drop.getZ(), drop.getStack().copy() + )); + } else { + world.spawnEntity(drop); + } + } + } + } + + @Nullable + private static DeathChest place(List allItems, DeathChest deathChest) { + final VDCConfig.Spawning config = VanillaDeathChest.config().spawning; + + final Pattern pattern = Pattern.compile(config.registryNameRegex); + deathChest.getItems().removeIf( + item -> !pattern.matcher( + Registry.ITEM.getId(item.getStack().getItem()).toString() + ).matches() + ); + + if (deathChest.getItems().isEmpty()) { + return null; + } + + final VDCConfig.ContainerType type = config.containerType; + boolean doubleChest = deathChest.getItems().size() > 27 && + (type == VDCConfig.ContainerType.SINGLE_OR_DOUBLE_CHEST || + type == VDCConfig.ContainerType.SINGLE_OR_DOUBLE_SHULKER_BOX); + + final ServerWorld world = deathChest.getWorld(); + final List allItemsBeforeContainerConsumption = new ArrayList<>(); + + if (config.useContainerInInventory) { + for (ItemEntity item : allItems) { + allItemsBeforeContainerConsumption.add( + new ItemEntity( + world, item.getX(), item.getY(), item.getZ(), item.getStack().copy() + ) + ); + } + + final ContainerConsumptionResult result = + consumeContainerInInventory(allItems, deathChest, doubleChest); + + if (result == ContainerConsumptionResult.FAILED) { + return null; + } + + if (result == ContainerConsumptionResult.SINGLE) { + doubleChest = false; + } + } + + final DeathChestLocationFinder.Location location = + DeathChestLocationFinder.find(deathChest, doubleChest); + + if (location == null) { + VanillaDeathChest.logger.warn( + "No death chest location found for player at [{}]", deathChest.getPos() + ); + return null; + } + + if (!location.isDoubleChest()) { + doubleChest = false; + } + + final BlockPos pos = location.getPos(); + final DeathChest newDeathChest = new DeathChest( + deathChest.getIdentifier(), world, deathChest.getPlayerUUID(), + deathChest.getItems(), deathChest.getInventory(), world.getTime(), pos, doubleChest, + true + ); + + if (!placeAndFillContainer(newDeathChest) || newDeathChest.getItems().isEmpty()) { + //Make sure we also drop any consumed containers. + if (!allItemsBeforeContainerConsumption.isEmpty()) { + allItems.clear(); + allItems.addAll(allItemsBeforeContainerConsumption); + } + + return null; + } + + final PlayerEntity player = world.getPlayerByUuid(deathChest.getPlayerUUID()); + + spawnDefenseEntities(newDeathChest); + + DeathChestsState.get(world).addDeathChest(newDeathChest); + + VanillaDeathChest.logger.info( + "Death chest for {} spawned at [{}, {}, {}] with identifier {}", + player == null ? deathChest.getPlayerUUID() : player.getGameProfile().getName(), + pos.getX(), pos.getY(), pos.getZ(), deathChest.getIdentifier() + ); + + if (player != null) { + player.sendMessage(new LiteralText(String.format( + config.spawnMessage, pos.getX(), pos.getY(), pos.getZ() + )), false); + } + + return newDeathChest; + } + + private static ContainerConsumptionResult consumeContainerInInventory( + List allItems, DeathChest deathChest, boolean doubleChest + ) { + final VDCConfig.ContainerType type = VanillaDeathChest.config().spawning.containerType; + final Set emptyItems = new HashSet<>(); + + int availableContainers = 0; + + for (ItemEntity item : allItems) { + final ItemStack stack = item.getStack(); + + if (type == VDCConfig.ContainerType.SINGLE_CHEST || + type == VDCConfig.ContainerType.SINGLE_OR_DOUBLE_CHEST) { + if (stack.getItem() != Item.BLOCK_ITEMS.get(Blocks.CHEST)) { + continue; + } + } else { + if (!(Block.getBlockFromItem(stack.getItem()) instanceof ShulkerBoxBlock)) { + continue; + } + + final NbtCompound tag = stack.getTag(); + + if (tag != null) { + final DefaultedList inventory = + DefaultedList.ofSize(27, ItemStack.EMPTY); + Inventories.readNbt(tag.getCompound("BlockEntityTag"), inventory); + + //The shulker box must be empty. + if (inventory.stream().anyMatch(itemStack -> !itemStack.isEmpty())) { + continue; + } + } + } + + if (availableContainers == 0 && (!doubleChest || stack.getCount() > 1)) { + availableContainers = doubleChest ? 2 : 1; + stack.decrement(availableContainers); + + if (stack.isEmpty()) { + emptyItems.add(item); + } + + break; + } + + //doubleChest is true, but stack.getCount() is only 1. + availableContainers++; + stack.decrement(1); + + if (stack.isEmpty()) { + emptyItems.add(item); + } + + if (availableContainers == 2) { + break; + } + } + + if (availableContainers == 0) { + return ContainerConsumptionResult.FAILED; + } + + allItems.removeAll(emptyItems); + deathChest.getItems().removeAll(emptyItems); + + return availableContainers == 1 ? + ContainerConsumptionResult.SINGLE : ContainerConsumptionResult.DOUBLE; + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/package-info.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/package-info.java new file mode 100644 index 0000000..c60a02f --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/package-info.java @@ -0,0 +1,27 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Classes that contain death chest-related logic for VanillaDeathChest. + */ +package com.therandomlabs.vanilladeathchest.deathchest; diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java new file mode 100644 index 0000000..71b5e13 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java @@ -0,0 +1,81 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.mixin; + +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; +import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.ShulkerBoxBlock; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +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; + +@Mixin(Block.class) +public final class BlockMixin { + @Unique + private static BlockPos brokenDeathChest; + + @SuppressWarnings("ConstantConditions") + @Inject(method = "onBreak", at = @At("HEAD")) + private void onBreak( + World world, BlockPos pos, BlockState state, PlayerEntity player, CallbackInfo info + ) { + if ((Object) this instanceof ShulkerBoxBlock || !state.getBlock().hasBlockEntity()) { + return; + } + + final BlockEntity blockEntity = world.getBlockEntity(pos); + + if (blockEntity instanceof DeathChestBlockEntity && + ((DeathChestBlockEntity) blockEntity).getDeathChest() != null) { + brokenDeathChest = pos; + } + } + + @Inject( + method = "dropStack", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/World;" + + "spawnEntity(Lnet/minecraft/entity/Entity;)Z" + ), + cancellable = true + ) + private static void dropStack( + World world, BlockPos pos, ItemStack stack, CallbackInfo callback + ) { + if (pos.equals(brokenDeathChest) && !VanillaDeathChest.config().misc.dropDeathChests) { + brokenDeathChest = null; + callback.cancel(); + } + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java new file mode 100644 index 0000000..093adad --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java @@ -0,0 +1,44 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.mixin; + +import com.therandomlabs.vanilladeathchest.util.ViewerCount; +import net.minecraft.block.entity.ChestBlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ChestBlockEntity.class) +public final class ChestBlockEntityMixin implements ViewerCount { + @SuppressWarnings("PMD.AvoidProtectedFieldInFinalClass") + @Shadow + protected int viewerCount; + + /** + * {@inheritDoc} + */ + @Override + public int getViewerCount() { + return viewerCount; + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ExplosionMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ExplosionMixin.java new file mode 100644 index 0000000..2bbf715 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ExplosionMixin.java @@ -0,0 +1,58 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.mixin; + +import java.util.List; + +import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; +import com.therandomlabs.vanilladeathchest.world.DeathChestsState; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.explosion.Explosion; +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; + +@Mixin(Explosion.class) +public abstract class ExplosionMixin { + @Shadow + @Final + private World world; + + @Shadow + public abstract List getAffectedBlocks(); + + @Inject(method = "collectBlocksAndDamageEntities", at = @At("TAIL")) + private void collectBlocksAndDamageEntities(CallbackInfo info) { + final DeathChestsState deathChestsState = DeathChestsState.get((ServerWorld) world); + getAffectedBlocks().removeIf(pos -> { + final DeathChest deathChest = deathChestsState.getExistingDeathChest(pos); + return deathChest != null && deathChest.isLocked(); + }); + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java new file mode 100644 index 0000000..6eb9e68 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java @@ -0,0 +1,241 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.mixin; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import com.therandomlabs.vanilladeathchest.VDCConfig; +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; +import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; +import com.therandomlabs.vanilladeathchest.util.DeathChestDefenseEntity; +import com.therandomlabs.vanilladeathchest.util.DropsList; +import com.therandomlabs.vanilladeathchest.world.DeathChestsState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.mob.Angerable; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtHelper; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +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; + +@SuppressWarnings("ConstantConditions") +@Mixin(value = LivingEntity.class, priority = Integer.MAX_VALUE) +public abstract class LivingEntityMixin implements DropsList, DeathChestDefenseEntity { + @Unique + private final List drops = new ArrayList<>(); + + @Unique + private PlayerInventory inventory; + + @Unique + private DeathChest deathChest; + + @Unique + private UUID deathChestPlayerUUID; + + /** + * {@inheritDoc} + */ + @Override + public List getDrops() { + return drops; + } + + /** + * {@inheritDoc} + */ + @Override + public void setDeathChest(DeathChest deathChest) { + this.deathChest = deathChest; + deathChestPlayerUUID = deathChest.getPlayerUUID(); + } + + @Inject(method = "drop", at = @At("HEAD")) + public void dropHead(CallbackInfo info) { + if ((Object) this instanceof PlayerEntity) { + drops.clear(); + //We can't pass in null here because Campanion mixins into setStack and needs the + //player. + inventory = new PlayerInventory((PlayerEntity) (Object) this); + final PlayerInventory oldInventory = ((PlayerEntity) (Object) this).inventory; + + for (int i = 0; i < oldInventory.size(); i++) { + inventory.setStack(i, oldInventory.getStack(i).copy()); + } + } + } + + @Inject(method = "drop", at = @At("TAIL")) + public void dropTail(CallbackInfo info) { + if (drops.isEmpty()) { + return; + } + + final LivingEntity entity = (LivingEntity) (Object) this; + final ServerWorld world = (ServerWorld) entity.getEntityWorld(); + + if ((VanillaDeathChest.SPAWN_DEATH_CHESTS != null && + !world.getGameRules().getBoolean(VanillaDeathChest.SPAWN_DEATH_CHESTS)) || + !VanillaDeathChest.config().spawning.isDimensionEnabled(world)) { + return; + } + + drops.forEach(Entity::remove); + final DeathChestsState deathChestsState = DeathChestsState.get(world); + final BlockPos pos = entity.getBlockPos(); + final DeathChest deathChest = new DeathChest( + UUID.randomUUID(), world, entity.getUuid(), drops, inventory, world.getTime(), pos, + false, true + ); + deathChestsState.getQueuedDeathChests().add(deathChest); + deathChestsState.markDirty(); + + VanillaDeathChest.logger.info( + "Death chest for {} queued at [{}, {}, {}] with identifier {}", + ((PlayerEntity) (Object) this).getGameProfile().getName(), + pos.getX(), pos.getY(), pos.getZ(), deathChest.getIdentifier() + ); + } + + @Inject(method = "tick", at = @At("HEAD")) + public void tick(CallbackInfo info) { + if (deathChestPlayerUUID == null) { + return; + } + + final LivingEntity entity = (LivingEntity) (Object) this; + final PlayerEntity player = entity.getEntityWorld().getPlayerByUuid(deathChestPlayerUUID); + + if ((Object) this instanceof MobEntity) { + final MobEntity mobEntity = (MobEntity) (Object) this; + mobEntity.setPersistent(); + + if (player != null) { + mobEntity.setAttacker(player); + mobEntity.setTarget(player); + } + } + + if (player != null && this instanceof Angerable) { + final Angerable angerable = (Angerable) this; + angerable.setTarget(player); + angerable.setAngerTime(Integer.MAX_VALUE); + } + + final VDCConfig.DefenseEntities config = VanillaDeathChest.config().defenseEntities; + + if (config.maxSquaredDistanceFromChest == 0.0) { + return; + } + + if (deathChest != null && !deathChest.exists()) { + deathChest = null; + } + + if (deathChest == null) { + return; + } + + final BlockPos pos = deathChest.getPos(); + final double squaredDistanceFromChest = pos.getSquaredDistance(entity.getPos(), true); + + if (squaredDistanceFromChest > config.maxSquaredDistanceFromChest) { + final double squaredDistanceFromPlayer = player == null ? + Double.MAX_VALUE : entity.getPos().squaredDistanceTo(player.getPos()); + + if (config.maxSquaredDistanceFromPlayer == 0.0 || + squaredDistanceFromPlayer > config.maxSquaredDistanceFromPlayer) { + entity.refreshPositionAndAngles( + pos.getX() + 0.5, pos.getY() + 1.0, pos.getZ() + 0.5, + entity.yaw, entity.pitch + ); + } + } + } + + @Inject(method = "writeCustomDataToTag", at = @At("HEAD")) + public void writeCustomDataToTag(NbtCompound tag, CallbackInfo info) { + if (deathChestPlayerUUID != null) { + if (deathChest != null) { + tag.put( + "DeathChestIdentifier", NbtHelper.fromUuid(deathChest.getIdentifier()) + ); + } + + tag.put("DeathChestPlayer", NbtHelper.fromUuid(deathChestPlayerUUID)); + } + } + + @Inject(method = "readCustomDataFromTag", at = @At("HEAD")) + public void readCustomDataFromTag(NbtCompound tag, CallbackInfo info) { + if (tag.contains("DeathChestPlayer")) { + deathChestPlayerUUID = NbtHelper.toUuid(tag.get("DeathChestPlayer")); + + if (tag.contains("DeathChestIdentifier")) { + final DeathChestsState deathChestsState = DeathChestsState.get( + (ServerWorld) ((LivingEntity) (Object) this).getEntityWorld() + ); + deathChest = deathChestsState.getDeathChest( + NbtHelper.toUuid(tag.getCompound("DeathChestIdentifier")) + ); + } + } + } + + @Inject(method = "dropLoot", at = @At("HEAD"), cancellable = true) + public void dropLoot(DamageSource source, boolean recentlyHit, CallbackInfo info) { + if (deathChestPlayerUUID != null && !VanillaDeathChest.config().defenseEntities.dropItems) { + info.cancel(); + } + } + + @Inject(method = "dropEquipment", at = @At("HEAD"), cancellable = true) + public void dropEquipment( + DamageSource source, int lootingModifier, boolean recentlyHit, CallbackInfo info + ) { + if (deathChestPlayerUUID != null && !VanillaDeathChest.config().defenseEntities.dropItems) { + info.cancel(); + } + } + + @Inject(method = "dropXp", at = @At("HEAD"), cancellable = true) + public void dropXp(CallbackInfo info) { + if (deathChestPlayerUUID != null && + !VanillaDeathChest.config().defenseEntities.dropExperience) { + info.cancel(); + } + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java new file mode 100644 index 0000000..9ee0fb7 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java @@ -0,0 +1,94 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.mixin; + +import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; +import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; +import com.therandomlabs.vanilladeathchest.world.DeathChestsState; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.LockableContainerBlockEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.server.world.ServerWorld; +import org.checkerframework.checker.nullness.qual.Nullable; +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; + +@Mixin(LockableContainerBlockEntity.class) +public final class LockableContainerBlockEntityMixin implements DeathChestBlockEntity { + @Unique + private boolean isDeathChest; + + @Unique + private DeathChest deathChest; + + /** + * {@inheritDoc} + */ + @Nullable + @Override + public DeathChest getDeathChest() { + if (!isDeathChest) { + return null; + } + + if (deathChest != null) { + return deathChest; + } + + final BlockEntity blockEntity = (BlockEntity) (Object) this; + final ServerWorld world = (ServerWorld) blockEntity.getWorld(); + + if (world == null) { + return null; + } + + deathChest = DeathChestsState.get(world).getExistingDeathChest(blockEntity.getPos()); + return deathChest; + } + + /** + * {@inheritDoc} + */ + @Override + public void markAsDeathChest() { + isDeathChest = true; + } + + @Inject(method = "fromTag", at = @At("TAIL")) + private void fromTag(BlockState state, NbtCompound tag, CallbackInfo info) { + isDeathChest = tag.getBoolean("IsDeathChest"); + } + + @Inject(method = "toTag", at = @At("TAIL")) + private void toTag(NbtCompound tag, CallbackInfoReturnable info) { + if (isDeathChest) { + tag.putBoolean("IsDeathChest", true); + } + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/PlayerEntityMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/PlayerEntityMixin.java new file mode 100644 index 0000000..4e9c935 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/PlayerEntityMixin.java @@ -0,0 +1,48 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.mixin; + +import com.therandomlabs.vanilladeathchest.util.DropsList; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(value = PlayerEntity.class, priority = Integer.MAX_VALUE) +public final class PlayerEntityMixin { + //We don't redirect PlayerEntity#dropItem to prevent conflicts with other mods that do the same. + @SuppressWarnings("ConstantConditions") + @Redirect( + method = "dropItem(Lnet/minecraft/item/ItemStack;ZZ)Lnet/minecraft/entity/ItemEntity;", + at = @At( + value = "INVOKE", + target = "net/minecraft/entity/ItemEntity.setPickupDelay(I)V" + ) + ) + public void setPickupDelay(ItemEntity entity, int pickupDelay) { + entity.setPickupDelay(pickupDelay); + ((DropsList) (Object) this).getDrops().add(entity); + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java new file mode 100644 index 0000000..1dda12d --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java @@ -0,0 +1,65 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.mixin; + +import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; +import com.therandomlabs.vanilladeathchest.deathchest.DeathChestInteractions; +import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; +import net.minecraft.block.entity.BlockEntity; +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 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; + +@Mixin(ServerPlayerInteractionManager.class) +public final class ServerPlayerInteractionManagerMixin { + @Shadow + public ServerWorld world; + + @Shadow + public ServerPlayerEntity player; + + @Inject(method = "tryBreakBlock", at = @At("HEAD"), cancellable = true) + private void tryBreakBlock(BlockPos pos, CallbackInfoReturnable info) { + if (!world.getBlockState(pos).getBlock().hasBlockEntity()) { + return; + } + + final BlockEntity blockEntity = world.getBlockEntity(pos); + + if (blockEntity instanceof DeathChestBlockEntity) { + final DeathChest deathChest = ((DeathChestBlockEntity) blockEntity).getDeathChest(); + + if (deathChest != null && + !DeathChestInteractions.attemptBreak(pos, deathChest, player)) { + info.setReturnValue(false); + } + } + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockEntityMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockEntityMixin.java new file mode 100644 index 0000000..1728055 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockEntityMixin.java @@ -0,0 +1,43 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.mixin; + +import com.therandomlabs.vanilladeathchest.util.ViewerCount; +import net.minecraft.block.entity.ShulkerBoxBlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ShulkerBoxBlockEntity.class) +public final class ShulkerBoxBlockEntityMixin implements ViewerCount { + @Shadow + private int viewerCount; + + /** + * {@inheritDoc} + */ + @Override + public int getViewerCount() { + return viewerCount; + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java new file mode 100644 index 0000000..3649185 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java @@ -0,0 +1,89 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.mixin; + +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; +import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; +import net.minecraft.block.ShulkerBoxBlock; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.inventory.Inventories; +import net.minecraft.item.ItemStack; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ShulkerBoxBlock.class) +public final class ShulkerBoxBlockMixin { + @SuppressWarnings("ConstantConditions") + @Redirect( + method = "onBreak", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/World;" + + "spawnEntity(Lnet/minecraft/entity/Entity;)Z" + ) + ) + private boolean spawnEntity(World world, Entity entity) { + if (VanillaDeathChest.config().misc.dropDeathChests) { + return world.spawnEntity(entity); + } + + final BlockPos pos = entity.getBlockPos(); + final BlockEntity blockEntity = world.getBlockEntity(pos); + + if (!(blockEntity instanceof DeathChestBlockEntity) || + ((DeathChestBlockEntity) blockEntity).getDeathChest() == null) { + return world.spawnEntity(entity); + } + + final DefaultedList inventory = DefaultedList.ofSize(27, ItemStack.EMPTY); + Inventories.readNbt( + ((ItemEntity) entity).getStack().getTag().getCompound("BlockEntityTag"), inventory + ); + + boolean dropped = false; + + for (ItemStack drop : inventory) { + if (!drop.isEmpty()) { + final ItemEntity item = new ItemEntity( + world, + pos.getX() + world.random.nextFloat() * 0.5F + 0.25, + pos.getY() + world.random.nextFloat() * 0.5F + 0.25, + pos.getZ() + world.random.nextFloat() * 0.5F + 0.25, + drop + ); + item.setToDefaultPickupDelay(); + world.spawnEntity(item); + dropped = true; + } + } + + return dropped; + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java new file mode 100644 index 0000000..6208abb --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java @@ -0,0 +1,37 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.mixin; + +import java.util.List; + +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(World.class) +public interface WorldAccessor { + @Accessor + List getUnloadedBlockEntities(); +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/package-info.java b/remappedSrc/com/therandomlabs/vanilladeathchest/package-info.java new file mode 100644 index 0000000..2508bb0 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/package-info.java @@ -0,0 +1,27 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * The main VanillaDeathChest package. + */ +package com.therandomlabs.vanilladeathchest; diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestBlockEntity.java b/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestBlockEntity.java new file mode 100644 index 0000000..a771862 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestBlockEntity.java @@ -0,0 +1,46 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.util; + +import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Allows a death chest {@link net.minecraft.block.entity.BlockEntity}'s + * {@link DeathChest} to be accessed. + */ +public interface DeathChestBlockEntity { + /** + * Returns the death chest. + * + * @return the {@link DeathChest}. + */ + @Nullable + DeathChest getDeathChest(); + + /** + * Marks this block entity as a death chest. + */ + void markAsDeathChest(); +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestDefenseEntity.java b/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestDefenseEntity.java new file mode 100644 index 0000000..e434429 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestDefenseEntity.java @@ -0,0 +1,38 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.util; + +import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; + +/** + * Allows death chest defense entity information to be set. + */ +public interface DeathChestDefenseEntity { + /** + * Sets the death chest. + * + * @param deathChest a death chest. + */ + void setDeathChest(DeathChest deathChest); +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/util/DropsList.java b/remappedSrc/com/therandomlabs/vanilladeathchest/util/DropsList.java new file mode 100644 index 0000000..6997fbd --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/util/DropsList.java @@ -0,0 +1,40 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.util; + +import java.util.List; + +import net.minecraft.entity.ItemEntity; + +/** + * Allows player {@link ItemEntity} drops to be accessed. + */ +public interface DropsList { + /** + * Returns the player drops. + * + * @return the player {@link ItemEntity} drops. + */ + List getDrops(); +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/util/ViewerCount.java b/remappedSrc/com/therandomlabs/vanilladeathchest/util/ViewerCount.java new file mode 100644 index 0000000..036eaa8 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/util/ViewerCount.java @@ -0,0 +1,36 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.util; + +/** + * Allows a container's viewer count to be accessed. + */ +public interface ViewerCount { + /** + * Returns the viewer count. + * + * @return the viewer count. + */ + int getViewerCount(); +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/util/package-info.java b/remappedSrc/com/therandomlabs/vanilladeathchest/util/package-info.java new file mode 100644 index 0000000..861c538 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/util/package-info.java @@ -0,0 +1,27 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * Utility classes for VanillaDeathChest. + */ +package com.therandomlabs.vanilladeathchest.util; diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java b/remappedSrc/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java new file mode 100644 index 0000000..3c96411 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java @@ -0,0 +1,225 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.therandomlabs.vanilladeathchest.world; + +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; +import com.therandomlabs.vanilladeathchest.mixin.WorldAccessor; +import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; +import net.fabricmc.fabric.api.util.NbtType; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtHelper; +import net.minecraft.nbt.NbtList; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.PersistentState; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Persistent death chests state. + */ +public final class DeathChestsState extends PersistentState { + private final ServerWorld world; + private final Map deathChests = new HashMap<>(); + private final Map existingDeathChests = new HashMap<>(); + private final Queue queuedDeathChests = + new PriorityQueue<>(Comparator.comparing(DeathChest::getCreationTime)); + + private DeathChestsState(String name, ServerWorld world) { + super(name); + this.world = world; + } + + /** + * {@inheritDoc} + */ + @Override + public void fromNbt(NbtCompound tag) { + deathChests.clear(); + tag.getList("DeathChests", NbtType.COMPOUND).stream(). + map(deathChestTag -> DeathChest.fromTag(world, (NbtCompound) deathChestTag)). + forEach(deathChest -> deathChests.put(deathChest.getIdentifier(), deathChest)); + + existingDeathChests.clear(); + tag.getList("ExistingDeathChests", NbtType.INT_ARRAY).stream(). + map(NbtHelper::toUuid). + map(deathChests::get). + forEach(deathChest -> existingDeathChests.put(deathChest.getPos(), deathChest)); + + queuedDeathChests.clear(); + tag.getList("QueuedDeathChests", NbtType.COMPOUND).stream(). + map(deathChestTag -> DeathChest.fromTag(world, (NbtCompound) deathChestTag)). + forEach(queuedDeathChests::add); + } + + /** + * {@inheritDoc} + */ + @Override + public NbtCompound writeNbt(NbtCompound tag) { + final NbtList deathChestsList = new NbtList(); + deathChests.values().stream(). + map(deathChest -> deathChest.toTag(new NbtCompound())). + forEach(deathChestsList::add); + tag.put("DeathChests", deathChestsList); + + final NbtList existingDeathChestsList = new NbtList(); + existingDeathChests.values().stream(). + map(DeathChest::getIdentifier). + map(NbtHelper::fromUuid). + forEach(existingDeathChestsList::add); + tag.put("ExistingDeathChests", existingDeathChestsList); + + final NbtList queuedDeathChestsList = new NbtList(); + queuedDeathChests.stream(). + map(deathChest -> deathChest.toTag(new NbtCompound())). + forEach(queuedDeathChestsList::add); + tag.put("QueuedDeathChests", queuedDeathChestsList); + + return tag; + } + + /** + * Returns the identifiers of all placed death chests. + * + * @return a {@link Set} of {@link UUID}s. + */ + public Set getDeathChestIdentifiers() { + return deathChests.keySet(); + } + + /** + * Returns the identifier strings of all placed death chests. + * + * @return a {@link Set} of strings. + */ + public Set getDeathChestIdentifierStrings() { + return getDeathChestIdentifiers().stream().map(UUID::toString).collect(Collectors.toSet()); + } + + /** + * Returns all placed death chests. + * + * @return a {@link Collection} of all placed death chests. + */ + public Collection getDeathChests() { + return new HashSet<>(deathChests.values()); + } + + /** + * Returns the death chest with the specified identifier. + * + * @param identifier an identifier. + * @return the {@link DeathChest} with the specified identifier. + */ + @Nullable + public DeathChest getDeathChest(UUID identifier) { + return deathChests.get(identifier); + } + + /** + * Returns all existing death chests. + * + * @return a {@link Collection} of all existing death chests. + */ + public Collection getExistingDeathChests() { + return new HashSet<>(existingDeathChests.values()); + } + + /** + * Returns the existing death chest at the specified position. + * + * @param pos a position. + * @return the existing {@link DeathChest} at the specified {@link BlockPos}, + * or {@code null} if it does not exist. + */ + @Nullable + public DeathChest getExistingDeathChest(BlockPos pos) { + final DeathChest deathChest = existingDeathChests.get(pos); + return deathChest == null ? existingDeathChests.get(pos.west()) : deathChest; + } + + /** + * Adds an existing death chest. + * + * @param deathChest a {@link DeathChest}. + */ + public void addDeathChest(DeathChest deathChest) { + deathChests.put(deathChest.getIdentifier(), deathChest); + existingDeathChests.put(deathChest.getPos(), deathChest); + } + + /** + * Returns a queue of all unplaced death chests. Changes to this queue are kept. + * + * @return a queue of all unplaced death chests. + */ + public Queue getQueuedDeathChests() { + return queuedDeathChests; + } + + /** + * Returns the {@link DeathChestsState} instance for the specified world. + * + * @param world a {@link ServerWorld}. + * @return the {@link DeathChestsState} instance for the specified world. + */ + public static DeathChestsState get(ServerWorld world) { + return world.getPersistentStateManager().getOrCreate( + () -> new DeathChestsState("deathchests", world), "deathchests" + ); + } + + /** + * Called when a block entity is unloaded. + * + * @param blockEntity a {@link BlockEntity}. + * @param world a {@link ServerWorld}. + */ + public static void onBlockEntityUnload(BlockEntity blockEntity, ServerWorld world) { + //Fabric API invokes this event in three different locations, but only two of them are + //when a block entity is removed. The other one is just when a chunk is unloaded. + //We can differentiate between these by checking whether the block entity is in + //World#unloadedBlockEntities. + if (((WorldAccessor) world).getUnloadedBlockEntities().contains(blockEntity)) { + return; + } + + if (blockEntity instanceof DeathChestBlockEntity) { + final DeathChest deathChest = ((DeathChestBlockEntity) blockEntity).getDeathChest(); + get(world).existingDeathChests.values().remove(deathChest); + } + } +} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/world/package-info.java b/remappedSrc/com/therandomlabs/vanilladeathchest/world/package-info.java new file mode 100644 index 0000000..9f14096 --- /dev/null +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/world/package-info.java @@ -0,0 +1,27 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021 TheRandomLabs + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * World-related classes for VanillaDeathChest. + */ +package com.therandomlabs.vanilladeathchest.world; From 7fce8625666ef12d95657f5f452be9a5171f9899 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 09:10:33 +0200 Subject: [PATCH 03/28] NBT-Related renaming --- .../deathchest/DeathChest.java | 38 +++++++++---------- .../deathchest/DeathChestPlacer.java | 10 ++--- .../mixin/LivingEntityMixin.java | 10 ++--- .../LockableContainerBlockEntityMixin.java | 10 ++--- .../mixin/ShulkerBoxBlockMixin.java | 2 +- .../world/DeathChestsState.java | 24 ++++++------ 6 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java index b6890f2..bed3fc9 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java @@ -37,10 +37,10 @@ import net.minecraft.entity.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; import net.minecraft.nbt.NbtHelper; -import net.minecraft.nbt.Tag; import net.minecraft.server.OperatorEntry; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; @@ -239,27 +239,27 @@ public boolean isProtectedFrom(PlayerEntity player) { } /** - * Serializes this death chest to a {@link CompoundTag}. + * Serializes this death chest to a {@link NbtCompound}. * - * @param tag a {@link CompoundTag}. - * @return the {@link CompoundTag}. + * @param tag a {@link NbtCompound}. + * @return the {@link NbtCompound}. */ - public CompoundTag toTag(CompoundTag tag) { + public NbtCompound writeNbt(NbtCompound tag) { tag.put("Identifier", NbtHelper.fromUuid(identifier)); tag.put("PlayerUUID", NbtHelper.fromUuid(playerUUID)); - final ListTag itemsList = new ListTag(); + final NbtList itemsList = new NbtList(); for (ItemEntity item : items) { - final CompoundTag itemTag = item.toTag(new CompoundTag()); - item.writeCustomDataToTag(itemTag); + final NbtCompound itemTag = item.writeNbt(new NbtCompound()); + item.writeCustomDataToNbt(itemTag); itemsList.add(itemTag); } tag.put("Items", itemsList); - final ListTag inventoryList = new ListTag(); - inventory.serialize(inventoryList); + final NbtList inventoryList = new NbtList(); + inventory.writeNbt(inventoryList); tag.put("Inventory", inventoryList); tag.putLong("CreationTime", creationTime); @@ -270,26 +270,26 @@ public CompoundTag toTag(CompoundTag tag) { } /** - * Deserializes a death chest from a {@link CompoundTag}. + * Deserializes a death chest from a {@link NbtCompound}. * * @param world a {@link ServerWorld}. - * @param tag a {@link CompoundTag}. + * @param tag a {@link NbtCompound}. * @return the deserialized {@link DeathChest}. */ @SuppressWarnings("ConstantConditions") - public static DeathChest fromTag(ServerWorld world, CompoundTag tag) { + public static DeathChest fromTag(ServerWorld world, NbtCompound tag) { final List items = new ArrayList<>(); - for (Tag itemTag : tag.getList("Items", NbtType.COMPOUND)) { + for (NbtElement itemTag : tag.getList("Items", NbtType.COMPOUND)) { final ItemEntity item = new ItemEntity(EntityType.ITEM, world); - item.fromTag((CompoundTag) itemTag); - item.readCustomDataFromTag((CompoundTag) itemTag); + item.readNbt((NbtCompound) itemTag); + item.readCustomDataFromNbt((NbtCompound) itemTag); items.add(item); } //We can pass in a null player here because deserialize doesn't use the player. final PlayerInventory inventory = new PlayerInventory(null); - inventory.deserialize(tag.getList("Inventory", NbtType.COMPOUND)); + inventory.readNbt(tag.getList("Inventory", NbtType.COMPOUND)); return new DeathChest( NbtHelper.toUuid(tag.get("Identifier")), world, diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java index 67c496d..cb8a6b9 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java @@ -54,7 +54,7 @@ import net.minecraft.inventory.Inventories; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.StringNbtReader; import net.minecraft.server.world.ServerWorld; import net.minecraft.text.LiteralText; @@ -201,13 +201,13 @@ public static void spawnDefenseEntities(DeathChest deathChest) { for (int i = 0; i < config.spawnCount; i++) { //The following spawn logic has been taken from SummonCommand. - CompoundTag tag; + NbtCompound tag; try { tag = StringNbtReader.parse(config.nbtTag); } catch (CommandSyntaxException ex) { //This should not happen. - tag = new CompoundTag(); + tag = new NbtCompound(); } final boolean emptyTag = tag.isEmpty(); @@ -375,12 +375,12 @@ private static ContainerConsumptionResult consumeContainerInInventory( continue; } - final CompoundTag tag = stack.getTag(); + final NbtCompound tag = stack.getTag(); if (tag != null) { final DefaultedList inventory = DefaultedList.ofSize(27, ItemStack.EMPTY); - Inventories.fromTag(tag.getCompound("BlockEntityTag"), inventory); + Inventories.readNbt(tag.getCompound("BlockEntityTag"), inventory); //The shulker box must be empty. if (inventory.stream().anyMatch(itemStack -> !itemStack.isEmpty())) { diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java index 305d292..af9e48b 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java @@ -41,7 +41,7 @@ import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; @@ -186,8 +186,8 @@ public void tick(CallbackInfo info) { } } - @Inject(method = "writeCustomDataToTag", at = @At("HEAD")) - public void writeCustomDataToTag(CompoundTag tag, CallbackInfo info) { + @Inject(method = "writeCustomDataToNbt", at = @At("HEAD")) + public void writeCustomDataToNbt(NbtCompound tag, CallbackInfo info) { if (deathChestPlayerUUID != null) { if (deathChest != null) { tag.put( @@ -199,8 +199,8 @@ public void writeCustomDataToTag(CompoundTag tag, CallbackInfo info) { } } - @Inject(method = "readCustomDataFromTag", at = @At("HEAD")) - public void readCustomDataFromTag(CompoundTag tag, CallbackInfo info) { + @Inject(method = "readCustomDataFromNbt", at = @At("HEAD")) + public void readCustomDataFromNbt(NbtCompound tag, CallbackInfo info) { if (tag.contains("DeathChestPlayer")) { deathChestPlayerUUID = NbtHelper.toUuid(tag.get("DeathChestPlayer")); diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java index 7691b9a..5a9c41a 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java @@ -29,7 +29,7 @@ import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.LockableContainerBlockEntity; -import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtCompound; import net.minecraft.server.world.ServerWorld; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.asm.mixin.Mixin; @@ -80,13 +80,13 @@ public void markAsDeathChest() { isDeathChest = true; } - @Inject(method = "fromTag", at = @At("TAIL")) - private void fromTag(BlockState state, CompoundTag tag, CallbackInfo info) { + @Inject(method = "writeNbt", at = @At("TAIL")) + private void writeNbt(BlockState state, NbtCompound tag, CallbackInfo info) { isDeathChest = tag.getBoolean("IsDeathChest"); } - @Inject(method = "toTag", at = @At("TAIL")) - private void toTag(CompoundTag tag, CallbackInfoReturnable info) { + @Inject(method = "readNbt", at = @At("TAIL")) + private void readNbt(NbtCompound tag, CallbackInfoReturnable info) { if (isDeathChest) { tag.putBoolean("IsDeathChest", true); } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java index 1f2a497..3649185 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java @@ -63,7 +63,7 @@ private boolean spawnEntity(World world, Entity entity) { } final DefaultedList inventory = DefaultedList.ofSize(27, ItemStack.EMPTY); - Inventories.fromTag( + Inventories.readNbt( ((ItemEntity) entity).getStack().getTag().getCompound("BlockEntityTag"), inventory ); diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java index 9cf6f70..775b07e 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java @@ -39,9 +39,9 @@ import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; import net.fabricmc.fabric.api.util.NbtType; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; +import net.minecraft.nbt.NbtList; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.world.PersistentState; @@ -58,7 +58,7 @@ public final class DeathChestsState extends PersistentState { new PriorityQueue<>(Comparator.comparing(DeathChest::getCreationTime)); private DeathChestsState(String name, ServerWorld world) { - super(name); + super(); this.world = world; } @@ -66,10 +66,10 @@ private DeathChestsState(String name, ServerWorld world) { * {@inheritDoc} */ @Override - public void fromTag(CompoundTag tag) { + public void fromTag(NbtCompound tag) { deathChests.clear(); tag.getList("DeathChests", NbtType.COMPOUND).stream(). - map(deathChestTag -> DeathChest.fromTag(world, (CompoundTag) deathChestTag)). + map(deathChestTag -> DeathChest.fromTag(world, (NbtCompound) deathChestTag)). forEach(deathChest -> deathChests.put(deathChest.getIdentifier(), deathChest)); existingDeathChests.clear(); @@ -80,7 +80,7 @@ public void fromTag(CompoundTag tag) { queuedDeathChests.clear(); tag.getList("QueuedDeathChests", NbtType.COMPOUND).stream(). - map(deathChestTag -> DeathChest.fromTag(world, (CompoundTag) deathChestTag)). + map(deathChestTag -> DeathChest.fromTag(world, (NbtCompound) deathChestTag)). forEach(queuedDeathChests::add); } @@ -88,23 +88,23 @@ public void fromTag(CompoundTag tag) { * {@inheritDoc} */ @Override - public CompoundTag toTag(CompoundTag tag) { - final ListTag deathChestsList = new ListTag(); + public NbtCompound writeNbt(NbtCompound tag) { + final NbtList deathChestsList = new NbtList(); deathChests.values().stream(). - map(deathChest -> deathChest.toTag(new CompoundTag())). + map(deathChest -> deathChest.writeNbt(new NbtCompound())). forEach(deathChestsList::add); tag.put("DeathChests", deathChestsList); - final ListTag existingDeathChestsList = new ListTag(); + final NbtList existingDeathChestsList = new NbtList(); existingDeathChests.values().stream(). map(DeathChest::getIdentifier). map(NbtHelper::fromUuid). forEach(existingDeathChestsList::add); tag.put("ExistingDeathChests", existingDeathChestsList); - final ListTag queuedDeathChestsList = new ListTag(); + final NbtList queuedDeathChestsList = new NbtList(); queuedDeathChests.stream(). - map(deathChest -> deathChest.toTag(new CompoundTag())). + map(deathChest -> deathChest.writeNbt(new NbtCompound())). forEach(queuedDeathChestsList::add); tag.put("QueuedDeathChests", queuedDeathChestsList); From 458accfd4b34acc7dbde268b552bb5e821b511f2 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 12:07:26 +0200 Subject: [PATCH 04/28] `hasBlockEntity` moved from `AbstractBlock` to `AbstractBlockState` --- .../therandomlabs/vanilladeathchest/deathchest/DeathChest.java | 2 +- .../vanilladeathchest/deathchest/DeathChestInteractions.java | 2 +- .../com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java | 2 +- .../mixin/ServerPlayerInteractionManagerMixin.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java index bed3fc9..fe7d8ee 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java @@ -201,7 +201,7 @@ public void setLocked(boolean flag) { public boolean exists() { //We don't bother checking the east block, as it is impossible to only break one //block in a double death chest. - if (!world.getBlockState(pos).getBlock().hasBlockEntity()) { + if (!world.getBlockState(pos).hasBlockEntity()) { return false; } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java index c3dcdb5..978f9cc 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java @@ -68,7 +68,7 @@ public static ActionResult interact( return ActionResult.PASS; } - if (!world.getBlockState(blockHitResult.getBlockPos()).getBlock().hasBlockEntity()) { + if (!world.getBlockState(blockHitResult.getBlockPos()).hasBlockEntity()) { return ActionResult.PASS; } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java index 71b5e13..a779d2c 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java @@ -49,7 +49,7 @@ public final class BlockMixin { private void onBreak( World world, BlockPos pos, BlockState state, PlayerEntity player, CallbackInfo info ) { - if ((Object) this instanceof ShulkerBoxBlock || !state.getBlock().hasBlockEntity()) { + if ((Object) this instanceof ShulkerBoxBlock || !state.hasBlockEntity()) { return; } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java index 1dda12d..3a7cb21 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java @@ -47,7 +47,7 @@ public final class ServerPlayerInteractionManagerMixin { @Inject(method = "tryBreakBlock", at = @At("HEAD"), cancellable = true) private void tryBreakBlock(BlockPos pos, CallbackInfoReturnable info) { - if (!world.getBlockState(pos).getBlock().hasBlockEntity()) { + if (!world.getBlockState(pos).hasBlockEntity()) { return; } From cc96f09e4af8d91cbab9ab78909c8a473cb55860 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 12:17:51 +0200 Subject: [PATCH 05/28] Initial update for DeathChestState handling --- .../world/DeathChestsState.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java index 775b07e..fc401c3 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; import java.util.PriorityQueue; import java.util.Queue; import java.util.Set; @@ -42,6 +43,7 @@ import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; import net.minecraft.nbt.NbtList; +import net.minecraft.scoreboard.ScoreboardState; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.world.PersistentState; @@ -62,11 +64,7 @@ private DeathChestsState(String name, ServerWorld world) { this.world = world; } - /** - * {@inheritDoc} - */ - @Override - public void fromTag(NbtCompound tag) { + public DeathChestsState readNbt(NbtCompound tag) { deathChests.clear(); tag.getList("DeathChests", NbtType.COMPOUND).stream(). map(deathChestTag -> DeathChest.fromTag(world, (NbtCompound) deathChestTag)). @@ -82,6 +80,8 @@ public void fromTag(NbtCompound tag) { tag.getList("QueuedDeathChests", NbtType.COMPOUND).stream(). map(deathChestTag -> DeathChest.fromTag(world, (NbtCompound) deathChestTag)). forEach(queuedDeathChests::add); + + return this; } /** @@ -198,7 +198,8 @@ public Queue getQueuedDeathChests() { */ public static DeathChestsState get(ServerWorld world) { return world.getPersistentStateManager().getOrCreate( - () -> new DeathChestsState("deathchests", world), "deathchests" + (data) -> DeathChestsState.stateFromNbt(world, data), + () -> DeathChestsState.createState(world), "deathchests" ); } @@ -222,4 +223,15 @@ public static void onBlockEntityUnload(BlockEntity blockEntity, ServerWorld worl get(world).existingDeathChests.values().remove(deathChest); } } + + public static DeathChestsState createState(ServerWorld world) { + //Is name really unused? + DeathChestsState deathChestsState = new DeathChestsState("deathchests", world); + Objects.requireNonNull(deathChestsState); + return deathChestsState; + } + + public static DeathChestsState stateFromNbt(ServerWorld world, NbtCompound nbt) { + return createState(world).readNbt(nbt); + } } From 3c7871f5d70e79b3bf546659f4bdbfb16f37cc1c Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 12:19:46 +0200 Subject: [PATCH 06/28] Using getter for `.abilities` --- .../therandomlabs/vanilladeathchest/deathchest/DeathChest.java | 2 +- .../vanilladeathchest/deathchest/DeathChestInteractions.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java index fe7d8ee..efd09c4 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java @@ -223,7 +223,7 @@ public boolean isProtectedFrom(PlayerEntity player) { final VDCConfig.Protection config = VanillaDeathChest.config().protection; if (!config.enable || playerUUID.equals(player.getUuid()) || - (config.bypassInCreativeMode && player.abilities.creativeMode)) { + (config.bypassInCreativeMode && player.getAbilities().creativeMode)) { return false; } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java index 978f9cc..9b8ca20 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java @@ -113,7 +113,7 @@ public static boolean attemptInteract(DeathChest deathChest, ServerPlayerEntity final int amount = config.amountToConsume; if (stack.getItem() == config.item) { - if (amount == 0 || player.abilities.creativeMode) { + if (amount == 0 || player.getAbilities().creativeMode) { deathChest.setLocked(false); return true; } From 7f3e749d6b43f6c59baf802b2f9f462a064630a9 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 12:20:49 +0200 Subject: [PATCH 07/28] accessing rotation data using getters --- .../vanilladeathchest/deathchest/DeathChestPlacer.java | 2 +- .../vanilladeathchest/mixin/LivingEntityMixin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java index cb8a6b9..1138c24 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java @@ -216,7 +216,7 @@ public static void spawnDefenseEntities(DeathChest deathChest) { final Entity entity = EntityType.loadEntityWithPassengers( tag, world, spawnedEntity -> { spawnedEntity.refreshPositionAndAngles( - x, y, z, spawnedEntity.yaw, spawnedEntity.pitch + x, y, z, spawnedEntity.getYaw(), spawnedEntity.getPitch() ); return spawnedEntity; } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java index af9e48b..2cf8921 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java @@ -180,7 +180,7 @@ public void tick(CallbackInfo info) { squaredDistanceFromPlayer > config.maxSquaredDistanceFromPlayer) { entity.refreshPositionAndAngles( pos.getX() + 0.5, pos.getY() + 1.0, pos.getZ() + 0.5, - entity.yaw, entity.pitch + entity.getYaw(), entity.getPitch() ); } } From c2327c2869039b08f64e90dcbc603bc1f42fe6a1 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 12:22:02 +0200 Subject: [PATCH 08/28] Using getter for `.inventory` --- .../com/therandomlabs/vanilladeathchest/command/VDCCommand.java | 2 +- .../vanilladeathchest/mixin/LivingEntityMixin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/command/VDCCommand.java b/src/main/java/com/therandomlabs/vanilladeathchest/command/VDCCommand.java index 2bab94d..4c1770c 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/command/VDCCommand.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/command/VDCCommand.java @@ -140,7 +140,7 @@ private static int executeRestoreInventory( Collection players ) throws CommandSyntaxException { for (ServerPlayerEntity player : players) { - final PlayerInventory inventory = player.inventory; + final PlayerInventory inventory = player.getInventory(); for (int i = 0; i < inventory.size(); i++) { inventory.setStack(i, deathChest.getInventory().getStack(i).copy()); diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java index 2cf8921..2029f49 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java @@ -90,7 +90,7 @@ public void dropHead(CallbackInfo info) { //We can't pass in null here because Campanion mixins into setStack and needs the //player. inventory = new PlayerInventory((PlayerEntity) (Object) this); - final PlayerInventory oldInventory = ((PlayerEntity) (Object) this).inventory; + final PlayerInventory oldInventory = ((PlayerEntity) (Object) this).getInventory(); for (int i = 0; i < oldInventory.size(); i++) { inventory.setStack(i, oldInventory.getStack(i).copy()); From b2d0d0186cf517179b82ec2b1dc0b96303f857f6 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 12:30:41 +0200 Subject: [PATCH 09/28] Using new `Entity.RemovalReason` --- .../vanilladeathchest/deathchest/DeathChestPlacer.java | 2 +- .../vanilladeathchest/mixin/LivingEntityMixin.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java index 1138c24..be4d27f 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java @@ -247,7 +247,7 @@ private static void placeAndDropRemaining(DeathChest deathChest) { for (ItemEntity drop : allItems) { if (!items.contains(drop)) { - if (drop.removed) { + if (drop.isRemoved()) { world.spawnEntity(new ItemEntity( world, drop.getX(), drop.getY(), drop.getZ(), drop.getStack().copy() )); diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java index 2029f49..1e28790 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java @@ -113,7 +113,7 @@ public void dropTail(CallbackInfo info) { return; } - drops.forEach(Entity::remove); + drops.forEach(Entity::discard); final DeathChestsState deathChestsState = DeathChestsState.get(world); final BlockPos pos = entity.getBlockPos(); final DeathChest deathChest = new DeathChest( From 00f0b5ea54220e939f102692eab9e5335c848da0 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 12:36:51 +0200 Subject: [PATCH 10/28] Replaced deprecated ModMenu-API --- .../vanilladeathchest/VDCModMenuEntryPoint.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java b/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java index de3d3bd..0984ec9 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java @@ -23,19 +23,16 @@ package com.therandomlabs.vanilladeathchest; -import io.github.prospector.modmenu.api.ConfigScreenFactory; -import io.github.prospector.modmenu.api.ModMenuApi; +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; /** * The Mod Menu entry point for VanillaDeathChest. */ public final class VDCModMenuEntryPoint implements ModMenuApi { - /** - * {@inheritDoc} - */ @Override public ConfigScreenFactory getModConfigScreenFactory() { - return parent -> AutoConfig.getConfigScreen(VDCConfig.class, parent).get(); + return screen -> AutoConfig.getConfigScreen(VDCConfig.class, screen).get(); } } From 084fc8bd8ffc2eb65c62f3d6dd10868715b41f48 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Thu, 17 Jun 2021 12:41:02 +0200 Subject: [PATCH 11/28] updated minecraft version --- src/main/resources/fabric.mod.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 1040a88..97b14af 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -29,6 +29,6 @@ "depends": { "fabricloader": ">=0.7.4", "fabric": "*", - "minecraft": "1.16.x" + "minecraft": "1.17.x" } } From ee89e97dd00accaabdbcbaee5517eb7af6b2c6ad Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Fri, 18 Jun 2021 11:15:17 +0200 Subject: [PATCH 12/28] Autoconfig compile-time update --- .../vanilladeathchest/VanillaDeathChest.java | 5 +- .../vanilladeathchest/VDCConfig.java | 241 +++++++++--------- .../VDCModMenuEntryPoint.java | 2 +- .../vanilladeathchest/VanillaDeathChest.java | 19 +- 4 files changed, 133 insertions(+), 134 deletions(-) diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java b/remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java index f73ba74..49af38a 100644 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java +++ b/remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java @@ -109,10 +109,7 @@ public static VDCConfig config() { */ public static void reloadConfig() { if (serializer == null) { - AutoConfig.register(VDCConfig.class, (definition, configClass) -> { - serializer = new TOMLConfigSerializer<>(definition, configClass); - return serializer; - }); + AutoConfig.register(VDCConfig.class, Toml4jConfigSerializer::new); } else { serializer.reloadFromDisk(); } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java b/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java index 3298550..ac956a9 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java @@ -30,13 +30,11 @@ import java.util.Set; import java.util.stream.Collectors; -import com.electronwill.nightconfig.core.conversion.SpecDoubleInRange; -import com.electronwill.nightconfig.core.conversion.SpecIntInRange; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.therandomlabs.autoconfigtoml.TOMLConfigSerializer; -import me.sargunvohra.mcmods.autoconfig1u.ConfigData; -import me.sargunvohra.mcmods.autoconfig1u.annotation.Config; -import me.sargunvohra.mcmods.autoconfig1u.annotation.ConfigEntry; +import me.shedaniel.autoconfig.ConfigData; +import me.shedaniel.autoconfig.annotation.Config; +import me.shedaniel.autoconfig.annotation.ConfigEntry; +import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.Comment; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.item.Item; @@ -49,103 +47,102 @@ import org.checkerframework.checker.nullness.qual.Nullable; @SuppressWarnings("CanBeFinal") -@TOMLConfigSerializer.Comment("VanillaDeathChest configuration.") +//@Comment("VanillaDeathChest configuration.") @Config(name = VanillaDeathChest.MOD_ID) public final class VDCConfig implements ConfigData { public static final class Spawning implements ConfigData { - @TOMLConfigSerializer.Comment({ - "The death chest container type.", - "SINGLE_CHEST: Only single chests.", - "SINGLE_OR_DOUBLE_CHEST: Single or double chests.", - "SINGLE_SHULKER_BOX: Single shulker boxes.", + @Comment( + "The death chest container type.\n" + + "SINGLE_CHEST: Only single chests.\n" + + "SINGLE_OR_DOUBLE_CHEST: Single or double chests.\n" + + "SINGLE_SHULKER_BOX: Single shulker boxes.\n" + "SINGLE_OR_DOUBLE_SHULKER_BOX: Single or double shulker boxes." - }) + ) @ConfigEntry.Gui.Tooltip public ContainerType containerType = ContainerType.SINGLE_OR_DOUBLE_CHEST; - @TOMLConfigSerializer.Comment({ - "The color of the shulker box if the container type is a shulker box.", - "WHITE: White.", - "ORANGE: Orange.", - "MAGENTA: Magenta.", - "LIGHT_BLUE: Light blue.", - "YELLOW: Yellow.", - "LIME: Lime.", - "PINK: Pink.", - "GRAY: Gray.", - "LIGHT_GRAY: Light gray.", - "CYAN: Cyan.", - "PURPLE: Purple.", - "BLUE: Blue.", - "BROWN: Brown.", - "GREEN: Green.", - "RED: Red.", - "BLACK: Black.", + @Comment( + "The color of the shulker box if the container type is a shulker box.\n"+ + "WHITE: White.\n" + + "ORANGE: Orange.\n" + + "MAGENTA: Magenta.\n" + + "LIGHT_BLUE: Light blue.\n" + + "YELLOW: Yellow.\n" + + "LIME: Lime.\n" + + "PINK: Pink.\n" + + "GRAY: Gray.\n" + + "LIGHT_GRAY: Light gray.\n" + + "CYAN: Cyan.\n" + + "PURPLE: Purple.\n" + + "BLUE: Blue.\n" + + "BROWN: Brown.\n" + + "GREEN: Green.\n" + + "RED: Red.\n" + + "BLACK: Black.\n" + "RANDOM: Random color." - }) + ) @ConfigEntry.Gui.Tooltip public ShulkerBoxColor shulkerBoxColor = ShulkerBoxColor.WHITE; - @TOMLConfigSerializer.Comment( + @Comment( "The dimensions that death chests should or should not spawn in." ) @ConfigEntry.Gui.Tooltip public List dimensions = new ArrayList<>(); - @TOMLConfigSerializer.Comment({ - "Whether the dimensions list should be a blacklist or a whitelist.", - "BLACKLIST: blacklist.", + @Comment("Whether the dimensions list should be a blacklist or a whitelist.\n"+ + "BLACKLIST: blacklist\n"+ "WHITELIST: whitelist." - }) + ) @ConfigEntry.Gui.Tooltip public DimensionListBehavior dimensionsBehavior = DimensionListBehavior.BLACKLIST; - @SpecIntInRange(min = 1, max = Integer.MAX_VALUE) - @TOMLConfigSerializer.Comment( + @ConfigEntry.BoundedDiscrete(min = 1, max = 1000) + @Comment( "The radius around the location of a player's death in which a suitable death " + "chest placement location should be searched for." ) @ConfigEntry.Gui.Tooltip public int locationSearchRadius = 8; - @TOMLConfigSerializer.Comment( + @Comment( "Causes a death chest to be forcibly placed at the location of a player's death " + "if no suitable locations are found nearby." ) @ConfigEntry.Gui.Tooltip public boolean forcePlacementIfNoSuitableLocation = true; - @TOMLConfigSerializer.Comment("Requires death chest placement to be on solid blocks.") + @Comment("Requires death chest placement to be on solid blocks.") @ConfigEntry.Gui.Tooltip public boolean requirePlacementOnSolidBlocks; - @TOMLConfigSerializer.Comment( + @Comment( "A regular expression that matches the registry names of items that can be " + "placed in death chests." ) @ConfigEntry.Gui.Tooltip public String registryNameRegex = ".+"; - @TOMLConfigSerializer.Comment({ + @Comment( "Causes death chests to only be spawned if the necessary container is in the " + - "player's inventory.", + "player's inventory.\n"+ "If this is enabled, the container is consumed if it is found." - }) + ) @ConfigEntry.Gui.Tooltip public boolean useContainerInInventory; - @TOMLConfigSerializer.Comment({ - "The display name of the death chest container.", + @Comment( + "The display name of the death chest container.\n"+ "Set this to an empty string to cause a custom display name to not be used." - }) + ) @ConfigEntry.Gui.Tooltip public String containerDisplayName = "Death Chest"; - @TOMLConfigSerializer.Comment({ - "The message sent to a player after a death chest is placed when they die.", - "The X, Y and Z coordinates are provided as arguments.", + @Comment( + "The message sent to a player after a death chest is placed when they die.\n"+ + "The X, Y and Z coordinates are provided as arguments.\n"+ "Set this to an empty string to disable this message." - }) + ) @ConfigEntry.Gui.Tooltip public String spawnMessage = "Death chest spawned at [%s, %s, %s]"; @@ -197,50 +194,51 @@ public boolean isDimensionEnabled(World world) { } public static final class KeyItem implements ConfigData { - @TOMLConfigSerializer.Comment({ - "The registry name of the key item.", - "A player must be holding this item in their main hand to unlock a death chest.", + @Comment( + "The registry name of the key item.\n"+ + "A player must be holding this item in their main hand to unlock a death chest.\n"+ "Set this to an empty string to allow death chests to be unlocked without an item." - }) + ) @ConfigEntry.Gui.Tooltip public String registryName = ""; - @SpecIntInRange(min = 0, max = Short.MAX_VALUE) - @TOMLConfigSerializer.Comment({ - "The meta value of the key item.", + //@IntRange(from = 0, to= Integer.MAX_VALUE) + @ConfigEntry.BoundedDiscrete(min = 0, max = Short.MAX_VALUE) + @Comment( + "The meta value of the key item.\n"+ "Set this to " + Short.MAX_VALUE + " to not require a specific meta value." - }) + ) @ConfigEntry.Gui.Tooltip public int meta = Short.MAX_VALUE; - @TOMLConfigSerializer.Comment({ - "The key consumption behavior.", - "CONSUME: Consume the item.", + @Comment( + "The key consumption behavior.\n"+ + "CONSUME: Consume the item.\n"+ "DAMAGE: Damage the item." - }) + ) @ConfigEntry.Gui.Tooltip public KeyConsumptionBehavior consumptionBehavior = KeyConsumptionBehavior.CONSUME; - @SpecIntInRange(min = 0, max = Short.MAX_VALUE) - @TOMLConfigSerializer.Comment({ - "The amount by which the key item should be consumed.", + @ConfigEntry.BoundedDiscrete(min = 0, max = 100) + @Comment( + "The amount by which the key item should be consumed.\n"+ "If the key item cannot be consumed this many times, the death chest will not " + - "be unlocked.", + "be unlocked.\n"+ "Players in creative mode will not have their key item consumed." - }) + ) @ConfigEntry.Gui.Tooltip public int amountToConsume = 1; - @TOMLConfigSerializer.Comment({ - "The message that is sent to the player when they fail to unlock a death chest.", + @Comment( + "The message that is sent to the player when they fail to unlock a death chest.\n"+ "This string takes the required amount (%1$s) and display name (%2$s) of the " + - "item as arguments.", + "item as arguments.\n"+ "Set this to an empty string to disable this message." - }) + ) @ConfigEntry.Gui.Tooltip public String unlockFailureMessage = "You need %s of %s to retrieve your items"; - @TOMLConfigSerializer.Comment( + @Comment( "Whether the unlock failed message should be sent as a status message rather " + "than a chat message." ) @@ -270,52 +268,52 @@ public void validatePostLoad() { } public static final class DefenseEntities implements ConfigData { - @TOMLConfigSerializer.Comment({ - "The registry name of the defense entity.", - "If the defense entity is a living entity, it will not automatically despawn.", - "If the defense entity can have a revenge target, then the revenge target will " + - "be set to its player.", + @Comment( + "The registry name of the defense entity.\n"+ + "If the defense entity is a living entity, it will not automatically despawn.\n"+ + "If the defense entity can have a revenge target, then the revenge target will\n" + + "be set to its player.\n" + "Set this to an empty string to disable defense entities." - }) + ) @ConfigEntry.Gui.Tooltip public String registryName = ""; - @TOMLConfigSerializer.Comment("Custom NBT data for defense entities in JSON format.") + @Comment("Custom NBT data for defense entities in JSON format.") @ConfigEntry.Gui.Tooltip public String nbtTag = "{}"; - @TOMLConfigSerializer.Comment("Causes defense entities to drop experience.") + @Comment("Causes defense entities to drop experience.") @ConfigEntry.Gui.Tooltip public boolean dropExperience; - @TOMLConfigSerializer.Comment("Causes defense entities to drop items.") + @Comment("Causes defense entities to drop items.") @ConfigEntry.Gui.Tooltip public boolean dropItems; - @SpecIntInRange(min = 1, max = Integer.MAX_VALUE) - @TOMLConfigSerializer.Comment( + @ConfigEntry.BoundedDiscrete(min = 1, max = 100) + @Comment( "The number of defense entities that are spawned when a death chest is placed." ) @ConfigEntry.Gui.Tooltip public int spawnCount = 3; - @SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) - @TOMLConfigSerializer.Comment({ + //@SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) + @Comment( "The maximum squared distance that a defense entity can be from its chest when " + - "a player is not nearby.", + "a player is not nearby.\n"+ "Set this to 0.0 to disable the limit so that defense entities are not " + "teleported back to their death chest." - }) + ) @ConfigEntry.Gui.Tooltip public double maxSquaredDistanceFromChest = 64.0; - @SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) - @TOMLConfigSerializer.Comment({ + //@SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) + @Comment( "The maximum squared distance that a defense entity can be from its player when" + - "its chest is not within the maximum squared distance.", + "its chest is not within the maximum squared distance.\n"+ "Set this to 0.0 to disable the limit so that defense entities are teleported " + "back to their death chest regardless of their distance from the player." - }) + ) @ConfigEntry.Gui.Tooltip public double maxSquaredDistanceFromPlayer = 64.0; @@ -350,64 +348,65 @@ public void validatePostLoad() { } public static final class Protection { - @TOMLConfigSerializer.Comment({ - "Enables death chest protection.", - "When a death chest is protected, it can only be unlocked by its owner." - }) + @Comment( + "Enables death chest protection.\n"+ + "When a death chest is protected, it can only be unlocked by its owner.\n" + ) @ConfigEntry.Gui.Tooltip public boolean enable = true; - @SpecIntInRange(min = 0, max = Integer.MAX_VALUE) - @TOMLConfigSerializer.Comment( + @ConfigEntry.BoundedDiscrete(min = 0, max = 5) + @Comment( "The required permission level to bypass death chest protection." ) @ConfigEntry.Gui.Tooltip public int bypassPermissionLevel = 3; - @TOMLConfigSerializer.Comment( + @Comment( "Causes players in creative mode to be able to bypass death chest protection." ) @ConfigEntry.Gui.Tooltip public boolean bypassInCreativeMode = true; - @SpecIntInRange(min = 0, max = Integer.MAX_VALUE) - @TOMLConfigSerializer.Comment({ - "The length of death chest protection in ticks.", - "120000 ticks is five in-game days.", + //@IntRange(from = 0, to= Integer.MAX_VALUE) + @ConfigEntry.BoundedDiscrete(min = 0, max = Integer.MAX_VALUE) + @Comment( + "The length of death chest protection in ticks.\n" + + "120000 ticks is five in-game days.\n" + "Set this to 0 to cause death chests to be protected indefinitely." - }) + ) @ConfigEntry.Gui.Tooltip public int period = 120000; } public static final class Misc { - @TOMLConfigSerializer.Comment("Causes death chests to be removed when they are emptied.") + @Comment("Causes death chests to be removed when they are emptied.") @ConfigEntry.Gui.Tooltip public boolean removeEmptyDeathChests = true; - @TOMLConfigSerializer.Comment( + @Comment( "Whether empty death chests should only be removed when they are closed." ) @ConfigEntry.Gui.Tooltip public boolean onlyRemoveClosedEmptyDeathChests; - @TOMLConfigSerializer.Comment("Causes death chests to be dropped when they are broken.") + @Comment("Causes death chests to be dropped when they are broken.") @ConfigEntry.Gui.Tooltip public boolean dropDeathChests; - @TOMLConfigSerializer.Comment({ - "The name of the game rule that controls whether death chests should be spawned.", - "Set this to an empty string to disable the game rule.", + @Comment( + "The name of the game rule that controls whether death chests should be spawned.\n"+ + "Set this to an empty string to disable the game rule.\n"+ "Changes to this option are applied after a game restart." - }) + ) @ConfigEntry.Gui.Tooltip public String gameRuleName = "spawnDeathChests"; - @TOMLConfigSerializer.Comment({ - "The name of the command that reloads this configuration from disk.", - "Set this to an empty string to disable the command.", + @Comment( + "The name of the command that reloads this configuration from disk.\n"+ + "Set this to an empty string to disable the command.\n"+ "Changes to this option are applied when a server is loaded." - }) + ) @ConfigEntry.Gui.Tooltip public String configReloadCommand = "vdcconfigreload"; } @@ -554,27 +553,27 @@ public enum KeyConsumptionBehavior { DAMAGE } - @TOMLConfigSerializer.Comment("Options related to death chest spawning.") + @Comment("Options related to death chest spawning.") @ConfigEntry.Category("spawning") @ConfigEntry.Gui.TransitiveObject public Spawning spawning = new Spawning(); - @TOMLConfigSerializer.Comment("Options related to the death chest key item.") + @Comment("Options related to the death chest key item.") @ConfigEntry.Category("key_item") @ConfigEntry.Gui.TransitiveObject public KeyItem keyItem = new KeyItem(); - @TOMLConfigSerializer.Comment("Options related to death chest defense entities.") + @Comment("Options related to death chest defense entities.") @ConfigEntry.Category("defense_entities") @ConfigEntry.Gui.TransitiveObject public DefenseEntities defenseEntities = new DefenseEntities(); - @TOMLConfigSerializer.Comment("Options related to death chest protection.") + @Comment("Options related to death chest protection.") @ConfigEntry.Category("protection") @ConfigEntry.Gui.TransitiveObject public Protection protection = new Protection(); - @TOMLConfigSerializer.Comment("Miscellaneous options.") + @Comment("Miscellaneous options.") @ConfigEntry.Category("misc") @ConfigEntry.Gui.TransitiveObject public Misc misc = new Misc(); diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java b/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java index 0984ec9..152cc6b 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java @@ -25,7 +25,7 @@ import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; -import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; +import me.shedaniel.autoconfig.AutoConfig; /** * The Mod Menu entry point for VanillaDeathChest. diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java b/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java index f73ba74..9e81375 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java @@ -23,13 +23,15 @@ package com.therandomlabs.vanilladeathchest; -import com.therandomlabs.autoconfigtoml.TOMLConfigSerializer; + import com.therandomlabs.vanilladeathchest.command.VDCCommand; import com.therandomlabs.vanilladeathchest.deathchest.DeathChestAutoRemover; import com.therandomlabs.vanilladeathchest.deathchest.DeathChestInteractions; import com.therandomlabs.vanilladeathchest.deathchest.DeathChestPlacer; import com.therandomlabs.vanilladeathchest.world.DeathChestsState; -import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; +import me.shedaniel.autoconfig.AutoConfig; +import me.shedaniel.autoconfig.serializer.ConfigSerializer; +import me.shedaniel.autoconfig.serializer.Toml4jConfigSerializer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents; @@ -63,7 +65,8 @@ public final class VanillaDeathChest implements ModInitializer { @SuppressWarnings("PMD.NonThreadSafeSingleton") @Nullable - private static TOMLConfigSerializer serializer; + private static Toml4jConfigSerializer serializer; + private static VDCConfig config; static { final String gameRuleName = VanillaDeathChest.config().misc.gameRuleName; @@ -97,11 +100,11 @@ public void onInitialize() { */ @SuppressWarnings("NullAway") public static VDCConfig config() { - if (serializer == null) { + if (config == null) { reloadConfig(); } - return serializer.getConfig(); + return config; } /** @@ -109,12 +112,12 @@ public static VDCConfig config() { */ public static void reloadConfig() { if (serializer == null) { + //AutoConfig.register(VDCConfig.class, Toml4jConfigSerializer::new); AutoConfig.register(VDCConfig.class, (definition, configClass) -> { - serializer = new TOMLConfigSerializer<>(definition, configClass); + serializer = new Toml4jConfigSerializer(definition, configClass); return serializer; }); - } else { - serializer.reloadFromDisk(); } + config = AutoConfig.getConfigHolder(VDCConfig.class).getConfig(); } } From fc309208462edb0d23756585662b72ff287d2c94 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Fri, 18 Jun 2021 11:34:13 +0200 Subject: [PATCH 13/28] removed remapped files --- .../vanilladeathchest/VDCConfig.java | 581 ------------------ .../VDCModMenuEntryPoint.java | 41 -- .../vanilladeathchest/VanillaDeathChest.java | 117 ---- .../vanilladeathchest/command/VDCCommand.java | 177 ------ .../command/package-info.java | 27 - .../deathchest/DeathChest.java | 301 --------- .../deathchest/DeathChestAutoRemover.java | 93 --- .../deathchest/DeathChestInteractions.java | 181 ------ .../deathchest/DeathChestLocationFinder.java | 223 ------- .../deathchest/DeathChestPlacer.java | 426 ------------- .../deathchest/package-info.java | 27 - .../vanilladeathchest/mixin/BlockMixin.java | 81 --- .../mixin/ChestBlockEntityMixin.java | 44 -- .../mixin/ExplosionMixin.java | 58 -- .../mixin/LivingEntityMixin.java | 241 -------- .../LockableContainerBlockEntityMixin.java | 94 --- .../mixin/PlayerEntityMixin.java | 48 -- .../ServerPlayerInteractionManagerMixin.java | 65 -- .../mixin/ShulkerBoxBlockEntityMixin.java | 43 -- .../mixin/ShulkerBoxBlockMixin.java | 89 --- .../mixin/WorldAccessor.java | 37 -- .../vanilladeathchest/package-info.java | 27 - .../util/DeathChestBlockEntity.java | 46 -- .../util/DeathChestDefenseEntity.java | 38 -- .../vanilladeathchest/util/DropsList.java | 40 -- .../vanilladeathchest/util/ViewerCount.java | 36 -- .../vanilladeathchest/util/package-info.java | 27 - .../world/DeathChestsState.java | 225 ------- .../vanilladeathchest/world/package-info.java | 27 - 29 files changed, 3460 deletions(-) delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/VDCConfig.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/command/VDCCommand.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/command/package-info.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestLocationFinder.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/package-info.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ExplosionMixin.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/PlayerEntityMixin.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockEntityMixin.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/package-info.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestBlockEntity.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestDefenseEntity.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/util/DropsList.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/util/ViewerCount.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/util/package-info.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java delete mode 100644 remappedSrc/com/therandomlabs/vanilladeathchest/world/package-info.java diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/VDCConfig.java b/remappedSrc/com/therandomlabs/vanilladeathchest/VDCConfig.java deleted file mode 100644 index 3298550..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/VDCConfig.java +++ /dev/null @@ -1,581 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Random; -import java.util.Set; -import java.util.stream.Collectors; - -import com.electronwill.nightconfig.core.conversion.SpecDoubleInRange; -import com.electronwill.nightconfig.core.conversion.SpecIntInRange; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.therandomlabs.autoconfigtoml.TOMLConfigSerializer; -import me.sargunvohra.mcmods.autoconfig1u.ConfigData; -import me.sargunvohra.mcmods.autoconfig1u.annotation.Config; -import me.sargunvohra.mcmods.autoconfig1u.annotation.ConfigEntry; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; -import net.minecraft.item.Item; -import net.minecraft.item.Items; -import net.minecraft.nbt.StringNbtReader; -import net.minecraft.util.DyeColor; -import net.minecraft.util.Identifier; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.World; -import org.checkerframework.checker.nullness.qual.Nullable; - -@SuppressWarnings("CanBeFinal") -@TOMLConfigSerializer.Comment("VanillaDeathChest configuration.") -@Config(name = VanillaDeathChest.MOD_ID) -public final class VDCConfig implements ConfigData { - public static final class Spawning implements ConfigData { - @TOMLConfigSerializer.Comment({ - "The death chest container type.", - "SINGLE_CHEST: Only single chests.", - "SINGLE_OR_DOUBLE_CHEST: Single or double chests.", - "SINGLE_SHULKER_BOX: Single shulker boxes.", - "SINGLE_OR_DOUBLE_SHULKER_BOX: Single or double shulker boxes." - }) - @ConfigEntry.Gui.Tooltip - public ContainerType containerType = ContainerType.SINGLE_OR_DOUBLE_CHEST; - - @TOMLConfigSerializer.Comment({ - "The color of the shulker box if the container type is a shulker box.", - "WHITE: White.", - "ORANGE: Orange.", - "MAGENTA: Magenta.", - "LIGHT_BLUE: Light blue.", - "YELLOW: Yellow.", - "LIME: Lime.", - "PINK: Pink.", - "GRAY: Gray.", - "LIGHT_GRAY: Light gray.", - "CYAN: Cyan.", - "PURPLE: Purple.", - "BLUE: Blue.", - "BROWN: Brown.", - "GREEN: Green.", - "RED: Red.", - "BLACK: Black.", - "RANDOM: Random color." - }) - @ConfigEntry.Gui.Tooltip - public ShulkerBoxColor shulkerBoxColor = ShulkerBoxColor.WHITE; - - @TOMLConfigSerializer.Comment( - "The dimensions that death chests should or should not spawn in." - ) - @ConfigEntry.Gui.Tooltip - public List dimensions = new ArrayList<>(); - - @TOMLConfigSerializer.Comment({ - "Whether the dimensions list should be a blacklist or a whitelist.", - "BLACKLIST: blacklist.", - "WHITELIST: whitelist." - }) - @ConfigEntry.Gui.Tooltip - public DimensionListBehavior dimensionsBehavior = DimensionListBehavior.BLACKLIST; - - @SpecIntInRange(min = 1, max = Integer.MAX_VALUE) - @TOMLConfigSerializer.Comment( - "The radius around the location of a player's death in which a suitable death " + - "chest placement location should be searched for." - ) - @ConfigEntry.Gui.Tooltip - public int locationSearchRadius = 8; - - @TOMLConfigSerializer.Comment( - "Causes a death chest to be forcibly placed at the location of a player's death " + - "if no suitable locations are found nearby." - ) - @ConfigEntry.Gui.Tooltip - public boolean forcePlacementIfNoSuitableLocation = true; - - @TOMLConfigSerializer.Comment("Requires death chest placement to be on solid blocks.") - @ConfigEntry.Gui.Tooltip - public boolean requirePlacementOnSolidBlocks; - - @TOMLConfigSerializer.Comment( - "A regular expression that matches the registry names of items that can be " + - "placed in death chests." - ) - @ConfigEntry.Gui.Tooltip - public String registryNameRegex = ".+"; - - @TOMLConfigSerializer.Comment({ - "Causes death chests to only be spawned if the necessary container is in the " + - "player's inventory.", - "If this is enabled, the container is consumed if it is found." - }) - @ConfigEntry.Gui.Tooltip - public boolean useContainerInInventory; - - @TOMLConfigSerializer.Comment({ - "The display name of the death chest container.", - "Set this to an empty string to cause a custom display name to not be used." - }) - @ConfigEntry.Gui.Tooltip - public String containerDisplayName = "Death Chest"; - - @TOMLConfigSerializer.Comment({ - "The message sent to a player after a death chest is placed when they die.", - "The X, Y and Z coordinates are provided as arguments.", - "Set this to an empty string to disable this message." - }) - @ConfigEntry.Gui.Tooltip - public String spawnMessage = "Death chest spawned at [%s, %s, %s]"; - - @Nullable - @ConfigEntry.Gui.Excluded - private Set dimensionIdentifiers; - - /** - * {@inheritDoc} - */ - @Override - public void validatePostLoad() { - dimensionIdentifiers = dimensions.stream(). - map(Identifier::new). - collect(Collectors.toSet()); - dimensions = dimensionIdentifiers.stream(). - map(Identifier::toString). - collect(Collectors.toList()); - } - - /** - * Returns whether death chest spawning is enabled in the specified world's dimension. - * - * @param world a {@link World}. - * @return {@code true} if death chest spawning is enabled in the specified dimension, - * or otherwise {@code false}. - */ - @SuppressWarnings({"ConstantConditions", "NullAway"}) - public boolean isDimensionEnabled(World world) { - final Identifier identifier = world.getRegistryManager(). - get(Registry.DIMENSION_TYPE_KEY). - getId(world.getDimension()); - - if (identifier == null) { - VanillaDeathChest.logger.error( - "Failed to determine dimension", new RuntimeException() - ); - return dimensionsBehavior == DimensionListBehavior.BLACKLIST; - } - - final boolean anyMatch = dimensionIdentifiers.stream().anyMatch(identifier::equals); - - if (dimensionsBehavior == DimensionListBehavior.BLACKLIST) { - return !anyMatch; - } - - return anyMatch; - } - } - - public static final class KeyItem implements ConfigData { - @TOMLConfigSerializer.Comment({ - "The registry name of the key item.", - "A player must be holding this item in their main hand to unlock a death chest.", - "Set this to an empty string to allow death chests to be unlocked without an item." - }) - @ConfigEntry.Gui.Tooltip - public String registryName = ""; - - @SpecIntInRange(min = 0, max = Short.MAX_VALUE) - @TOMLConfigSerializer.Comment({ - "The meta value of the key item.", - "Set this to " + Short.MAX_VALUE + " to not require a specific meta value." - }) - @ConfigEntry.Gui.Tooltip - public int meta = Short.MAX_VALUE; - - @TOMLConfigSerializer.Comment({ - "The key consumption behavior.", - "CONSUME: Consume the item.", - "DAMAGE: Damage the item." - }) - @ConfigEntry.Gui.Tooltip - public KeyConsumptionBehavior consumptionBehavior = KeyConsumptionBehavior.CONSUME; - - @SpecIntInRange(min = 0, max = Short.MAX_VALUE) - @TOMLConfigSerializer.Comment({ - "The amount by which the key item should be consumed.", - "If the key item cannot be consumed this many times, the death chest will not " + - "be unlocked.", - "Players in creative mode will not have their key item consumed." - }) - @ConfigEntry.Gui.Tooltip - public int amountToConsume = 1; - - @TOMLConfigSerializer.Comment({ - "The message that is sent to the player when they fail to unlock a death chest.", - "This string takes the required amount (%1$s) and display name (%2$s) of the " + - "item as arguments.", - "Set this to an empty string to disable this message." - }) - @ConfigEntry.Gui.Tooltip - public String unlockFailureMessage = "You need %s of %s to retrieve your items"; - - @TOMLConfigSerializer.Comment( - "Whether the unlock failed message should be sent as a status message rather " + - "than a chat message." - ) - @ConfigEntry.Gui.Tooltip - public boolean unlockFailureStatusMessage = true; - - @Nullable - @ConfigEntry.Gui.Excluded - public Item item; - - /** - * {@inheritDoc} - */ - @Override - public void validatePostLoad() { - if (!registryName.isEmpty()) { - item = Registry.ITEM.get(new Identifier(registryName)); - - if (item == Items.AIR) { - registryName = ""; - item = null; - } else { - registryName = new Identifier(registryName).toString(); - } - } - } - } - - public static final class DefenseEntities implements ConfigData { - @TOMLConfigSerializer.Comment({ - "The registry name of the defense entity.", - "If the defense entity is a living entity, it will not automatically despawn.", - "If the defense entity can have a revenge target, then the revenge target will " + - "be set to its player.", - "Set this to an empty string to disable defense entities." - }) - @ConfigEntry.Gui.Tooltip - public String registryName = ""; - - @TOMLConfigSerializer.Comment("Custom NBT data for defense entities in JSON format.") - @ConfigEntry.Gui.Tooltip - public String nbtTag = "{}"; - - @TOMLConfigSerializer.Comment("Causes defense entities to drop experience.") - @ConfigEntry.Gui.Tooltip - public boolean dropExperience; - - @TOMLConfigSerializer.Comment("Causes defense entities to drop items.") - @ConfigEntry.Gui.Tooltip - public boolean dropItems; - - @SpecIntInRange(min = 1, max = Integer.MAX_VALUE) - @TOMLConfigSerializer.Comment( - "The number of defense entities that are spawned when a death chest is placed." - ) - @ConfigEntry.Gui.Tooltip - public int spawnCount = 3; - - @SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) - @TOMLConfigSerializer.Comment({ - "The maximum squared distance that a defense entity can be from its chest when " + - "a player is not nearby.", - "Set this to 0.0 to disable the limit so that defense entities are not " + - "teleported back to their death chest." - }) - @ConfigEntry.Gui.Tooltip - public double maxSquaredDistanceFromChest = 64.0; - - @SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) - @TOMLConfigSerializer.Comment({ - "The maximum squared distance that a defense entity can be from its player when" + - "its chest is not within the maximum squared distance.", - "Set this to 0.0 to disable the limit so that defense entities are teleported " + - "back to their death chest regardless of their distance from the player." - }) - @ConfigEntry.Gui.Tooltip - public double maxSquaredDistanceFromPlayer = 64.0; - - @Nullable - @ConfigEntry.Gui.Excluded - public EntityType entityType; - - /** - * {@inheritDoc} - */ - @Override - public void validatePostLoad() { - if (!registryName.isEmpty()) { - final Optional> optional = - Registry.ENTITY_TYPE.getOrEmpty(new Identifier(registryName)); - - if (optional.isPresent()) { - registryName = new Identifier(registryName).toString(); - entityType = optional.get(); - } else { - registryName = ""; - entityType = null; - } - } - - try { - StringNbtReader.parse(nbtTag); - } catch (CommandSyntaxException ex) { - nbtTag = "{}"; - } - } - } - - public static final class Protection { - @TOMLConfigSerializer.Comment({ - "Enables death chest protection.", - "When a death chest is protected, it can only be unlocked by its owner." - }) - @ConfigEntry.Gui.Tooltip - public boolean enable = true; - - @SpecIntInRange(min = 0, max = Integer.MAX_VALUE) - @TOMLConfigSerializer.Comment( - "The required permission level to bypass death chest protection." - ) - @ConfigEntry.Gui.Tooltip - public int bypassPermissionLevel = 3; - - @TOMLConfigSerializer.Comment( - "Causes players in creative mode to be able to bypass death chest protection." - ) - @ConfigEntry.Gui.Tooltip - public boolean bypassInCreativeMode = true; - - @SpecIntInRange(min = 0, max = Integer.MAX_VALUE) - @TOMLConfigSerializer.Comment({ - "The length of death chest protection in ticks.", - "120000 ticks is five in-game days.", - "Set this to 0 to cause death chests to be protected indefinitely." - }) - @ConfigEntry.Gui.Tooltip - public int period = 120000; - } - - public static final class Misc { - @TOMLConfigSerializer.Comment("Causes death chests to be removed when they are emptied.") - @ConfigEntry.Gui.Tooltip - public boolean removeEmptyDeathChests = true; - - @TOMLConfigSerializer.Comment( - "Whether empty death chests should only be removed when they are closed." - ) - @ConfigEntry.Gui.Tooltip - public boolean onlyRemoveClosedEmptyDeathChests; - - @TOMLConfigSerializer.Comment("Causes death chests to be dropped when they are broken.") - @ConfigEntry.Gui.Tooltip - public boolean dropDeathChests; - - @TOMLConfigSerializer.Comment({ - "The name of the game rule that controls whether death chests should be spawned.", - "Set this to an empty string to disable the game rule.", - "Changes to this option are applied after a game restart." - }) - @ConfigEntry.Gui.Tooltip - public String gameRuleName = "spawnDeathChests"; - - @TOMLConfigSerializer.Comment({ - "The name of the command that reloads this configuration from disk.", - "Set this to an empty string to disable the command.", - "Changes to this option are applied when a server is loaded." - }) - @ConfigEntry.Gui.Tooltip - public String configReloadCommand = "vdcconfigreload"; - } - - /** - * The death chest container type. - */ - public enum ContainerType { - /** - * Only single chests. - */ - SINGLE_CHEST, - /** - * Single or double chests. - */ - SINGLE_OR_DOUBLE_CHEST, - /** - * Only single shulker boxes. - */ - SINGLE_SHULKER_BOX, - /** - * Single or double shulker boxes. - */ - SINGLE_OR_DOUBLE_SHULKER_BOX - } - - /** - * The shulker box color. - */ - public enum ShulkerBoxColor { - /** - * White. - */ - WHITE, - /** - * Orange. - */ - ORANGE, - /** - * Magenta. - */ - MAGENTA, - /** - * Light blue. - */ - LIGHT_BLUE, - /** - * Yellow. - */ - YELLOW, - /** - * Lime. - */ - LIME, - /** - * Pink. - */ - PINK, - /** - * Gray. - */ - GRAY, - /** - * Light gray. - */ - LIGHT_GRAY, - /** - * Cyan. - */ - CYAN, - /** - * Purple. - */ - PURPLE, - /** - * Blue. - */ - BLUE, - /** - * Brown. - */ - BROWN, - /** - * Green. - */ - GREEN, - /** - * Red. - */ - RED, - /** - * Black. - */ - BLACK, - /** - * Random color. - */ - RANDOM; - - private static final Random random = new Random(); - - @Nullable - private final DyeColor color; - - ShulkerBoxColor() { - color = "RANDOM".equals(name()) ? null : DyeColor.valueOf(name()); - } - - /** - * Returns this shulker box color as a {@link DyeColor}. - * - * @return this shulker box color as a {@link DyeColor}. - */ - public DyeColor get() { - return color == null ? DyeColor.byId(random.nextInt(16)) : color; - } - } - - /** - * Dimension list behaviors. - */ - public enum DimensionListBehavior { - /** - * Blacklist. - */ - BLACKLIST, - /** - * Whitelist. - */ - WHITELIST - } - - /** - * Key item consumption behaviors. - */ - public enum KeyConsumptionBehavior { - /** - * Consume the item. - */ - CONSUME, - /** - * Damage the item. - */ - DAMAGE - } - - @TOMLConfigSerializer.Comment("Options related to death chest spawning.") - @ConfigEntry.Category("spawning") - @ConfigEntry.Gui.TransitiveObject - public Spawning spawning = new Spawning(); - - @TOMLConfigSerializer.Comment("Options related to the death chest key item.") - @ConfigEntry.Category("key_item") - @ConfigEntry.Gui.TransitiveObject - public KeyItem keyItem = new KeyItem(); - - @TOMLConfigSerializer.Comment("Options related to death chest defense entities.") - @ConfigEntry.Category("defense_entities") - @ConfigEntry.Gui.TransitiveObject - public DefenseEntities defenseEntities = new DefenseEntities(); - - @TOMLConfigSerializer.Comment("Options related to death chest protection.") - @ConfigEntry.Category("protection") - @ConfigEntry.Gui.TransitiveObject - public Protection protection = new Protection(); - - @TOMLConfigSerializer.Comment("Miscellaneous options.") - @ConfigEntry.Category("misc") - @ConfigEntry.Gui.TransitiveObject - public Misc misc = new Misc(); -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java b/remappedSrc/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java deleted file mode 100644 index de3d3bd..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest; - -import io.github.prospector.modmenu.api.ConfigScreenFactory; -import io.github.prospector.modmenu.api.ModMenuApi; -import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; - -/** - * The Mod Menu entry point for VanillaDeathChest. - */ -public final class VDCModMenuEntryPoint implements ModMenuApi { - /** - * {@inheritDoc} - */ - @Override - public ConfigScreenFactory getModConfigScreenFactory() { - return parent -> AutoConfig.getConfigScreen(VDCConfig.class, parent).get(); - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java b/remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java deleted file mode 100644 index 49af38a..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest; - -import com.therandomlabs.autoconfigtoml.TOMLConfigSerializer; -import com.therandomlabs.vanilladeathchest.command.VDCCommand; -import com.therandomlabs.vanilladeathchest.deathchest.DeathChestAutoRemover; -import com.therandomlabs.vanilladeathchest.deathchest.DeathChestInteractions; -import com.therandomlabs.vanilladeathchest.deathchest.DeathChestPlacer; -import com.therandomlabs.vanilladeathchest.world.DeathChestsState; -import me.sargunvohra.mcmods.autoconfig1u.AutoConfig; -import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; -import net.fabricmc.fabric.api.event.player.UseBlockCallback; -import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory; -import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry; -import net.minecraft.world.GameRules; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * The main VanillaDeathChest class. - */ -public final class VanillaDeathChest implements ModInitializer { - /** - * The VanillaDeathChest mod ID. - */ - public static final String MOD_ID = "vanilladeathchest"; - - /** - * The VanillaDeathChest logger. This should only be used by VanillaDeathChest. - */ - public static final Logger logger = LogManager.getLogger(MOD_ID); - - /** - * The game rule that controls whether death chests should be spawned. - */ - public static final GameRules.@Nullable Key SPAWN_DEATH_CHESTS; - - @SuppressWarnings("PMD.NonThreadSafeSingleton") - @Nullable - private static TOMLConfigSerializer serializer; - - static { - final String gameRuleName = VanillaDeathChest.config().misc.gameRuleName; - - if (gameRuleName.isEmpty()) { - SPAWN_DEATH_CHESTS = null; - } else { - SPAWN_DEATH_CHESTS = GameRuleRegistry.register( - gameRuleName, GameRules.Category.DROPS, GameRuleFactory.createBooleanRule(true) - ); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onInitialize() { - reloadConfig(); - CommandRegistrationCallback.EVENT.register(VDCCommand::register); - ServerTickEvents.START_WORLD_TICK.register(DeathChestPlacer::placeQueued); - ServerTickEvents.END_WORLD_TICK.register(DeathChestAutoRemover::removeEmpty); - UseBlockCallback.EVENT.register(DeathChestInteractions::interact); - ServerBlockEntityEvents.BLOCK_ENTITY_UNLOAD.register(DeathChestsState::onBlockEntityUnload); - } - - /** - * Returns the VanillaDeathChest configuration. - * - * @return a {@link VDCConfig} object. - */ - @SuppressWarnings("NullAway") - public static VDCConfig config() { - if (serializer == null) { - reloadConfig(); - } - - return serializer.getConfig(); - } - - /** - * Reloads the VanillaDeathChest configuration from disk. - */ - public static void reloadConfig() { - if (serializer == null) { - AutoConfig.register(VDCConfig.class, Toml4jConfigSerializer::new); - } else { - serializer.reloadFromDisk(); - } - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/command/VDCCommand.java b/remappedSrc/com/therandomlabs/vanilladeathchest/command/VDCCommand.java deleted file mode 100644 index 2bab94d..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/command/VDCCommand.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.command; - -import java.util.Collection; -import java.util.Collections; -import java.util.Set; -import java.util.UUID; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import com.mojang.brigadier.suggestion.SuggestionProvider; -import com.mojang.brigadier.tree.LiteralCommandNode; -import com.therandomlabs.vanilladeathchest.VanillaDeathChest; -import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; -import com.therandomlabs.vanilladeathchest.deathchest.DeathChestPlacer; -import com.therandomlabs.vanilladeathchest.world.DeathChestsState; -import net.minecraft.command.CommandSource; -import net.minecraft.command.argument.EntityArgumentType; -import net.minecraft.command.argument.UuidArgumentType; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.server.command.CommandManager; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.LiteralText; -import net.minecraft.util.math.BlockPos; - -/** - * The command that reloads the VanillaDeathChest configuration. - */ -public final class VDCCommand { - private static final SuggestionProvider SUGGESTION_PROVIDER = - (context, builder) -> CommandSource.suggestMatching( - DeathChestsState.get( - context.getSource().getWorld() - ).getDeathChestIdentifierStrings(), builder - ); - - private static final SimpleCommandExceptionType INVALID_IDENTIFIER_EXCEPTION = - new SimpleCommandExceptionType(new LiteralText("Invalid death chest identifier")); - - private VDCCommand() {} - - /** - * Registers the command that reloads the VanillaDeathChest configuration. - * - * @param dispatcher the {@link CommandDispatcher}. - * @param dedicated whether the server is dedicated. - */ - public static void register( - CommandDispatcher dispatcher, boolean dedicated - ) { - final LiteralCommandNode commandNode = dispatcher.register( - CommandManager.literal("vanilladeathchest"). - then(CommandManager.literal("reloadconfig"). - requires(source -> source.hasPermissionLevel(4)). - executes( - context -> executeReloadConfig(context.getSource()) - ) - ). - then(CommandManager.literal("restoreinventory"). - requires(source -> source.hasPermissionLevel(2)). - then( - CommandManager.argument( - "identifier", UuidArgumentType.uuid() - ).suggests(SUGGESTION_PROVIDER).executes( - context -> executeRestoreInventory( - context.getSource(), getDeathChest(context) - ) - ).then( - CommandManager.argument( - "targets", EntityArgumentType.players() - ).executes( - context -> executeRestoreInventory( - context.getSource(), - getDeathChest(context), - EntityArgumentType.getPlayers( - context, "targets" - ) - ) - ) - ) - ) - ). - then(CommandManager.literal("place"). - requires(source -> source.hasPermissionLevel(2)). - then( - CommandManager.argument( - "identifier", UuidArgumentType.uuid() - ).suggests(SUGGESTION_PROVIDER).executes( - context -> executePlace( - context.getSource(), getDeathChest(context) - ) - ) - ) - ) - ); - dispatcher.register(CommandManager.literal("vdc").redirect(commandNode)); - } - - private static int executeReloadConfig(ServerCommandSource source) { - VanillaDeathChest.reloadConfig(); - source.sendFeedback(new LiteralText("VanillaDeathChest configuration reloaded!"), true); - return Command.SINGLE_SUCCESS; - } - - private static int executeRestoreInventory(ServerCommandSource source, DeathChest deathChest) - throws CommandSyntaxException { - return executeRestoreInventory( - source, deathChest, Collections.singleton(source.getPlayer()) - ); - } - - private static int executeRestoreInventory( - ServerCommandSource source, DeathChest deathChest, - Collection players - ) throws CommandSyntaxException { - for (ServerPlayerEntity player : players) { - final PlayerInventory inventory = player.inventory; - - for (int i = 0; i < inventory.size(); i++) { - inventory.setStack(i, deathChest.getInventory().getStack(i).copy()); - } - } - - source.sendFeedback(new LiteralText("Inventory restored!"), true); - return Command.SINGLE_SUCCESS; - } - - private static int executePlace(ServerCommandSource source, DeathChest deathChest) { - DeathChestPlacer.placeAndFillContainer(deathChest); - DeathChestsState.get(source.getWorld()).addDeathChest(deathChest); - final BlockPos pos = deathChest.getPos(); - source.sendFeedback(new LiteralText(String.format( - "Death chest placed at [%s, %s, %s]", pos.getX(), pos.getY(), pos.getZ() - )), true); - return Command.SINGLE_SUCCESS; - } - - @SuppressWarnings("NullAway") - private static DeathChest getDeathChest(CommandContext context) - throws CommandSyntaxException { - final DeathChestsState state = DeathChestsState.get(context.getSource().getWorld()); - final Set identifiers = state.getDeathChestIdentifiers(); - final UUID identifier = context.getArgument("identifier", UUID.class); - - if (identifiers.contains(identifier)) { - return state.getDeathChest(identifier); - } - - throw INVALID_IDENTIFIER_EXCEPTION.create(); - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/command/package-info.java b/remappedSrc/com/therandomlabs/vanilladeathchest/command/package-info.java deleted file mode 100644 index 821f407..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/command/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - * Command-related classes for VanillaDeathChest. - */ -package com.therandomlabs.vanilladeathchest.command; diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java deleted file mode 100644 index a680b5a..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java +++ /dev/null @@ -1,301 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.deathchest; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import com.therandomlabs.vanilladeathchest.VDCConfig; -import com.therandomlabs.vanilladeathchest.VanillaDeathChest; -import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; -import com.therandomlabs.vanilladeathchest.world.DeathChestsState; -import net.fabricmc.fabric.api.util.NbtType; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.entity.EntityType; -import net.minecraft.entity.ItemEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtElement; -import net.minecraft.nbt.NbtHelper; -import net.minecraft.nbt.NbtList; -import net.minecraft.server.OperatorEntry; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; - -/** - * Represents a death chest. - */ -public final class DeathChest { - private final UUID identifier; - private final ServerWorld world; - private final UUID playerUUID; - @SuppressWarnings("PMD.LooseCoupling") - private final ArrayList items; - private final PlayerInventory inventory; - private final long creationTime; - private final BlockPos pos; - private final boolean isDoubleChest; - private boolean locked; - - /** - * Constructs a {@link DeathChest} with the specified properties. - * - * @param identifier a {@link UUID}. - * @param world a {@link ServerWorld}. - * @param playerUUID a player UUID. - * @param items the items. - * @param inventory the player inventory at the time of death. - * @param creationTime a creation time. - * @param pos a position. If this is a double chest, this is the position of the west block. - * @param isDoubleChest whether the chest is a double chest. - * @param locked whether the chest is locked. - */ - public DeathChest( - UUID identifier, ServerWorld world, UUID playerUUID, List items, - PlayerInventory inventory, long creationTime, BlockPos pos, boolean isDoubleChest, - boolean locked - ) { - this.identifier = identifier; - this.world = world; - this.playerUUID = playerUUID; - this.items = new ArrayList<>(items); - this.inventory = inventory; - this.creationTime = creationTime; - this.pos = pos; - this.isDoubleChest = isDoubleChest; - this.locked = locked; - } - - /** - * Returns this death chest's identifier. - * - * @return a {@link UUID}. - */ - public UUID getIdentifier() { - return identifier; - } - - /** - * Returns this death chest's world. - * - * @return this death chest's world. - */ - public ServerWorld getWorld() { - return world; - } - - /** - * Returns this death chest's player UUID. - * - * @return this death chest's player UUID. - */ - public UUID getPlayerUUID() { - return playerUUID; - } - - /** - * Returns a mutable list containing this death chest's items. - * - * @return a mutable list containing this death chest's items. - */ - public List getItems() { - return items; - } - - /** - * Returns a cloned mutable list containing this death chest's items. - * - * @return a cloned mutable list containing this death chest's items. - */ - @SuppressWarnings("unchecked") - public List cloneItems() { - return (List) items.clone(); - } - - /** - * Returns the player inventory at the time of death. - * - * @return the {@link PlayerInventory} at the time of death. - */ - public PlayerInventory getInventory() { - return inventory; - } - - /** - * Returns this death chest's creation time. - * - * @return this death chest's creation time. - */ - public long getCreationTime() { - return creationTime; - } - - /** - * Returns this death chest's position. - * If this is a double chest, this is the position of the west block. - * - * @return this death chest's position. - */ - public BlockPos getPos() { - return pos; - } - - /** - * Returns whether this death chest is a double chest. - * This may be {@code true} even if one half of the double chest has been destroyed. - * - * @return {@code true} if this death chest is a double chest, or otherwise {@code false}. - */ - public boolean isDoubleChest() { - return isDoubleChest; - } - - /** - * Returns whether this death chest is locked. - * - * @return {@code true} if this death chest is locked, or otherwise {@code false}. - */ - public boolean isLocked() { - return locked; - } - - /** - * Sets whether this death chest is locked. - * - * @param flag {@code true} if this death chest should be locked, - * or {@code false} if this death chest should be unlocked. - */ - public void setLocked(boolean flag) { - locked = flag; - DeathChestsState.get(world).markDirty(); - } - - /** - * Returns whether this death chest exists in the world. - * - * @return {@code true} if this death chest exists, or otherwise {@code false}. - */ - public boolean exists() { - //We don't bother checking the east block, as it is impossible to only break one - //block in a double death chest. - if (!world.getBlockState(pos).getBlock().hasBlockEntity()) { - return false; - } - - final BlockEntity blockEntity = world.getBlockEntity(pos); - return blockEntity instanceof DeathChestBlockEntity && - equals(((DeathChestBlockEntity) blockEntity).getDeathChest()); - } - - /** - * Returns whether death chest protection prevents the specified player from opening this - * death chest. - * - * @param player a player. - * @return {@code true} if this death chest is protected from the specified player, - * or otherwise {@code false}. - */ - @SuppressWarnings("ConstantConditions") - public boolean isProtectedFrom(PlayerEntity player) { - final VDCConfig.Protection config = VanillaDeathChest.config().protection; - - if (!config.enable || playerUUID.equals(player.getUuid()) || - (config.bypassInCreativeMode && player.abilities.creativeMode)) { - return false; - } - - final OperatorEntry entry = - player.getServer().getPlayerManager().getOpList().get(player.getGameProfile()); - - if (entry != null && entry.getPermissionLevel() >= config.bypassPermissionLevel) { - return false; - } - - return config.period == 0 || - player.getEntityWorld().getTime() - creationTime <= config.period; - } - - /** - * Serializes this death chest to a {@link NbtCompound}. - * - * @param tag a {@link NbtCompound}. - * @return the {@link NbtCompound}. - */ - public NbtCompound toTag(NbtCompound tag) { - tag.put("Identifier", NbtHelper.fromUuid(identifier)); - tag.put("PlayerUUID", NbtHelper.fromUuid(playerUUID)); - - final NbtList itemsList = new NbtList(); - - for (ItemEntity item : items) { - final NbtCompound itemTag = item.writeNbt(new NbtCompound()); - item.writeCustomDataToNbt(itemTag); - itemsList.add(itemTag); - } - - tag.put("Items", itemsList); - - final NbtList inventoryList = new NbtList(); - inventory.writeNbt(inventoryList); - tag.put("Inventory", inventoryList); - - tag.putLong("CreationTime", creationTime); - tag.put("Pos", NbtHelper.fromBlockPos(pos)); - tag.putBoolean("IsDoubleChest", isDoubleChest); - tag.putBoolean("Locked", locked); - return tag; - } - - /** - * Deserializes a death chest from a {@link NbtCompound}. - * - * @param world a {@link ServerWorld}. - * @param tag a {@link NbtCompound}. - * @return the deserialized {@link DeathChest}. - */ - @SuppressWarnings("ConstantConditions") - public static DeathChest fromTag(ServerWorld world, NbtCompound tag) { - final List items = new ArrayList<>(); - - for (NbtElement itemTag : tag.getList("Items", NbtType.COMPOUND)) { - final ItemEntity item = new ItemEntity(EntityType.ITEM, world); - item.readNbt((NbtCompound) itemTag); - item.readCustomDataFromNbt((NbtCompound) itemTag); - items.add(item); - } - - //We can pass in a null player here because deserialize doesn't use the player. - final PlayerInventory inventory = new PlayerInventory(null); - inventory.readNbt(tag.getList("Inventory", NbtType.COMPOUND)); - - return new DeathChest( - NbtHelper.toUuid(tag.get("Identifier")), world, - NbtHelper.toUuid(tag.get("PlayerUUID")), items, inventory, - tag.getLong("CreationTime"), NbtHelper.toBlockPos(tag.getCompound("Pos")), - tag.getBoolean("IsDoubleChest"), tag.getBoolean("Locked") - ); - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java deleted file mode 100644 index c1eba18..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.deathchest; - -import com.therandomlabs.vanilladeathchest.VanillaDeathChest; -import com.therandomlabs.vanilladeathchest.util.ViewerCount; -import com.therandomlabs.vanilladeathchest.world.DeathChestsState; -import net.minecraft.block.Blocks; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.block.entity.LockableContainerBlockEntity; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; - -/** - * Handles the automatic removal of empty death chests. - */ -public final class DeathChestAutoRemover { - private DeathChestAutoRemover() {} - - /** - * Removes all empty death chests in loaded chunks. - * This is called at the end of every world tick. - * - * @param world a {@link ServerWorld}. - */ - public static void removeEmpty(ServerWorld world) { - if (!VanillaDeathChest.config().misc.removeEmptyDeathChests) { - return; - } - - DeathChestsState.get(world).getExistingDeathChests(). - forEach(DeathChestAutoRemover::removeIfEmpty); - } - - private static void removeIfEmpty(DeathChest deathChest) { - final ServerWorld world = deathChest.getWorld(); - final BlockPos pos = deathChest.getPos(); - - //Don't unnecessarily load any chunks. - if (!world.getChunkManager().isChunkLoaded(pos.getX() >> 4, pos.getZ() >> 4)) { - return; - } - - final BlockEntity blockEntity = world.getBlockEntity(pos); - - if (!(blockEntity instanceof LockableContainerBlockEntity) || - !((LockableContainerBlockEntity) blockEntity).isEmpty()) { - return; - } - - final boolean isDoubleChest = deathChest.isDoubleChest(); - - if (isDoubleChest) { - final BlockEntity eastBlockEntity = world.getBlockEntity(pos.east()); - - if (!(eastBlockEntity instanceof LockableContainerBlockEntity) || - !((LockableContainerBlockEntity) eastBlockEntity).isEmpty()) { - return; - } - } - - if (!VanillaDeathChest.config().misc.onlyRemoveClosedEmptyDeathChests || - !(blockEntity instanceof ViewerCount) || - ((ViewerCount) blockEntity).getViewerCount() == 0) { - world.setBlockState(pos, Blocks.AIR.getDefaultState()); - - if (isDoubleChest) { - world.setBlockState(pos.east(), Blocks.AIR.getDefaultState()); - } - } - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java deleted file mode 100644 index c3dcdb5..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.deathchest; - -import com.therandomlabs.vanilladeathchest.VDCConfig; -import com.therandomlabs.vanilladeathchest.VanillaDeathChest; -import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.entity.EquipmentSlot; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.LiteralText; -import net.minecraft.text.Text; -import net.minecraft.text.TranslatableText; -import net.minecraft.util.ActionResult; -import net.minecraft.util.Hand; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * Handles death chest interactions. - */ -public final class DeathChestInteractions { - @Nullable - private static DeathChest ignoreDeathChest; - - private DeathChestInteractions() {} - - /** - * Called when a block is right-clicked. - * - * @param player the player. - * @param world the world. - * @param hand the hand. - * @param blockHitResult the {@link BlockHitResult}. - * @return {@link ActionResult#PASS} if the interaction can occur, - * or {@link ActionResult#SUCCESS} if it cannot. - */ - public static ActionResult interact( - PlayerEntity player, World world, Hand hand, BlockHitResult blockHitResult - ) { - if (!(world instanceof ServerWorld)) { - return ActionResult.PASS; - } - - if (!world.getBlockState(blockHitResult.getBlockPos()).getBlock().hasBlockEntity()) { - return ActionResult.PASS; - } - - final BlockEntity blockEntity = world.getBlockEntity(blockHitResult.getBlockPos()); - - if (blockEntity instanceof DeathChestBlockEntity) { - final DeathChest deathChest = ((DeathChestBlockEntity) blockEntity).getDeathChest(); - return deathChest == null || attemptInteract(deathChest, (ServerPlayerEntity) player) ? - ActionResult.PASS : ActionResult.SUCCESS; - } - - return ActionResult.PASS; - } - - /** - * If the specified death chest is locked, an unlock attempt is performed. - * The method then returns whether the specified player can unlock the death chest. - * - * @param deathChest a death chest. - * @param player a player. - * @return {@code true} if the player can interact with the death chest, - * or otherwise {@code false}. - */ - @SuppressWarnings("PMD.CompareObjectsWithEquals") - public static boolean attemptInteract(DeathChest deathChest, ServerPlayerEntity player) { - if (deathChest.isProtectedFrom(player)) { - return false; - } - - final VDCConfig.KeyItem config = VanillaDeathChest.config().keyItem; - - if (config.item == null) { - deathChest.setLocked(false); - return true; - } - - if (!deathChest.isLocked()) { - return true; - } - - final ItemStack stack = player.getStackInHand(player.getActiveHand()); - final int amount = config.amountToConsume; - - if (stack.getItem() == config.item) { - if (amount == 0 || player.abilities.creativeMode) { - deathChest.setLocked(false); - return true; - } - - if (config.consumptionBehavior == VDCConfig.KeyConsumptionBehavior.DAMAGE) { - if (stack.isDamageable() && stack.getDamage() + amount <= stack.getMaxDamage()) { - stack.damage( - amount, player, - breaker -> breaker.sendEquipmentBreakStatus(EquipmentSlot.MAINHAND) - ); - deathChest.setLocked(false); - return true; - } - } else if (stack.getCount() >= amount) { - stack.decrement(amount); - deathChest.setLocked(false); - return true; - } - } - - final String message = config.unlockFailureMessage; - - if (!message.isEmpty()) { - final Text component = new LiteralText(String.format( - message, - amount, new TranslatableText(config.item.getTranslationKey()).getString() - )); - - player.sendMessage(component, config.unlockFailureStatusMessage); - } - - return false; - } - - /** - * Called when a player attempts to break a death chest. - * Returns whether the death chest should be broken. - * If this is {@code true} and the death chest is a double chest, the other death chest - * block is automatically destroyed. - * - * @param pos a position. - * @param deathChest a death chest. - * @param player a player. - * @return {@code true} if the death chest should be broken, or otherwise {@code false}. - */ - public static boolean attemptBreak( - BlockPos pos, DeathChest deathChest, ServerPlayerEntity player - ) { - if (deathChest.equals(ignoreDeathChest)) { - return true; - } - - if (!attemptInteract(deathChest, player)) { - return false; - } - - if (deathChest.isDoubleChest()) { - ignoreDeathChest = deathChest; - final BlockPos west = deathChest.getPos(); - player.interactionManager.tryBreakBlock(pos.equals(west) ? west.east() : west); - } - - return true; - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestLocationFinder.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestLocationFinder.java deleted file mode 100644 index 2d32569..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestLocationFinder.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.deathchest; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.therandomlabs.vanilladeathchest.VDCConfig; -import com.therandomlabs.vanilladeathchest.VanillaDeathChest; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemPlacementContext; -import net.minecraft.item.ItemUsageContext; -import net.minecraft.util.Hand; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.World; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * Finds suitable locations for the placement of death chests. - */ -public final class DeathChestLocationFinder { - /** - * A death chest location. - */ - public static final class Location { - private final BlockPos pos; - private final boolean isDoubleChest; - - private Location(BlockPos pos, boolean isDoubleChest) { - this.pos = pos; - this.isDoubleChest = isDoubleChest; - } - - /** - * Returns the {@link BlockPos} of this location. - * If this is a double chest, this is the position of the west block. - * - * @return the {@link BlockPos} of this location. - */ - public BlockPos getPos() { - return pos; - } - - /** - * Returns whether a double chest should be placed at this location. - * - * @return {@code true} if a double chest should be placed at this location, - * or otherwise {@code false}. - */ - public boolean isDoubleChest() { - return isDoubleChest; - } - } - - private static final class SearchOrder implements Iterable { - private final int size; - private List translations; - - private SearchOrder(int size) { - this.size = size; - - translations = new ArrayList<>(); - - for (int x = 0; x <= size; x++) { - addTranslations(x); - addTranslations(-x); - } - - translations = ImmutableList.copyOf(translations); - } - - @NonNull - @Override - public Iterator iterator() { - return translations.iterator(); - } - - private void addTranslations(int x) { - for (int y = 0; y <= size; y++) { - addTranslations(x, y); - addTranslations(x, -y); - } - } - - private void addTranslations(int x, int y) { - for (int z = 0; z <= size; z++) { - translations.add(new BlockPos(x, y, z)); - translations.add(new BlockPos(x, y, -z)); - } - } - } - - @Nullable - private static SearchOrder searchOrder; - - private DeathChestLocationFinder() {} - - /** - * Finds the most suitable location to place a queued death chest. - * - * @param deathChest a queued {@link DeathChest}. - * @param doubleChest whether a double chest is preferred. - * @return a {@link Location} that describes the most suitable location to place the specified - * death chest. - */ - @Nullable - public static Location find(DeathChest deathChest, boolean doubleChest) { - final World world = deathChest.getWorld(); - final PlayerEntity player = world.getPlayerByUuid(deathChest.getPlayerUUID()); - final BlockPos pos = deathChest.getPos(); - - final VDCConfig.Spawning config = VanillaDeathChest.config().spawning; - - final BlockPos searchPos = new BlockPos( - pos.getX(), Math.min(256, Math.max(1, pos.getY())), pos.getZ() - ); - - BlockPos singleChestPos = null; - - for (BlockPos translation : getSearchOrder(config.locationSearchRadius)) { - final BlockPos potentialPos = searchPos.add(translation); - - if (!canPlace(world, player, potentialPos)) { - continue; - } - - if (!doubleChest || canPlace(world, player, potentialPos.east())) { - return new Location(potentialPos, doubleChest); - } - - if (singleChestPos == null) { - singleChestPos = potentialPos; - } - } - - if (singleChestPos != null) { - return new Location(singleChestPos, false); - } - - return config.forcePlacementIfNoSuitableLocation ? new Location(pos, doubleChest) : null; - } - - @SuppressWarnings("PMD.NonThreadSafeSingleton") - private static Iterable getSearchOrder(int size) { - if (searchOrder == null || searchOrder.size != size) { - searchOrder = new SearchOrder(size); - } - - return searchOrder; - } - - private static boolean canPlace( - World world, PlayerEntity player, BlockPos pos, boolean doubleChest - ) { - if (doubleChest) { - return canPlace(world, player, pos) && canPlace(world, player, pos.east()); - } - - return canPlace(world, player, pos); - } - - private static boolean canPlace(World world, PlayerEntity player, BlockPos pos) { - if (!world.canPlayerModifyAt(player, pos) || - (VanillaDeathChest.config().spawning.requirePlacementOnSolidBlocks && - !world.isTopSolid(pos.down(), player))) { - return false; - } - - final ItemPlacementContext context = new ItemPlacementContext(new ItemUsageContext( - player, Hand.MAIN_HAND, - new BlockHitResult(new Vec3d(0.0, 0.0, 0.0), Direction.DOWN, pos, false) - )); - - if (isReplaceable(world, pos, context) && isReplaceable(world, pos.up(), context)) { - return isNotChest(world, pos.north()) && isNotChest(world, pos.east()) && - isNotChest(world, pos.south()) && isNotChest(world, pos.west()); - } - - return false; - } - - private static boolean isReplaceable(World world, BlockPos pos, ItemPlacementContext context) { - if (pos.getY() < 1 || pos.getY() > world.getHeight()) { - return false; - } - - final BlockState state = world.getBlockState(pos); - return state.isAir() || state.canReplace(context); - } - - private static boolean isNotChest(World world, BlockPos pos) { - return world.getBlockState(pos).getBlock() != Blocks.CHEST; - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java deleted file mode 100644 index cb8a6b9..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/DeathChestPlacer.java +++ /dev/null @@ -1,426 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.deathchest; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Queue; -import java.util.Set; -import java.util.regex.Pattern; - -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.therandomlabs.vanilladeathchest.VDCConfig; -import com.therandomlabs.vanilladeathchest.VanillaDeathChest; -import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; -import com.therandomlabs.vanilladeathchest.util.DeathChestDefenseEntity; -import com.therandomlabs.vanilladeathchest.world.DeathChestsState; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.block.ChestBlock; -import net.minecraft.block.ShulkerBoxBlock; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.block.entity.LootableContainerBlockEntity; -import net.minecraft.block.enums.ChestType; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; -import net.minecraft.entity.ItemEntity; -import net.minecraft.entity.SpawnReason; -import net.minecraft.entity.mob.MobEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.inventory.Inventories; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.StringNbtReader; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.LiteralText; -import net.minecraft.util.collection.DefaultedList; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.registry.Registry; -import net.minecraft.world.World; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * Handles death chest placement. - */ -public final class DeathChestPlacer { - private enum ContainerConsumptionResult { - FAILED, - SINGLE, - DOUBLE - } - - private DeathChestPlacer() {} - - /** - * Places all queued death chests that are ready to be placed in the specified world. - * This is called at the end of every world tick. - * - * @param world a {@link ServerWorld}. - */ - @SuppressWarnings("ConstantConditions") - public static void placeQueued(ServerWorld world) { - final DeathChestsState state = DeathChestsState.get(world); - final Queue queue = state.getQueuedDeathChests(); - - //We wait two ticks to prevent conflicts with other mods that place things after death. - if (queue.isEmpty() || world.getTime() - queue.peek().getCreationTime() < 2L) { - return; - } - - while (!queue.isEmpty() && world.getTime() - queue.peek().getCreationTime() >= 2L) { - placeAndDropRemaining(queue.poll()); - } - - state.markDirty(); - } - - /** - * Places and fills the specified death chest. - * - * @param deathChest a death chest. - * @return {@code true} if the placement is successful, or otherwise {@code false}. - */ - @SuppressWarnings("NullAway") - public static boolean placeAndFillContainer(DeathChest deathChest) { - final VDCConfig.Spawning config = VanillaDeathChest.config().spawning; - final BlockPos pos = deathChest.getPos(); - final List items = deathChest.getItems(); - final boolean doubleChest = deathChest.isDoubleChest(); - - final Block block; - - if (config.containerType == VDCConfig.ContainerType.SINGLE_SHULKER_BOX || - config.containerType == VDCConfig.ContainerType.SINGLE_OR_DOUBLE_SHULKER_BOX) { - block = ShulkerBoxBlock.get(config.shulkerBoxColor.get()); - } else { - block = Blocks.CHEST; - } - - final BlockPos east = pos.east(); - final World world = deathChest.getWorld(); - final BlockState state = block.getDefaultState(); - - if (doubleChest) { - if (block == Blocks.CHEST) { - world.setBlockState(pos, state.with(ChestBlock.CHEST_TYPE, ChestType.LEFT)); - world.setBlockState(east, state.with(ChestBlock.CHEST_TYPE, ChestType.RIGHT)); - } else { - world.setBlockState(pos, state); - world.setBlockState(east, state); - } - } else { - world.setBlockState(pos, state); - } - - final BlockEntity blockEntity = world.getBlockEntity(pos); - final BlockEntity eastBlockEntity = doubleChest ? world.getBlockEntity(east) : null; - - if (!(blockEntity instanceof LootableContainerBlockEntity) || - (doubleChest && !(eastBlockEntity instanceof LootableContainerBlockEntity))) { - VanillaDeathChest.logger.warn( - "Failed to place death chest at [{}] due to invalid block entity", pos - ); - return false; - } - - final LootableContainerBlockEntity container = - (LootableContainerBlockEntity) (doubleChest ? eastBlockEntity : blockEntity); - - for (int i = 0; i < items.size() && i < 27; i++) { - container.setStack(i, items.get(i).getStack().copy()); - } - - ((DeathChestBlockEntity) container).markAsDeathChest(); - - if (!config.containerDisplayName.isEmpty()) { - container.setCustomName(new LiteralText(config.containerDisplayName)); - } - - if (doubleChest) { - final LootableContainerBlockEntity westContainer = - (LootableContainerBlockEntity) blockEntity; - - for (int i = 27; i < items.size(); i++) { - westContainer.setStack(i - 27, items.get(i).getStack().copy()); - } - - ((DeathChestBlockEntity) westContainer).markAsDeathChest(); - - if (!config.containerDisplayName.isEmpty()) { - westContainer.setCustomName(new LiteralText(config.containerDisplayName)); - } - } else if (items.size() > 27) { - items.subList(27, items.size()).clear(); - } - - return true; - } - - /** - * Spawns the defense entities for the specified death chest. - * - * @param deathChest a death chest. - */ - public static void spawnDefenseEntities(DeathChest deathChest) { - final VDCConfig.DefenseEntities config = VanillaDeathChest.config().defenseEntities; - - if (config.entityType == null) { - return; - } - - final ServerWorld world = deathChest.getWorld(); - final BlockPos pos = deathChest.getPos(); - final double x = pos.getX() + 0.5; - final double y = pos.getY() + 1.0; - final double z = pos.getZ() + 0.5; - - for (int i = 0; i < config.spawnCount; i++) { - //The following spawn logic has been taken from SummonCommand. - NbtCompound tag; - - try { - tag = StringNbtReader.parse(config.nbtTag); - } catch (CommandSyntaxException ex) { - //This should not happen. - tag = new NbtCompound(); - } - - final boolean emptyTag = tag.isEmpty(); - tag.putString("id", config.registryName); - - final Entity entity = EntityType.loadEntityWithPassengers( - tag, world, spawnedEntity -> { - spawnedEntity.refreshPositionAndAngles( - x, y, z, spawnedEntity.yaw, spawnedEntity.pitch - ); - return spawnedEntity; - } - ); - - if (entity instanceof DeathChestDefenseEntity) { - ((DeathChestDefenseEntity) entity).setDeathChest(deathChest); - - if (emptyTag && entity instanceof MobEntity) { - ((MobEntity) entity).initialize( - world, world.getLocalDifficulty(pos), SpawnReason.EVENT, null, null - ); - } - } - - world.spawnEntityAndPassengers(entity); - } - } - - private static void placeAndDropRemaining(DeathChest deathChest) { - final List allItems = deathChest.cloneItems(); - - final DeathChest newDeathChest = place(allItems, deathChest); - final List items = - newDeathChest == null ? Collections.emptyList() : newDeathChest.getItems(); - - final World world = deathChest.getWorld(); - - for (ItemEntity drop : allItems) { - if (!items.contains(drop)) { - if (drop.removed) { - world.spawnEntity(new ItemEntity( - world, drop.getX(), drop.getY(), drop.getZ(), drop.getStack().copy() - )); - } else { - world.spawnEntity(drop); - } - } - } - } - - @Nullable - private static DeathChest place(List allItems, DeathChest deathChest) { - final VDCConfig.Spawning config = VanillaDeathChest.config().spawning; - - final Pattern pattern = Pattern.compile(config.registryNameRegex); - deathChest.getItems().removeIf( - item -> !pattern.matcher( - Registry.ITEM.getId(item.getStack().getItem()).toString() - ).matches() - ); - - if (deathChest.getItems().isEmpty()) { - return null; - } - - final VDCConfig.ContainerType type = config.containerType; - boolean doubleChest = deathChest.getItems().size() > 27 && - (type == VDCConfig.ContainerType.SINGLE_OR_DOUBLE_CHEST || - type == VDCConfig.ContainerType.SINGLE_OR_DOUBLE_SHULKER_BOX); - - final ServerWorld world = deathChest.getWorld(); - final List allItemsBeforeContainerConsumption = new ArrayList<>(); - - if (config.useContainerInInventory) { - for (ItemEntity item : allItems) { - allItemsBeforeContainerConsumption.add( - new ItemEntity( - world, item.getX(), item.getY(), item.getZ(), item.getStack().copy() - ) - ); - } - - final ContainerConsumptionResult result = - consumeContainerInInventory(allItems, deathChest, doubleChest); - - if (result == ContainerConsumptionResult.FAILED) { - return null; - } - - if (result == ContainerConsumptionResult.SINGLE) { - doubleChest = false; - } - } - - final DeathChestLocationFinder.Location location = - DeathChestLocationFinder.find(deathChest, doubleChest); - - if (location == null) { - VanillaDeathChest.logger.warn( - "No death chest location found for player at [{}]", deathChest.getPos() - ); - return null; - } - - if (!location.isDoubleChest()) { - doubleChest = false; - } - - final BlockPos pos = location.getPos(); - final DeathChest newDeathChest = new DeathChest( - deathChest.getIdentifier(), world, deathChest.getPlayerUUID(), - deathChest.getItems(), deathChest.getInventory(), world.getTime(), pos, doubleChest, - true - ); - - if (!placeAndFillContainer(newDeathChest) || newDeathChest.getItems().isEmpty()) { - //Make sure we also drop any consumed containers. - if (!allItemsBeforeContainerConsumption.isEmpty()) { - allItems.clear(); - allItems.addAll(allItemsBeforeContainerConsumption); - } - - return null; - } - - final PlayerEntity player = world.getPlayerByUuid(deathChest.getPlayerUUID()); - - spawnDefenseEntities(newDeathChest); - - DeathChestsState.get(world).addDeathChest(newDeathChest); - - VanillaDeathChest.logger.info( - "Death chest for {} spawned at [{}, {}, {}] with identifier {}", - player == null ? deathChest.getPlayerUUID() : player.getGameProfile().getName(), - pos.getX(), pos.getY(), pos.getZ(), deathChest.getIdentifier() - ); - - if (player != null) { - player.sendMessage(new LiteralText(String.format( - config.spawnMessage, pos.getX(), pos.getY(), pos.getZ() - )), false); - } - - return newDeathChest; - } - - private static ContainerConsumptionResult consumeContainerInInventory( - List allItems, DeathChest deathChest, boolean doubleChest - ) { - final VDCConfig.ContainerType type = VanillaDeathChest.config().spawning.containerType; - final Set emptyItems = new HashSet<>(); - - int availableContainers = 0; - - for (ItemEntity item : allItems) { - final ItemStack stack = item.getStack(); - - if (type == VDCConfig.ContainerType.SINGLE_CHEST || - type == VDCConfig.ContainerType.SINGLE_OR_DOUBLE_CHEST) { - if (stack.getItem() != Item.BLOCK_ITEMS.get(Blocks.CHEST)) { - continue; - } - } else { - if (!(Block.getBlockFromItem(stack.getItem()) instanceof ShulkerBoxBlock)) { - continue; - } - - final NbtCompound tag = stack.getTag(); - - if (tag != null) { - final DefaultedList inventory = - DefaultedList.ofSize(27, ItemStack.EMPTY); - Inventories.readNbt(tag.getCompound("BlockEntityTag"), inventory); - - //The shulker box must be empty. - if (inventory.stream().anyMatch(itemStack -> !itemStack.isEmpty())) { - continue; - } - } - } - - if (availableContainers == 0 && (!doubleChest || stack.getCount() > 1)) { - availableContainers = doubleChest ? 2 : 1; - stack.decrement(availableContainers); - - if (stack.isEmpty()) { - emptyItems.add(item); - } - - break; - } - - //doubleChest is true, but stack.getCount() is only 1. - availableContainers++; - stack.decrement(1); - - if (stack.isEmpty()) { - emptyItems.add(item); - } - - if (availableContainers == 2) { - break; - } - } - - if (availableContainers == 0) { - return ContainerConsumptionResult.FAILED; - } - - allItems.removeAll(emptyItems); - deathChest.getItems().removeAll(emptyItems); - - return availableContainers == 1 ? - ContainerConsumptionResult.SINGLE : ContainerConsumptionResult.DOUBLE; - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/package-info.java b/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/package-info.java deleted file mode 100644 index c60a02f..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/deathchest/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - * Classes that contain death chest-related logic for VanillaDeathChest. - */ -package com.therandomlabs.vanilladeathchest.deathchest; diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java deleted file mode 100644 index 71b5e13..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.mixin; - -import com.therandomlabs.vanilladeathchest.VanillaDeathChest; -import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.block.ShulkerBoxBlock; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -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; - -@Mixin(Block.class) -public final class BlockMixin { - @Unique - private static BlockPos brokenDeathChest; - - @SuppressWarnings("ConstantConditions") - @Inject(method = "onBreak", at = @At("HEAD")) - private void onBreak( - World world, BlockPos pos, BlockState state, PlayerEntity player, CallbackInfo info - ) { - if ((Object) this instanceof ShulkerBoxBlock || !state.getBlock().hasBlockEntity()) { - return; - } - - final BlockEntity blockEntity = world.getBlockEntity(pos); - - if (blockEntity instanceof DeathChestBlockEntity && - ((DeathChestBlockEntity) blockEntity).getDeathChest() != null) { - brokenDeathChest = pos; - } - } - - @Inject( - method = "dropStack", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/World;" + - "spawnEntity(Lnet/minecraft/entity/Entity;)Z" - ), - cancellable = true - ) - private static void dropStack( - World world, BlockPos pos, ItemStack stack, CallbackInfo callback - ) { - if (pos.equals(brokenDeathChest) && !VanillaDeathChest.config().misc.dropDeathChests) { - brokenDeathChest = null; - callback.cancel(); - } - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java deleted file mode 100644 index 093adad..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.mixin; - -import com.therandomlabs.vanilladeathchest.util.ViewerCount; -import net.minecraft.block.entity.ChestBlockEntity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; - -@Mixin(ChestBlockEntity.class) -public final class ChestBlockEntityMixin implements ViewerCount { - @SuppressWarnings("PMD.AvoidProtectedFieldInFinalClass") - @Shadow - protected int viewerCount; - - /** - * {@inheritDoc} - */ - @Override - public int getViewerCount() { - return viewerCount; - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ExplosionMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ExplosionMixin.java deleted file mode 100644 index 2bbf715..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ExplosionMixin.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.mixin; - -import java.util.List; - -import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; -import com.therandomlabs.vanilladeathchest.world.DeathChestsState; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -import net.minecraft.world.explosion.Explosion; -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; - -@Mixin(Explosion.class) -public abstract class ExplosionMixin { - @Shadow - @Final - private World world; - - @Shadow - public abstract List getAffectedBlocks(); - - @Inject(method = "collectBlocksAndDamageEntities", at = @At("TAIL")) - private void collectBlocksAndDamageEntities(CallbackInfo info) { - final DeathChestsState deathChestsState = DeathChestsState.get((ServerWorld) world); - getAffectedBlocks().removeIf(pos -> { - final DeathChest deathChest = deathChestsState.getExistingDeathChest(pos); - return deathChest != null && deathChest.isLocked(); - }); - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java deleted file mode 100644 index 6eb9e68..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LivingEntityMixin.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.mixin; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -import com.therandomlabs.vanilladeathchest.VDCConfig; -import com.therandomlabs.vanilladeathchest.VanillaDeathChest; -import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; -import com.therandomlabs.vanilladeathchest.util.DeathChestDefenseEntity; -import com.therandomlabs.vanilladeathchest.util.DropsList; -import com.therandomlabs.vanilladeathchest.world.DeathChestsState; -import net.minecraft.entity.Entity; -import net.minecraft.entity.ItemEntity; -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.damage.DamageSource; -import net.minecraft.entity.mob.Angerable; -import net.minecraft.entity.mob.MobEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtHelper; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; -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; - -@SuppressWarnings("ConstantConditions") -@Mixin(value = LivingEntity.class, priority = Integer.MAX_VALUE) -public abstract class LivingEntityMixin implements DropsList, DeathChestDefenseEntity { - @Unique - private final List drops = new ArrayList<>(); - - @Unique - private PlayerInventory inventory; - - @Unique - private DeathChest deathChest; - - @Unique - private UUID deathChestPlayerUUID; - - /** - * {@inheritDoc} - */ - @Override - public List getDrops() { - return drops; - } - - /** - * {@inheritDoc} - */ - @Override - public void setDeathChest(DeathChest deathChest) { - this.deathChest = deathChest; - deathChestPlayerUUID = deathChest.getPlayerUUID(); - } - - @Inject(method = "drop", at = @At("HEAD")) - public void dropHead(CallbackInfo info) { - if ((Object) this instanceof PlayerEntity) { - drops.clear(); - //We can't pass in null here because Campanion mixins into setStack and needs the - //player. - inventory = new PlayerInventory((PlayerEntity) (Object) this); - final PlayerInventory oldInventory = ((PlayerEntity) (Object) this).inventory; - - for (int i = 0; i < oldInventory.size(); i++) { - inventory.setStack(i, oldInventory.getStack(i).copy()); - } - } - } - - @Inject(method = "drop", at = @At("TAIL")) - public void dropTail(CallbackInfo info) { - if (drops.isEmpty()) { - return; - } - - final LivingEntity entity = (LivingEntity) (Object) this; - final ServerWorld world = (ServerWorld) entity.getEntityWorld(); - - if ((VanillaDeathChest.SPAWN_DEATH_CHESTS != null && - !world.getGameRules().getBoolean(VanillaDeathChest.SPAWN_DEATH_CHESTS)) || - !VanillaDeathChest.config().spawning.isDimensionEnabled(world)) { - return; - } - - drops.forEach(Entity::remove); - final DeathChestsState deathChestsState = DeathChestsState.get(world); - final BlockPos pos = entity.getBlockPos(); - final DeathChest deathChest = new DeathChest( - UUID.randomUUID(), world, entity.getUuid(), drops, inventory, world.getTime(), pos, - false, true - ); - deathChestsState.getQueuedDeathChests().add(deathChest); - deathChestsState.markDirty(); - - VanillaDeathChest.logger.info( - "Death chest for {} queued at [{}, {}, {}] with identifier {}", - ((PlayerEntity) (Object) this).getGameProfile().getName(), - pos.getX(), pos.getY(), pos.getZ(), deathChest.getIdentifier() - ); - } - - @Inject(method = "tick", at = @At("HEAD")) - public void tick(CallbackInfo info) { - if (deathChestPlayerUUID == null) { - return; - } - - final LivingEntity entity = (LivingEntity) (Object) this; - final PlayerEntity player = entity.getEntityWorld().getPlayerByUuid(deathChestPlayerUUID); - - if ((Object) this instanceof MobEntity) { - final MobEntity mobEntity = (MobEntity) (Object) this; - mobEntity.setPersistent(); - - if (player != null) { - mobEntity.setAttacker(player); - mobEntity.setTarget(player); - } - } - - if (player != null && this instanceof Angerable) { - final Angerable angerable = (Angerable) this; - angerable.setTarget(player); - angerable.setAngerTime(Integer.MAX_VALUE); - } - - final VDCConfig.DefenseEntities config = VanillaDeathChest.config().defenseEntities; - - if (config.maxSquaredDistanceFromChest == 0.0) { - return; - } - - if (deathChest != null && !deathChest.exists()) { - deathChest = null; - } - - if (deathChest == null) { - return; - } - - final BlockPos pos = deathChest.getPos(); - final double squaredDistanceFromChest = pos.getSquaredDistance(entity.getPos(), true); - - if (squaredDistanceFromChest > config.maxSquaredDistanceFromChest) { - final double squaredDistanceFromPlayer = player == null ? - Double.MAX_VALUE : entity.getPos().squaredDistanceTo(player.getPos()); - - if (config.maxSquaredDistanceFromPlayer == 0.0 || - squaredDistanceFromPlayer > config.maxSquaredDistanceFromPlayer) { - entity.refreshPositionAndAngles( - pos.getX() + 0.5, pos.getY() + 1.0, pos.getZ() + 0.5, - entity.yaw, entity.pitch - ); - } - } - } - - @Inject(method = "writeCustomDataToTag", at = @At("HEAD")) - public void writeCustomDataToTag(NbtCompound tag, CallbackInfo info) { - if (deathChestPlayerUUID != null) { - if (deathChest != null) { - tag.put( - "DeathChestIdentifier", NbtHelper.fromUuid(deathChest.getIdentifier()) - ); - } - - tag.put("DeathChestPlayer", NbtHelper.fromUuid(deathChestPlayerUUID)); - } - } - - @Inject(method = "readCustomDataFromTag", at = @At("HEAD")) - public void readCustomDataFromTag(NbtCompound tag, CallbackInfo info) { - if (tag.contains("DeathChestPlayer")) { - deathChestPlayerUUID = NbtHelper.toUuid(tag.get("DeathChestPlayer")); - - if (tag.contains("DeathChestIdentifier")) { - final DeathChestsState deathChestsState = DeathChestsState.get( - (ServerWorld) ((LivingEntity) (Object) this).getEntityWorld() - ); - deathChest = deathChestsState.getDeathChest( - NbtHelper.toUuid(tag.getCompound("DeathChestIdentifier")) - ); - } - } - } - - @Inject(method = "dropLoot", at = @At("HEAD"), cancellable = true) - public void dropLoot(DamageSource source, boolean recentlyHit, CallbackInfo info) { - if (deathChestPlayerUUID != null && !VanillaDeathChest.config().defenseEntities.dropItems) { - info.cancel(); - } - } - - @Inject(method = "dropEquipment", at = @At("HEAD"), cancellable = true) - public void dropEquipment( - DamageSource source, int lootingModifier, boolean recentlyHit, CallbackInfo info - ) { - if (deathChestPlayerUUID != null && !VanillaDeathChest.config().defenseEntities.dropItems) { - info.cancel(); - } - } - - @Inject(method = "dropXp", at = @At("HEAD"), cancellable = true) - public void dropXp(CallbackInfo info) { - if (deathChestPlayerUUID != null && - !VanillaDeathChest.config().defenseEntities.dropExperience) { - info.cancel(); - } - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java deleted file mode 100644 index 9ee0fb7..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.mixin; - -import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; -import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; -import com.therandomlabs.vanilladeathchest.world.DeathChestsState; -import net.minecraft.block.BlockState; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.block.entity.LockableContainerBlockEntity; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.server.world.ServerWorld; -import org.checkerframework.checker.nullness.qual.Nullable; -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; - -@Mixin(LockableContainerBlockEntity.class) -public final class LockableContainerBlockEntityMixin implements DeathChestBlockEntity { - @Unique - private boolean isDeathChest; - - @Unique - private DeathChest deathChest; - - /** - * {@inheritDoc} - */ - @Nullable - @Override - public DeathChest getDeathChest() { - if (!isDeathChest) { - return null; - } - - if (deathChest != null) { - return deathChest; - } - - final BlockEntity blockEntity = (BlockEntity) (Object) this; - final ServerWorld world = (ServerWorld) blockEntity.getWorld(); - - if (world == null) { - return null; - } - - deathChest = DeathChestsState.get(world).getExistingDeathChest(blockEntity.getPos()); - return deathChest; - } - - /** - * {@inheritDoc} - */ - @Override - public void markAsDeathChest() { - isDeathChest = true; - } - - @Inject(method = "fromTag", at = @At("TAIL")) - private void fromTag(BlockState state, NbtCompound tag, CallbackInfo info) { - isDeathChest = tag.getBoolean("IsDeathChest"); - } - - @Inject(method = "toTag", at = @At("TAIL")) - private void toTag(NbtCompound tag, CallbackInfoReturnable info) { - if (isDeathChest) { - tag.putBoolean("IsDeathChest", true); - } - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/PlayerEntityMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/PlayerEntityMixin.java deleted file mode 100644 index 4e9c935..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/PlayerEntityMixin.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.mixin; - -import com.therandomlabs.vanilladeathchest.util.DropsList; -import net.minecraft.entity.ItemEntity; -import net.minecraft.entity.player.PlayerEntity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(value = PlayerEntity.class, priority = Integer.MAX_VALUE) -public final class PlayerEntityMixin { - //We don't redirect PlayerEntity#dropItem to prevent conflicts with other mods that do the same. - @SuppressWarnings("ConstantConditions") - @Redirect( - method = "dropItem(Lnet/minecraft/item/ItemStack;ZZ)Lnet/minecraft/entity/ItemEntity;", - at = @At( - value = "INVOKE", - target = "net/minecraft/entity/ItemEntity.setPickupDelay(I)V" - ) - ) - public void setPickupDelay(ItemEntity entity, int pickupDelay) { - entity.setPickupDelay(pickupDelay); - ((DropsList) (Object) this).getDrops().add(entity); - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java deleted file mode 100644 index 1dda12d..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ServerPlayerInteractionManagerMixin.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.mixin; - -import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; -import com.therandomlabs.vanilladeathchest.deathchest.DeathChestInteractions; -import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; -import net.minecraft.block.entity.BlockEntity; -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 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; - -@Mixin(ServerPlayerInteractionManager.class) -public final class ServerPlayerInteractionManagerMixin { - @Shadow - public ServerWorld world; - - @Shadow - public ServerPlayerEntity player; - - @Inject(method = "tryBreakBlock", at = @At("HEAD"), cancellable = true) - private void tryBreakBlock(BlockPos pos, CallbackInfoReturnable info) { - if (!world.getBlockState(pos).getBlock().hasBlockEntity()) { - return; - } - - final BlockEntity blockEntity = world.getBlockEntity(pos); - - if (blockEntity instanceof DeathChestBlockEntity) { - final DeathChest deathChest = ((DeathChestBlockEntity) blockEntity).getDeathChest(); - - if (deathChest != null && - !DeathChestInteractions.attemptBreak(pos, deathChest, player)) { - info.setReturnValue(false); - } - } - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockEntityMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockEntityMixin.java deleted file mode 100644 index 1728055..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockEntityMixin.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.mixin; - -import com.therandomlabs.vanilladeathchest.util.ViewerCount; -import net.minecraft.block.entity.ShulkerBoxBlockEntity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; - -@Mixin(ShulkerBoxBlockEntity.class) -public final class ShulkerBoxBlockEntityMixin implements ViewerCount { - @Shadow - private int viewerCount; - - /** - * {@inheritDoc} - */ - @Override - public int getViewerCount() { - return viewerCount; - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java deleted file mode 100644 index 3649185..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/ShulkerBoxBlockMixin.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.mixin; - -import com.therandomlabs.vanilladeathchest.VanillaDeathChest; -import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; -import net.minecraft.block.ShulkerBoxBlock; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.entity.Entity; -import net.minecraft.entity.ItemEntity; -import net.minecraft.inventory.Inventories; -import net.minecraft.item.ItemStack; -import net.minecraft.util.collection.DefaultedList; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(ShulkerBoxBlock.class) -public final class ShulkerBoxBlockMixin { - @SuppressWarnings("ConstantConditions") - @Redirect( - method = "onBreak", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/World;" + - "spawnEntity(Lnet/minecraft/entity/Entity;)Z" - ) - ) - private boolean spawnEntity(World world, Entity entity) { - if (VanillaDeathChest.config().misc.dropDeathChests) { - return world.spawnEntity(entity); - } - - final BlockPos pos = entity.getBlockPos(); - final BlockEntity blockEntity = world.getBlockEntity(pos); - - if (!(blockEntity instanceof DeathChestBlockEntity) || - ((DeathChestBlockEntity) blockEntity).getDeathChest() == null) { - return world.spawnEntity(entity); - } - - final DefaultedList inventory = DefaultedList.ofSize(27, ItemStack.EMPTY); - Inventories.readNbt( - ((ItemEntity) entity).getStack().getTag().getCompound("BlockEntityTag"), inventory - ); - - boolean dropped = false; - - for (ItemStack drop : inventory) { - if (!drop.isEmpty()) { - final ItemEntity item = new ItemEntity( - world, - pos.getX() + world.random.nextFloat() * 0.5F + 0.25, - pos.getY() + world.random.nextFloat() * 0.5F + 0.25, - pos.getZ() + world.random.nextFloat() * 0.5F + 0.25, - drop - ); - item.setToDefaultPickupDelay(); - world.spawnEntity(item); - dropped = true; - } - } - - return dropped; - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java b/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java deleted file mode 100644 index 6208abb..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.mixin; - -import java.util.List; - -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.world.World; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(World.class) -public interface WorldAccessor { - @Accessor - List getUnloadedBlockEntities(); -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/package-info.java b/remappedSrc/com/therandomlabs/vanilladeathchest/package-info.java deleted file mode 100644 index 2508bb0..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - * The main VanillaDeathChest package. - */ -package com.therandomlabs.vanilladeathchest; diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestBlockEntity.java b/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestBlockEntity.java deleted file mode 100644 index a771862..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestBlockEntity.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.util; - -import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * Allows a death chest {@link net.minecraft.block.entity.BlockEntity}'s - * {@link DeathChest} to be accessed. - */ -public interface DeathChestBlockEntity { - /** - * Returns the death chest. - * - * @return the {@link DeathChest}. - */ - @Nullable - DeathChest getDeathChest(); - - /** - * Marks this block entity as a death chest. - */ - void markAsDeathChest(); -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestDefenseEntity.java b/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestDefenseEntity.java deleted file mode 100644 index e434429..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/util/DeathChestDefenseEntity.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.util; - -import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; - -/** - * Allows death chest defense entity information to be set. - */ -public interface DeathChestDefenseEntity { - /** - * Sets the death chest. - * - * @param deathChest a death chest. - */ - void setDeathChest(DeathChest deathChest); -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/util/DropsList.java b/remappedSrc/com/therandomlabs/vanilladeathchest/util/DropsList.java deleted file mode 100644 index 6997fbd..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/util/DropsList.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.util; - -import java.util.List; - -import net.minecraft.entity.ItemEntity; - -/** - * Allows player {@link ItemEntity} drops to be accessed. - */ -public interface DropsList { - /** - * Returns the player drops. - * - * @return the player {@link ItemEntity} drops. - */ - List getDrops(); -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/util/ViewerCount.java b/remappedSrc/com/therandomlabs/vanilladeathchest/util/ViewerCount.java deleted file mode 100644 index 036eaa8..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/util/ViewerCount.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.util; - -/** - * Allows a container's viewer count to be accessed. - */ -public interface ViewerCount { - /** - * Returns the viewer count. - * - * @return the viewer count. - */ - int getViewerCount(); -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/util/package-info.java b/remappedSrc/com/therandomlabs/vanilladeathchest/util/package-info.java deleted file mode 100644 index 861c538..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/util/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - * Utility classes for VanillaDeathChest. - */ -package com.therandomlabs.vanilladeathchest.util; diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java b/remappedSrc/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java deleted file mode 100644 index 3c96411..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -package com.therandomlabs.vanilladeathchest.world; - -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; -import com.therandomlabs.vanilladeathchest.mixin.WorldAccessor; -import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; -import net.fabricmc.fabric.api.util.NbtType; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtHelper; -import net.minecraft.nbt.NbtList; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.PersistentState; -import org.checkerframework.checker.nullness.qual.Nullable; - -/** - * Persistent death chests state. - */ -public final class DeathChestsState extends PersistentState { - private final ServerWorld world; - private final Map deathChests = new HashMap<>(); - private final Map existingDeathChests = new HashMap<>(); - private final Queue queuedDeathChests = - new PriorityQueue<>(Comparator.comparing(DeathChest::getCreationTime)); - - private DeathChestsState(String name, ServerWorld world) { - super(name); - this.world = world; - } - - /** - * {@inheritDoc} - */ - @Override - public void fromNbt(NbtCompound tag) { - deathChests.clear(); - tag.getList("DeathChests", NbtType.COMPOUND).stream(). - map(deathChestTag -> DeathChest.fromTag(world, (NbtCompound) deathChestTag)). - forEach(deathChest -> deathChests.put(deathChest.getIdentifier(), deathChest)); - - existingDeathChests.clear(); - tag.getList("ExistingDeathChests", NbtType.INT_ARRAY).stream(). - map(NbtHelper::toUuid). - map(deathChests::get). - forEach(deathChest -> existingDeathChests.put(deathChest.getPos(), deathChest)); - - queuedDeathChests.clear(); - tag.getList("QueuedDeathChests", NbtType.COMPOUND).stream(). - map(deathChestTag -> DeathChest.fromTag(world, (NbtCompound) deathChestTag)). - forEach(queuedDeathChests::add); - } - - /** - * {@inheritDoc} - */ - @Override - public NbtCompound writeNbt(NbtCompound tag) { - final NbtList deathChestsList = new NbtList(); - deathChests.values().stream(). - map(deathChest -> deathChest.toTag(new NbtCompound())). - forEach(deathChestsList::add); - tag.put("DeathChests", deathChestsList); - - final NbtList existingDeathChestsList = new NbtList(); - existingDeathChests.values().stream(). - map(DeathChest::getIdentifier). - map(NbtHelper::fromUuid). - forEach(existingDeathChestsList::add); - tag.put("ExistingDeathChests", existingDeathChestsList); - - final NbtList queuedDeathChestsList = new NbtList(); - queuedDeathChests.stream(). - map(deathChest -> deathChest.toTag(new NbtCompound())). - forEach(queuedDeathChestsList::add); - tag.put("QueuedDeathChests", queuedDeathChestsList); - - return tag; - } - - /** - * Returns the identifiers of all placed death chests. - * - * @return a {@link Set} of {@link UUID}s. - */ - public Set getDeathChestIdentifiers() { - return deathChests.keySet(); - } - - /** - * Returns the identifier strings of all placed death chests. - * - * @return a {@link Set} of strings. - */ - public Set getDeathChestIdentifierStrings() { - return getDeathChestIdentifiers().stream().map(UUID::toString).collect(Collectors.toSet()); - } - - /** - * Returns all placed death chests. - * - * @return a {@link Collection} of all placed death chests. - */ - public Collection getDeathChests() { - return new HashSet<>(deathChests.values()); - } - - /** - * Returns the death chest with the specified identifier. - * - * @param identifier an identifier. - * @return the {@link DeathChest} with the specified identifier. - */ - @Nullable - public DeathChest getDeathChest(UUID identifier) { - return deathChests.get(identifier); - } - - /** - * Returns all existing death chests. - * - * @return a {@link Collection} of all existing death chests. - */ - public Collection getExistingDeathChests() { - return new HashSet<>(existingDeathChests.values()); - } - - /** - * Returns the existing death chest at the specified position. - * - * @param pos a position. - * @return the existing {@link DeathChest} at the specified {@link BlockPos}, - * or {@code null} if it does not exist. - */ - @Nullable - public DeathChest getExistingDeathChest(BlockPos pos) { - final DeathChest deathChest = existingDeathChests.get(pos); - return deathChest == null ? existingDeathChests.get(pos.west()) : deathChest; - } - - /** - * Adds an existing death chest. - * - * @param deathChest a {@link DeathChest}. - */ - public void addDeathChest(DeathChest deathChest) { - deathChests.put(deathChest.getIdentifier(), deathChest); - existingDeathChests.put(deathChest.getPos(), deathChest); - } - - /** - * Returns a queue of all unplaced death chests. Changes to this queue are kept. - * - * @return a queue of all unplaced death chests. - */ - public Queue getQueuedDeathChests() { - return queuedDeathChests; - } - - /** - * Returns the {@link DeathChestsState} instance for the specified world. - * - * @param world a {@link ServerWorld}. - * @return the {@link DeathChestsState} instance for the specified world. - */ - public static DeathChestsState get(ServerWorld world) { - return world.getPersistentStateManager().getOrCreate( - () -> new DeathChestsState("deathchests", world), "deathchests" - ); - } - - /** - * Called when a block entity is unloaded. - * - * @param blockEntity a {@link BlockEntity}. - * @param world a {@link ServerWorld}. - */ - public static void onBlockEntityUnload(BlockEntity blockEntity, ServerWorld world) { - //Fabric API invokes this event in three different locations, but only two of them are - //when a block entity is removed. The other one is just when a chunk is unloaded. - //We can differentiate between these by checking whether the block entity is in - //World#unloadedBlockEntities. - if (((WorldAccessor) world).getUnloadedBlockEntities().contains(blockEntity)) { - return; - } - - if (blockEntity instanceof DeathChestBlockEntity) { - final DeathChest deathChest = ((DeathChestBlockEntity) blockEntity).getDeathChest(); - get(world).existingDeathChests.values().remove(deathChest); - } - } -} diff --git a/remappedSrc/com/therandomlabs/vanilladeathchest/world/package-info.java b/remappedSrc/com/therandomlabs/vanilladeathchest/world/package-info.java deleted file mode 100644 index 9f14096..0000000 --- a/remappedSrc/com/therandomlabs/vanilladeathchest/world/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2021 TheRandomLabs - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - * World-related classes for VanillaDeathChest. - */ -package com.therandomlabs.vanilladeathchest.world; From 5a19a9c7c1a9a21d842dae8b683a96f53f5b87a5 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Fri, 18 Jun 2021 11:34:40 +0200 Subject: [PATCH 14/28] removed old autoConfig from gradle --- build.gradle | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.gradle b/build.gradle index 583d3da..2de28a0 100644 --- a/build.gradle +++ b/build.gradle @@ -15,8 +15,6 @@ ext { fabricAPIVersion = "0.35.2+1.17" modMenuVersion = "2.0.2" clothConfigVersion = "5.0.34" - autoConfigVersion = "3.3.1" - autoConfigTOMLVersion = "autoconfig-3.x.x-fabric-SNAPSHOT" } version = "2.1.0-fabric" From 4716350f78d7450b1ff99f4585774c34bcb04620 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Fri, 18 Jun 2021 11:35:34 +0200 Subject: [PATCH 15/28] there are multiple `dropStack`calls now calling a private version. However the private one does not have access to `pos`. We need to hook into the public ones --- .../vanilladeathchest/mixin/BlockMixin.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java index a779d2c..ef58b24 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java @@ -29,9 +29,11 @@ import net.minecraft.block.BlockState; import net.minecraft.block.ShulkerBoxBlock; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; @@ -39,6 +41,8 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.function.Supplier; + @Mixin(Block.class) public final class BlockMixin { @Unique @@ -62,11 +66,10 @@ private void onBreak( } @Inject( - method = "dropStack", + method = "dropStack(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/item/ItemStack;)V", at = @At( value = "INVOKE", - target = "Lnet/minecraft/world/World;" + - "spawnEntity(Lnet/minecraft/entity/Entity;)Z" + target = "Lnet/minecraft/block/Block;dropStack(Lnet/minecraft/world/World;Ljava/util/function/Supplier;Lnet/minecraft/item/ItemStack;)V" ), cancellable = true ) @@ -78,4 +81,21 @@ private static void dropStack( callback.cancel(); } } + + @Inject( + method = "dropStack(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/item/ItemStack;)V", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/block/Block;dropStack(Lnet/minecraft/world/World;Ljava/util/function/Supplier;Lnet/minecraft/item/ItemStack;)V" + ), + cancellable = true + ) + private static void dropStack( + World world, BlockPos pos, Direction direction, ItemStack stack, CallbackInfo callback + ) { + if (pos.equals(brokenDeathChest) && !VanillaDeathChest.config().misc.dropDeathChests) { + brokenDeathChest = null; + callback.cancel(); + } + } } From f239d26e58c738a68ab8e6aa6486e2071ceff8f1 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Fri, 18 Jun 2021 11:35:58 +0200 Subject: [PATCH 16/28] fixed read/writeNbt signtures --- .../mixin/LockableContainerBlockEntityMixin.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java index 5a9c41a..b4f55cc 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java @@ -81,14 +81,18 @@ public void markAsDeathChest() { } @Inject(method = "writeNbt", at = @At("TAIL")) - private void writeNbt(BlockState state, NbtCompound tag, CallbackInfo info) { - isDeathChest = tag.getBoolean("IsDeathChest"); + private void writeNbt( + NbtCompound nbt, CallbackInfoReturnable cir + ) { + isDeathChest = nbt.getBoolean("IsDeathChest"); } @Inject(method = "readNbt", at = @At("TAIL")) - private void readNbt(NbtCompound tag, CallbackInfoReturnable info) { + private void readNbt( + NbtCompound nbt, CallbackInfo ci + ) { if (isDeathChest) { - tag.putBoolean("IsDeathChest", true); + nbt.putBoolean("IsDeathChest", true); } } } From 63106704b91c9af8ec6f81215af249c9e24b70c8 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Fri, 18 Jun 2021 11:36:29 +0200 Subject: [PATCH 17/28] ChunkRemoval works differently now. This needs to be fixed later! --- .../vanilladeathchest/mixin/WorldAccessor.java | 4 ++-- .../vanilladeathchest/world/DeathChestsState.java | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java index 6208abb..3937ce4 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java @@ -32,6 +32,6 @@ @Mixin(World.class) public interface WorldAccessor { - @Accessor - List getUnloadedBlockEntities(); + /*@Accessor + List getUnloadedBlockEntities();*/ } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java index fc401c3..89535f1 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java @@ -214,9 +214,12 @@ public static void onBlockEntityUnload(BlockEntity blockEntity, ServerWorld worl //when a block entity is removed. The other one is just when a chunk is unloaded. //We can differentiate between these by checking whether the block entity is in //World#unloadedBlockEntities. - if (((WorldAccessor) world).getUnloadedBlockEntities().contains(blockEntity)) { + + // Chunk removal in ClientWorld#unloadBlockEntities no longer relies + // on World#unloadedBlockEntities. + /*if (((WorldAccessor) world).getUnloadedBlockEntities().contains(blockEntity)) { return; - } + }*/ if (blockEntity instanceof DeathChestBlockEntity) { final DeathChest deathChest = ((DeathChestBlockEntity) blockEntity).getDeathChest(); From dd68107e19e8afe531c830ad7cd2e71b5ca0871e Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Fri, 18 Jun 2021 11:36:52 +0200 Subject: [PATCH 18/28] viewCount is accessible through a static method --- .../deathchest/DeathChestAutoRemover.java | 5 +++-- .../mixin/ChestBlockEntityMixin.java | 12 +----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java index c1eba18..aa8e12c 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java @@ -28,6 +28,7 @@ import com.therandomlabs.vanilladeathchest.world.DeathChestsState; import net.minecraft.block.Blocks; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.ChestBlockEntity; import net.minecraft.block.entity.LockableContainerBlockEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; @@ -81,8 +82,8 @@ private static void removeIfEmpty(DeathChest deathChest) { } if (!VanillaDeathChest.config().misc.onlyRemoveClosedEmptyDeathChests || - !(blockEntity instanceof ViewerCount) || - ((ViewerCount) blockEntity).getViewerCount() == 0) { + !(blockEntity instanceof ChestBlockEntity) || + ChestBlockEntity.getPlayersLookingInChestCount(world, blockEntity.getPos())==0) { world.setBlockState(pos, Blocks.AIR.getDefaultState()); if (isDoubleChest) { diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java index 093adad..410e78d 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java @@ -29,16 +29,6 @@ import org.spongepowered.asm.mixin.Shadow; @Mixin(ChestBlockEntity.class) -public final class ChestBlockEntityMixin implements ViewerCount { - @SuppressWarnings("PMD.AvoidProtectedFieldInFinalClass") - @Shadow - protected int viewerCount; +public final class ChestBlockEntityMixin { - /** - * {@inheritDoc} - */ - @Override - public int getViewerCount() { - return viewerCount; - } } From 557923f6aac08f89bcd569ad16ba0f1dc578164e Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Mon, 21 Jun 2021 17:38:36 +0200 Subject: [PATCH 19/28] Styleguide --- build.gradle | 3 ++- fabric.gradle | 22 ++++++++++--------- .../deathchest/DeathChest.java | 2 +- .../deathchest/DeathChestAutoRemover.java | 3 +-- .../vanilladeathchest/mixin/BlockMixin.java | 18 +++++++++------ .../mixin/ChestBlockEntityMixin.java | 3 --- .../LockableContainerBlockEntityMixin.java | 1 - .../mixin/WorldAccessor.java | 4 ---- .../world/DeathChestsState.java | 11 ++++++++-- 9 files changed, 36 insertions(+), 31 deletions(-) diff --git a/build.gradle b/build.gradle index 2de28a0..9a92d86 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ ext { fabricAPIVersion = "0.35.2+1.17" modMenuVersion = "2.0.2" clothConfigVersion = "5.0.34" + nightConfigVersion = "3.6.3" } version = "2.1.0-fabric" @@ -32,7 +33,7 @@ if (project.hasProperty("curseForgeAPIKey")) { id = "393000" addGameVersion "Fabric" - addGameVersion "Java 16" + addGameVersion "Java 16" addGameVersion "Java 10" addGameVersion "Java 9" addGameVersion "Java 8" diff --git a/fabric.gradle b/fabric.gradle index 05b919b..591f397 100644 --- a/fabric.gradle +++ b/fabric.gradle @@ -24,7 +24,7 @@ if (testMod) { repositories { if (project.hasProperty("clothConfigVersion")) { - maven { url "https://maven.shedaniel.me/" } + maven { url "https://maven.shedaniel.me/" } } if (project.hasProperty("autoConfigVersion")) { @@ -39,11 +39,11 @@ repositories { } } - if (project.hasProperty("modMenuVersion")) { - maven { - url "https://maven.terraformersmc.com/releases/" - } - } + if (project.hasProperty("modMenuVersion")) { + maven { + url "https://maven.terraformersmc.com/releases/" + } + } } dependencies { @@ -61,8 +61,8 @@ dependencies { if (project.hasProperty("clothConfigVersion")) { modApi("me.shedaniel.cloth:cloth-config-fabric:${project.clothConfigVersion}") { - exclude(group: "net.fabricmc.fabric-api") - } + exclude(group: "net.fabricmc.fabric-api") + } if (includeClothConfig) { include "me.shedaniel.cloth:cloth-config-fabric:${project.clothConfigVersion}" @@ -78,8 +78,10 @@ dependencies { include "me.sargunvohra.mcmods:autoconfig1u:${project.autoConfigVersion}" } } - + modImplementation "com.electronwill.night-config:core:${project.nightConfigVersion}" + include "com.electronwill.night-config:core:${project.nightConfigVersion}" if (project.hasProperty("autoConfigTOMLVersion")) { + modImplementation "com.github.TheRandomLabs:AutoConfig-TOML:${project.autoConfigTOMLVersion}" include "com.github.TheRandomLabs:AutoConfig-TOML:${project.autoConfigTOMLVersion}" } @@ -135,4 +137,4 @@ publishing { artifact javadocJar } } -} \ No newline at end of file +} diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java index efd09c4..18b15a7 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java @@ -39,8 +39,8 @@ import net.minecraft.entity.player.PlayerInventory; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; -import net.minecraft.nbt.NbtList; import net.minecraft.nbt.NbtHelper; +import net.minecraft.nbt.NbtList; import net.minecraft.server.OperatorEntry; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java index aa8e12c..db8f31c 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestAutoRemover.java @@ -24,7 +24,6 @@ package com.therandomlabs.vanilladeathchest.deathchest; import com.therandomlabs.vanilladeathchest.VanillaDeathChest; -import com.therandomlabs.vanilladeathchest.util.ViewerCount; import com.therandomlabs.vanilladeathchest.world.DeathChestsState; import net.minecraft.block.Blocks; import net.minecraft.block.entity.BlockEntity; @@ -83,7 +82,7 @@ private static void removeIfEmpty(DeathChest deathChest) { if (!VanillaDeathChest.config().misc.onlyRemoveClosedEmptyDeathChests || !(blockEntity instanceof ChestBlockEntity) || - ChestBlockEntity.getPlayersLookingInChestCount(world, blockEntity.getPos())==0) { + ChestBlockEntity.getPlayersLookingInChestCount(world, blockEntity.getPos()) == 0) { world.setBlockState(pos, Blocks.AIR.getDefaultState()); if (isDoubleChest) { diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java index ef58b24..4f5aaf3 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/BlockMixin.java @@ -29,7 +29,6 @@ import net.minecraft.block.BlockState; import net.minecraft.block.ShulkerBoxBlock; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.entity.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; @@ -41,8 +40,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.function.Supplier; - @Mixin(Block.class) public final class BlockMixin { @Unique @@ -66,10 +63,13 @@ private void onBreak( } @Inject( - method = "dropStack(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/item/ItemStack;)V", + method = "dropStack(Lnet/minecraft/world/World;" + + "Lnet/minecraft/util/math/BlockPos;" + + "Lnet/minecraft/item/ItemStack;)V", at = @At( value = "INVOKE", - target = "Lnet/minecraft/block/Block;dropStack(Lnet/minecraft/world/World;Ljava/util/function/Supplier;Lnet/minecraft/item/ItemStack;)V" + target = "Lnet/minecraft/block/Block;dropStack(Lnet/minecraft/world/World;" + + "Ljava/util/function/Supplier;Lnet/minecraft/item/ItemStack;)V" ), cancellable = true ) @@ -83,10 +83,14 @@ private static void dropStack( } @Inject( - method = "dropStack(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/item/ItemStack;)V", + method = "dropStack(Lnet/minecraft/world/World;" + + "Lnet/minecraft/util/math/BlockPos;" + + "Lnet/minecraft/util/math/Direction;" + + "Lnet/minecraft/item/ItemStack;)V", at = @At( value = "INVOKE", - target = "Lnet/minecraft/block/Block;dropStack(Lnet/minecraft/world/World;Ljava/util/function/Supplier;Lnet/minecraft/item/ItemStack;)V" + target = "Lnet/minecraft/block/Block;dropStack(Lnet/minecraft/world/World;" + + "Ljava/util/function/Supplier;Lnet/minecraft/item/ItemStack;)V" ), cancellable = true ) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java index 410e78d..2805f1f 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java @@ -23,12 +23,9 @@ package com.therandomlabs.vanilladeathchest.mixin; -import com.therandomlabs.vanilladeathchest.util.ViewerCount; import net.minecraft.block.entity.ChestBlockEntity; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; @Mixin(ChestBlockEntity.class) public final class ChestBlockEntityMixin { - } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java index b4f55cc..73a7c95 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java @@ -26,7 +26,6 @@ import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; import com.therandomlabs.vanilladeathchest.world.DeathChestsState; -import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.LockableContainerBlockEntity; import net.minecraft.nbt.NbtCompound; diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java index 3937ce4..8ae9f23 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/WorldAccessor.java @@ -23,12 +23,8 @@ package com.therandomlabs.vanilladeathchest.mixin; -import java.util.List; - -import net.minecraft.block.entity.BlockEntity; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(World.class) public interface WorldAccessor { diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java index 89535f1..51b1cd2 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java @@ -36,14 +36,12 @@ import java.util.stream.Collectors; import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; -import com.therandomlabs.vanilladeathchest.mixin.WorldAccessor; import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; import net.fabricmc.fabric.api.util.NbtType; import net.minecraft.block.entity.BlockEntity; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; import net.minecraft.nbt.NbtList; -import net.minecraft.scoreboard.ScoreboardState; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.world.PersistentState; @@ -64,6 +62,9 @@ private DeathChestsState(String name, ServerWorld world) { this.world = world; } + /** + * + */ public DeathChestsState readNbt(NbtCompound tag) { deathChests.clear(); tag.getList("DeathChests", NbtType.COMPOUND).stream(). @@ -227,6 +228,9 @@ public static void onBlockEntityUnload(BlockEntity blockEntity, ServerWorld worl } } + /** + * + */ public static DeathChestsState createState(ServerWorld world) { //Is name really unused? DeathChestsState deathChestsState = new DeathChestsState("deathchests", world); @@ -234,6 +238,9 @@ public static DeathChestsState createState(ServerWorld world) { return deathChestsState; } + /** + * + */ public static DeathChestsState stateFromNbt(ServerWorld world, NbtCompound nbt) { return createState(world).readNbt(nbt); } From 039cc9a836f84c56d2259cb2f6e684578ca578df Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Wed, 23 Jun 2021 09:21:52 +0200 Subject: [PATCH 20/28] Use Jankson for config serialization (builtin TOML does not work) --- build.gradle | 1 - fabric.gradle | 5 +- .../vanilladeathchest/VDCConfig.java | 69 ++++++++----------- .../vanilladeathchest/VanillaDeathChest.java | 11 ++- 4 files changed, 34 insertions(+), 52 deletions(-) diff --git a/build.gradle b/build.gradle index 9a92d86..cf53c88 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,6 @@ ext { fabricAPIVersion = "0.35.2+1.17" modMenuVersion = "2.0.2" clothConfigVersion = "5.0.34" - nightConfigVersion = "3.6.3" } version = "2.1.0-fabric" diff --git a/fabric.gradle b/fabric.gradle index 591f397..125b5f6 100644 --- a/fabric.gradle +++ b/fabric.gradle @@ -78,9 +78,8 @@ dependencies { include "me.sargunvohra.mcmods:autoconfig1u:${project.autoConfigVersion}" } } - modImplementation "com.electronwill.night-config:core:${project.nightConfigVersion}" - include "com.electronwill.night-config:core:${project.nightConfigVersion}" - if (project.hasProperty("autoConfigTOMLVersion")) { + + if (project.hasProperty("autoConfigTOMLVersion")) { modImplementation "com.github.TheRandomLabs:AutoConfig-TOML:${project.autoConfigTOMLVersion}" include "com.github.TheRandomLabs:AutoConfig-TOML:${project.autoConfigTOMLVersion}" diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java b/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java index ac956a9..0b04a3f 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java @@ -58,7 +58,7 @@ public static final class Spawning implements ConfigData { "SINGLE_SHULKER_BOX: Single shulker boxes.\n" + "SINGLE_OR_DOUBLE_SHULKER_BOX: Single or double shulker boxes." ) - @ConfigEntry.Gui.Tooltip + @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) public ContainerType containerType = ContainerType.SINGLE_OR_DOUBLE_CHEST; @Comment( @@ -81,20 +81,20 @@ public static final class Spawning implements ConfigData { "BLACK: Black.\n" + "RANDOM: Random color." ) - @ConfigEntry.Gui.Tooltip + @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) public ShulkerBoxColor shulkerBoxColor = ShulkerBoxColor.WHITE; @Comment( "The dimensions that death chests should or should not spawn in." ) - @ConfigEntry.Gui.Tooltip + @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) public List dimensions = new ArrayList<>(); @Comment("Whether the dimensions list should be a blacklist or a whitelist.\n"+ "BLACKLIST: blacklist\n"+ "WHITELIST: whitelist." ) - @ConfigEntry.Gui.Tooltip + @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) public DimensionListBehavior dimensionsBehavior = DimensionListBehavior.BLACKLIST; @ConfigEntry.BoundedDiscrete(min = 1, max = 1000) @@ -102,25 +102,21 @@ public static final class Spawning implements ConfigData { "The radius around the location of a player's death in which a suitable death " + "chest placement location should be searched for." ) - @ConfigEntry.Gui.Tooltip public int locationSearchRadius = 8; @Comment( "Causes a death chest to be forcibly placed at the location of a player's death " + "if no suitable locations are found nearby." ) - @ConfigEntry.Gui.Tooltip public boolean forcePlacementIfNoSuitableLocation = true; @Comment("Requires death chest placement to be on solid blocks.") - @ConfigEntry.Gui.Tooltip public boolean requirePlacementOnSolidBlocks; @Comment( "A regular expression that matches the registry names of items that can be " + "placed in death chests." ) - @ConfigEntry.Gui.Tooltip public String registryNameRegex = ".+"; @Comment( @@ -128,14 +124,12 @@ public static final class Spawning implements ConfigData { "player's inventory.\n"+ "If this is enabled, the container is consumed if it is found." ) - @ConfigEntry.Gui.Tooltip public boolean useContainerInInventory; @Comment( "The display name of the death chest container.\n"+ "Set this to an empty string to cause a custom display name to not be used." ) - @ConfigEntry.Gui.Tooltip public String containerDisplayName = "Death Chest"; @Comment( @@ -143,7 +137,6 @@ public static final class Spawning implements ConfigData { "The X, Y and Z coordinates are provided as arguments.\n"+ "Set this to an empty string to disable this message." ) - @ConfigEntry.Gui.Tooltip public String spawnMessage = "Death chest spawned at [%s, %s, %s]"; @Nullable @@ -199,16 +192,14 @@ public static final class KeyItem implements ConfigData { "A player must be holding this item in their main hand to unlock a death chest.\n"+ "Set this to an empty string to allow death chests to be unlocked without an item." ) - @ConfigEntry.Gui.Tooltip public String registryName = ""; //@IntRange(from = 0, to= Integer.MAX_VALUE) - @ConfigEntry.BoundedDiscrete(min = 0, max = Short.MAX_VALUE) + //@ConfigEntry.BoundedDiscrete(min = 0, max = Short.MAX_VALUE) @Comment( "The meta value of the key item.\n"+ "Set this to " + Short.MAX_VALUE + " to not require a specific meta value." ) - @ConfigEntry.Gui.Tooltip public int meta = Short.MAX_VALUE; @Comment( @@ -216,7 +207,7 @@ public static final class KeyItem implements ConfigData { "CONSUME: Consume the item.\n"+ "DAMAGE: Damage the item." ) - @ConfigEntry.Gui.Tooltip + @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) public KeyConsumptionBehavior consumptionBehavior = KeyConsumptionBehavior.CONSUME; @ConfigEntry.BoundedDiscrete(min = 0, max = 100) @@ -226,7 +217,6 @@ public static final class KeyItem implements ConfigData { "be unlocked.\n"+ "Players in creative mode will not have their key item consumed." ) - @ConfigEntry.Gui.Tooltip public int amountToConsume = 1; @Comment( @@ -235,14 +225,12 @@ public static final class KeyItem implements ConfigData { "item as arguments.\n"+ "Set this to an empty string to disable this message." ) - @ConfigEntry.Gui.Tooltip public String unlockFailureMessage = "You need %s of %s to retrieve your items"; @Comment( "Whether the unlock failed message should be sent as a status message rather " + "than a chat message." ) - @ConfigEntry.Gui.Tooltip public boolean unlockFailureStatusMessage = true; @Nullable @@ -275,26 +263,21 @@ public static final class DefenseEntities implements ConfigData { "be set to its player.\n" + "Set this to an empty string to disable defense entities." ) - @ConfigEntry.Gui.Tooltip public String registryName = ""; @Comment("Custom NBT data for defense entities in JSON format.") - @ConfigEntry.Gui.Tooltip public String nbtTag = "{}"; @Comment("Causes defense entities to drop experience.") - @ConfigEntry.Gui.Tooltip public boolean dropExperience; @Comment("Causes defense entities to drop items.") - @ConfigEntry.Gui.Tooltip public boolean dropItems; @ConfigEntry.BoundedDiscrete(min = 1, max = 100) @Comment( "The number of defense entities that are spawned when a death chest is placed." ) - @ConfigEntry.Gui.Tooltip public int spawnCount = 3; //@SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) @@ -304,7 +287,6 @@ public static final class DefenseEntities implements ConfigData { "Set this to 0.0 to disable the limit so that defense entities are not " + "teleported back to their death chest." ) - @ConfigEntry.Gui.Tooltip public double maxSquaredDistanceFromChest = 64.0; //@SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) @@ -314,7 +296,6 @@ public static final class DefenseEntities implements ConfigData { "Set this to 0.0 to disable the limit so that defense entities are teleported " + "back to their death chest regardless of their distance from the player." ) - @ConfigEntry.Gui.Tooltip public double maxSquaredDistanceFromPlayer = 64.0; @Nullable @@ -347,51 +328,44 @@ public void validatePostLoad() { } } - public static final class Protection { + public static final class Protection implements ConfigData{ @Comment( "Enables death chest protection.\n"+ "When a death chest is protected, it can only be unlocked by its owner.\n" ) - @ConfigEntry.Gui.Tooltip public boolean enable = true; @ConfigEntry.BoundedDiscrete(min = 0, max = 5) @Comment( "The required permission level to bypass death chest protection." ) - @ConfigEntry.Gui.Tooltip public int bypassPermissionLevel = 3; @Comment( "Causes players in creative mode to be able to bypass death chest protection." ) - @ConfigEntry.Gui.Tooltip public boolean bypassInCreativeMode = true; //@IntRange(from = 0, to= Integer.MAX_VALUE) - @ConfigEntry.BoundedDiscrete(min = 0, max = Integer.MAX_VALUE) + //@ConfigEntry.BoundedDiscrete(min = 0, max = Integer.MAX_VALUE) @Comment( "The length of death chest protection in ticks.\n" + "120000 ticks is five in-game days.\n" + "Set this to 0 to cause death chests to be protected indefinitely." ) - @ConfigEntry.Gui.Tooltip public int period = 120000; } - public static final class Misc { + public static final class Misc implements ConfigData{ @Comment("Causes death chests to be removed when they are emptied.") - @ConfigEntry.Gui.Tooltip public boolean removeEmptyDeathChests = true; @Comment( "Whether empty death chests should only be removed when they are closed." ) - @ConfigEntry.Gui.Tooltip public boolean onlyRemoveClosedEmptyDeathChests; @Comment("Causes death chests to be dropped when they are broken.") - @ConfigEntry.Gui.Tooltip public boolean dropDeathChests; @Comment( @@ -399,7 +373,6 @@ public static final class Misc { "Set this to an empty string to disable the game rule.\n"+ "Changes to this option are applied after a game restart." ) - @ConfigEntry.Gui.Tooltip public String gameRuleName = "spawnDeathChests"; @Comment( @@ -407,7 +380,6 @@ public static final class Misc { "Set this to an empty string to disable the command.\n"+ "Changes to this option are applied when a server is loaded." ) - @ConfigEntry.Gui.Tooltip public String configReloadCommand = "vdcconfigreload"; } @@ -552,28 +524,41 @@ public enum KeyConsumptionBehavior { */ DAMAGE } + @Override + public void validatePostLoad() { + try { + spawning.validatePostLoad(); + keyItem.validatePostLoad(); + defenseEntities.validatePostLoad(); + protection.validatePostLoad(); + misc.validatePostLoad(); + } catch (ValidationException e) { + throw new RuntimeException(e); + } + } - @Comment("Options related to death chest spawning.") + //Comments seem to hide the comments within the class + //@Comment("Options related to death chest spawning.") @ConfigEntry.Category("spawning") @ConfigEntry.Gui.TransitiveObject public Spawning spawning = new Spawning(); - @Comment("Options related to the death chest key item.") + //@Comment("Options related to the death chest key item.") @ConfigEntry.Category("key_item") @ConfigEntry.Gui.TransitiveObject public KeyItem keyItem = new KeyItem(); - @Comment("Options related to death chest defense entities.") + //@Comment("Options related to death chest defense entities.") @ConfigEntry.Category("defense_entities") @ConfigEntry.Gui.TransitiveObject public DefenseEntities defenseEntities = new DefenseEntities(); - @Comment("Options related to death chest protection.") + //@Comment("Options related to death chest protection.") @ConfigEntry.Category("protection") @ConfigEntry.Gui.TransitiveObject public Protection protection = new Protection(); - @Comment("Miscellaneous options.") + //@Comment("Miscellaneous options.") @ConfigEntry.Category("misc") @ConfigEntry.Gui.TransitiveObject public Misc misc = new Misc(); diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java b/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java index 9e81375..790093f 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java @@ -31,6 +31,7 @@ import com.therandomlabs.vanilladeathchest.world.DeathChestsState; import me.shedaniel.autoconfig.AutoConfig; import me.shedaniel.autoconfig.serializer.ConfigSerializer; +import me.shedaniel.autoconfig.serializer.JanksonConfigSerializer; import me.shedaniel.autoconfig.serializer.Toml4jConfigSerializer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; @@ -65,8 +66,8 @@ public final class VanillaDeathChest implements ModInitializer { @SuppressWarnings("PMD.NonThreadSafeSingleton") @Nullable - private static Toml4jConfigSerializer serializer; private static VDCConfig config; + private static boolean didRegister = false; static { final String gameRuleName = VanillaDeathChest.config().misc.gameRuleName; @@ -111,12 +112,10 @@ public static VDCConfig config() { * Reloads the VanillaDeathChest configuration from disk. */ public static void reloadConfig() { - if (serializer == null) { + if (!didRegister) { + didRegister = true; + AutoConfig.register(VDCConfig.class, JanksonConfigSerializer::new); //AutoConfig.register(VDCConfig.class, Toml4jConfigSerializer::new); - AutoConfig.register(VDCConfig.class, (definition, configClass) -> { - serializer = new Toml4jConfigSerializer(definition, configClass); - return serializer; - }); } config = AutoConfig.getConfigHolder(VDCConfig.class).getConfig(); } From 0286a74a4657010e4fc939400800d35b98df68f9 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Wed, 23 Jun 2021 09:45:51 +0200 Subject: [PATCH 21/28] fixed checkStyle issues --- fabric.gradle | 4 +- .../vanilladeathchest/VDCConfig.java | 123 +++++++++--------- .../VDCModMenuEntryPoint.java | 4 + .../vanilladeathchest/VanillaDeathChest.java | 6 +- 4 files changed, 72 insertions(+), 65 deletions(-) diff --git a/fabric.gradle b/fabric.gradle index 125b5f6..989b67a 100644 --- a/fabric.gradle +++ b/fabric.gradle @@ -78,8 +78,8 @@ dependencies { include "me.sargunvohra.mcmods:autoconfig1u:${project.autoConfigVersion}" } } - - if (project.hasProperty("autoConfigTOMLVersion")) { + + if (project.hasProperty("autoConfigTOMLVersion")) { modImplementation "com.github.TheRandomLabs:AutoConfig-TOML:${project.autoConfigTOMLVersion}" include "com.github.TheRandomLabs:AutoConfig-TOML:${project.autoConfigTOMLVersion}" diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java b/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java index 0b04a3f..13beea2 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/VDCConfig.java @@ -53,33 +53,33 @@ public final class VDCConfig implements ConfigData { public static final class Spawning implements ConfigData { @Comment( "The death chest container type.\n" + - "SINGLE_CHEST: Only single chests.\n" + - "SINGLE_OR_DOUBLE_CHEST: Single or double chests.\n" + - "SINGLE_SHULKER_BOX: Single shulker boxes.\n" + - "SINGLE_OR_DOUBLE_SHULKER_BOX: Single or double shulker boxes." + "SINGLE_CHEST: Only single chests.\n" + + "SINGLE_OR_DOUBLE_CHEST: Single or double chests.\n" + + "SINGLE_SHULKER_BOX: Single shulker boxes.\n" + + "SINGLE_OR_DOUBLE_SHULKER_BOX: Single or double shulker boxes." ) @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) public ContainerType containerType = ContainerType.SINGLE_OR_DOUBLE_CHEST; @Comment( - "The color of the shulker box if the container type is a shulker box.\n"+ - "WHITE: White.\n" + - "ORANGE: Orange.\n" + - "MAGENTA: Magenta.\n" + - "LIGHT_BLUE: Light blue.\n" + - "YELLOW: Yellow.\n" + - "LIME: Lime.\n" + - "PINK: Pink.\n" + - "GRAY: Gray.\n" + - "LIGHT_GRAY: Light gray.\n" + - "CYAN: Cyan.\n" + - "PURPLE: Purple.\n" + - "BLUE: Blue.\n" + - "BROWN: Brown.\n" + - "GREEN: Green.\n" + - "RED: Red.\n" + - "BLACK: Black.\n" + - "RANDOM: Random color." + "The color of the shulker box if the container type is a shulker box.\n" + + "WHITE: White.\n" + + "ORANGE: Orange.\n" + + "MAGENTA: Magenta.\n" + + "LIGHT_BLUE: Light blue.\n" + + "YELLOW: Yellow.\n" + + "LIME: Lime.\n" + + "PINK: Pink.\n" + + "GRAY: Gray.\n" + + "LIGHT_GRAY: Light gray.\n" + + "CYAN: Cyan.\n" + + "PURPLE: Purple.\n" + + "BLUE: Blue.\n" + + "BROWN: Brown.\n" + + "GREEN: Green.\n" + + "RED: Red.\n" + + "BLACK: Black.\n" + + "RANDOM: Random color." ) @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) public ShulkerBoxColor shulkerBoxColor = ShulkerBoxColor.WHITE; @@ -90,8 +90,8 @@ public static final class Spawning implements ConfigData { @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) public List dimensions = new ArrayList<>(); - @Comment("Whether the dimensions list should be a blacklist or a whitelist.\n"+ - "BLACKLIST: blacklist\n"+ + @Comment("Whether the dimensions list should be a blacklist or a whitelist.\n" + + "BLACKLIST: blacklist\n" + "WHITELIST: whitelist." ) @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) @@ -121,21 +121,21 @@ public static final class Spawning implements ConfigData { @Comment( "Causes death chests to only be spawned if the necessary container is in the " + - "player's inventory.\n"+ - "If this is enabled, the container is consumed if it is found." + "player's inventory.\n" + + "If this is enabled, the container is consumed if it is found." ) public boolean useContainerInInventory; @Comment( - "The display name of the death chest container.\n"+ - "Set this to an empty string to cause a custom display name to not be used." + "The display name of the death chest container.\n" + + "Set this to an empty string to cause a custom display name to not be used." ) public String containerDisplayName = "Death Chest"; @Comment( - "The message sent to a player after a death chest is placed when they die.\n"+ - "The X, Y and Z coordinates are provided as arguments.\n"+ - "Set this to an empty string to disable this message." + "The message sent to a player after a death chest is placed when they die.\n" + + "The X, Y and Z coordinates are provided as arguments.\n" + + "Set this to an empty string to disable this message." ) public String spawnMessage = "Death chest spawned at [%s, %s, %s]"; @@ -188,8 +188,8 @@ public boolean isDimensionEnabled(World world) { public static final class KeyItem implements ConfigData { @Comment( - "The registry name of the key item.\n"+ - "A player must be holding this item in their main hand to unlock a death chest.\n"+ + "The registry name of the key item.\n" + + "A player must be holding this item in their main hand to unlock a death chest.\n" + "Set this to an empty string to allow death chests to be unlocked without an item." ) public String registryName = ""; @@ -197,32 +197,32 @@ public static final class KeyItem implements ConfigData { //@IntRange(from = 0, to= Integer.MAX_VALUE) //@ConfigEntry.BoundedDiscrete(min = 0, max = Short.MAX_VALUE) @Comment( - "The meta value of the key item.\n"+ - "Set this to " + Short.MAX_VALUE + " to not require a specific meta value." + "The meta value of the key item.\n" + + "Set this to " + Short.MAX_VALUE + " to not require a specific meta value." ) public int meta = Short.MAX_VALUE; @Comment( - "The key consumption behavior.\n"+ - "CONSUME: Consume the item.\n"+ - "DAMAGE: Damage the item." + "The key consumption behavior.\n" + + "CONSUME: Consume the item.\n" + + "DAMAGE: Damage the item." ) @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON) public KeyConsumptionBehavior consumptionBehavior = KeyConsumptionBehavior.CONSUME; @ConfigEntry.BoundedDiscrete(min = 0, max = 100) @Comment( - "The amount by which the key item should be consumed.\n"+ + "The amount by which the key item should be consumed.\n" + "If the key item cannot be consumed this many times, the death chest will not " + - "be unlocked.\n"+ + "be unlocked.\n" + "Players in creative mode will not have their key item consumed." ) public int amountToConsume = 1; @Comment( - "The message that is sent to the player when they fail to unlock a death chest.\n"+ + "The message that is sent to the player when they fail to unlock a death chest.\n" + "This string takes the required amount (%1$s) and display name (%2$s) of the " + - "item as arguments.\n"+ + "item as arguments.\n" + "Set this to an empty string to disable this message." ) public String unlockFailureMessage = "You need %s of %s to retrieve your items"; @@ -257,10 +257,10 @@ public void validatePostLoad() { public static final class DefenseEntities implements ConfigData { @Comment( - "The registry name of the defense entity.\n"+ - "If the defense entity is a living entity, it will not automatically despawn.\n"+ + "The registry name of the defense entity.\n" + + "If the defense entity is a living entity, it will not automatically despawn.\n" + "If the defense entity can have a revenge target, then the revenge target will\n" + - "be set to its player.\n" + + "be set to its player.\n" + "Set this to an empty string to disable defense entities." ) public String registryName = ""; @@ -283,18 +283,18 @@ public static final class DefenseEntities implements ConfigData { //@SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) @Comment( "The maximum squared distance that a defense entity can be from its chest when " + - "a player is not nearby.\n"+ + "a player is not nearby.\n" + "Set this to 0.0 to disable the limit so that defense entities are not " + - "teleported back to their death chest." + "teleported back to their death chest." ) public double maxSquaredDistanceFromChest = 64.0; //@SpecDoubleInRange(min = 0.0, max = Double.MAX_VALUE) @Comment( "The maximum squared distance that a defense entity can be from its player when" + - "its chest is not within the maximum squared distance.\n"+ + "its chest is not within the maximum squared distance.\n" + "Set this to 0.0 to disable the limit so that defense entities are teleported " + - "back to their death chest regardless of their distance from the player." + "back to their death chest regardless of their distance from the player." ) public double maxSquaredDistanceFromPlayer = 64.0; @@ -328,10 +328,10 @@ public void validatePostLoad() { } } - public static final class Protection implements ConfigData{ + public static final class Protection implements ConfigData { @Comment( - "Enables death chest protection.\n"+ - "When a death chest is protected, it can only be unlocked by its owner.\n" + "Enables death chest protection.\n" + + "When a death chest is protected, it can only be unlocked by its owner.\n" ) public boolean enable = true; @@ -356,7 +356,7 @@ public static final class Protection implements ConfigData{ public int period = 120000; } - public static final class Misc implements ConfigData{ + public static final class Misc implements ConfigData { @Comment("Causes death chests to be removed when they are emptied.") public boolean removeEmptyDeathChests = true; @@ -369,16 +369,16 @@ public static final class Misc implements ConfigData{ public boolean dropDeathChests; @Comment( - "The name of the game rule that controls whether death chests should be spawned.\n"+ - "Set this to an empty string to disable the game rule.\n"+ - "Changes to this option are applied after a game restart." + "The name of the game rule that controls whether death chests should be spawned.\n" + + "Set this to an empty string to disable the game rule.\n" + + "Changes to this option are applied after a game restart." ) public String gameRuleName = "spawnDeathChests"; @Comment( - "The name of the command that reloads this configuration from disk.\n"+ - "Set this to an empty string to disable the command.\n"+ - "Changes to this option are applied when a server is loaded." + "The name of the command that reloads this configuration from disk.\n" + + "Set this to an empty string to disable the command.\n" + + "Changes to this option are applied when a server is loaded." ) public String configReloadCommand = "vdcconfigreload"; } @@ -524,6 +524,11 @@ public enum KeyConsumptionBehavior { */ DAMAGE } + + /** + * + * {@inheritDoc} + */ @Override public void validatePostLoad() { try { diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java b/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java index 152cc6b..0d816e7 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/VDCModMenuEntryPoint.java @@ -31,6 +31,10 @@ * The Mod Menu entry point for VanillaDeathChest. */ public final class VDCModMenuEntryPoint implements ModMenuApi { + /** + * + * {@inheritDoc} + */ @Override public ConfigScreenFactory getModConfigScreenFactory() { return screen -> AutoConfig.getConfigScreen(VDCConfig.class, screen).get(); diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java b/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java index 790093f..8900b25 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/VanillaDeathChest.java @@ -23,16 +23,13 @@ package com.therandomlabs.vanilladeathchest; - import com.therandomlabs.vanilladeathchest.command.VDCCommand; import com.therandomlabs.vanilladeathchest.deathchest.DeathChestAutoRemover; import com.therandomlabs.vanilladeathchest.deathchest.DeathChestInteractions; import com.therandomlabs.vanilladeathchest.deathchest.DeathChestPlacer; import com.therandomlabs.vanilladeathchest.world.DeathChestsState; import me.shedaniel.autoconfig.AutoConfig; -import me.shedaniel.autoconfig.serializer.ConfigSerializer; import me.shedaniel.autoconfig.serializer.JanksonConfigSerializer; -import me.shedaniel.autoconfig.serializer.Toml4jConfigSerializer; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents; @@ -115,8 +112,9 @@ public static void reloadConfig() { if (!didRegister) { didRegister = true; AutoConfig.register(VDCConfig.class, JanksonConfigSerializer::new); - //AutoConfig.register(VDCConfig.class, Toml4jConfigSerializer::new); + //AutoConfig.register(VDCConfig.class, Toml4jConfigSerializer::new); } + config = AutoConfig.getConfigHolder(VDCConfig.class).getConfig(); } } From d29bd378666791cb1309631b311ae98341accb3b Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Wed, 23 Jun 2021 10:50:23 +0200 Subject: [PATCH 22/28] write was read --- .../mixin/LockableContainerBlockEntityMixin.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java index 73a7c95..5096efc 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java @@ -79,16 +79,16 @@ public void markAsDeathChest() { isDeathChest = true; } - @Inject(method = "writeNbt", at = @At("TAIL")) - private void writeNbt( - NbtCompound nbt, CallbackInfoReturnable cir + @Inject(method = "readNbt", at = @At("TAIL")) + private void readNbt( + NbtCompound nbt, CallbackInfo ci ) { isDeathChest = nbt.getBoolean("IsDeathChest"); } - @Inject(method = "readNbt", at = @At("TAIL")) - private void readNbt( - NbtCompound nbt, CallbackInfo ci + @Inject(method = "writeNbt", at = @At("TAIL")) + private void writeNbt( + NbtCompound nbt, CallbackInfoReturnable cir ) { if (isDeathChest) { nbt.putBoolean("IsDeathChest", true); From 0952434aea77221a1f26e3d8d48c460d20cc4ea1 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Wed, 23 Jun 2021 10:53:26 +0200 Subject: [PATCH 23/28] Consisten naming --- .../vanilladeathchest/deathchest/DeathChest.java | 2 +- .../vanilladeathchest/world/DeathChestsState.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java index 18b15a7..fe8c5c3 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChest.java @@ -277,7 +277,7 @@ public NbtCompound writeNbt(NbtCompound tag) { * @return the deserialized {@link DeathChest}. */ @SuppressWarnings("ConstantConditions") - public static DeathChest fromTag(ServerWorld world, NbtCompound tag) { + public static DeathChest readNbt(ServerWorld world, NbtCompound tag) { final List items = new ArrayList<>(); for (NbtElement itemTag : tag.getList("Items", NbtType.COMPOUND)) { diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java index 51b1cd2..f4848d9 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java @@ -38,6 +38,7 @@ import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; import net.fabricmc.fabric.api.util.NbtType; +import net.minecraft.block.Block; import net.minecraft.block.entity.BlockEntity; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; @@ -68,7 +69,7 @@ private DeathChestsState(String name, ServerWorld world) { public DeathChestsState readNbt(NbtCompound tag) { deathChests.clear(); tag.getList("DeathChests", NbtType.COMPOUND).stream(). - map(deathChestTag -> DeathChest.fromTag(world, (NbtCompound) deathChestTag)). + map(deathChestTag -> DeathChest.readNbt(world, (NbtCompound) deathChestTag)). forEach(deathChest -> deathChests.put(deathChest.getIdentifier(), deathChest)); existingDeathChests.clear(); @@ -79,7 +80,7 @@ public DeathChestsState readNbt(NbtCompound tag) { queuedDeathChests.clear(); tag.getList("QueuedDeathChests", NbtType.COMPOUND).stream(). - map(deathChestTag -> DeathChest.fromTag(world, (NbtCompound) deathChestTag)). + map(deathChestTag -> DeathChest.readNbt(world, (NbtCompound) deathChestTag)). forEach(queuedDeathChests::add); return this; From 3f920efcc4a9400adcc16593df8a118e3e30e7cc Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Wed, 23 Jun 2021 15:27:03 +0200 Subject: [PATCH 24/28] Try to detect chun-unloads in `onBlockEntityUnload`using the inventory size. --- ...xin.java => ChestBlockEntityAccessor.java} | 7 +++++- .../world/DeathChestsState.java | 23 ++++++++++++++++++- .../resources/vanilladeathchest.mixins.json | 4 ++-- 3 files changed, 30 insertions(+), 4 deletions(-) rename src/main/java/com/therandomlabs/vanilladeathchest/mixin/{ChestBlockEntityMixin.java => ChestBlockEntityAccessor.java} (84%) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityAccessor.java similarity index 84% rename from src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java rename to src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityAccessor.java index 2805f1f..06d6b74 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/ChestBlockEntityAccessor.java @@ -24,8 +24,13 @@ package com.therandomlabs.vanilladeathchest.mixin; import net.minecraft.block.entity.ChestBlockEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.collection.DefaultedList; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(ChestBlockEntity.class) -public final class ChestBlockEntityMixin { +public interface ChestBlockEntityAccessor { + @Accessor + DefaultedList getInventory(); } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java index f4848d9..827101d 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java @@ -35,11 +35,14 @@ import java.util.UUID; import java.util.stream.Collectors; +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; +import com.therandomlabs.vanilladeathchest.mixin.ChestBlockEntityAccessor; import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; import net.fabricmc.fabric.api.util.NbtType; import net.minecraft.block.Block; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; import net.minecraft.nbt.NbtList; @@ -223,9 +226,27 @@ public static void onBlockEntityUnload(BlockEntity blockEntity, ServerWorld worl return; }*/ + // Using a heuristic for chunk unload: If the chest was destroyed (and not chunk unloaded) + // the chest inventory should be empty. + if (blockEntity instanceof DeathChestBlockEntity) { final DeathChest deathChest = ((DeathChestBlockEntity) blockEntity).getDeathChest(); - get(world).existingDeathChests.values().remove(deathChest); + final DeathChestsState state = get(world); + final boolean isEmpty; + + if (blockEntity instanceof ChestBlockEntityAccessor) { + isEmpty = ((ChestBlockEntityAccessor) blockEntity).getInventory(). + stream(). + allMatch(ItemStack::isEmpty); + } else { + //not sure if this case can happen, but we keep the chest around + isEmpty = false; + } + + if (deathChest != null && isEmpty) { + Block bl = blockEntity.getCachedState().getBlock(); + state.existingDeathChests.values().remove(deathChest); + } } } diff --git a/src/main/resources/vanilladeathchest.mixins.json b/src/main/resources/vanilladeathchest.mixins.json index e52bdc9..6ef4f67 100644 --- a/src/main/resources/vanilladeathchest.mixins.json +++ b/src/main/resources/vanilladeathchest.mixins.json @@ -2,10 +2,10 @@ "required": true, "minVersion": "0.8", "package": "com.therandomlabs.vanilladeathchest.mixin", - "compatibilityLevel": "JAVA_8", + "compatibilityLevel": "JAVA_16", "mixins": [ "BlockMixin", - "ChestBlockEntityMixin", + "ChestBlockEntityAccessor", "ExplosionMixin", "LivingEntityMixin", "LockableContainerBlockEntityMixin", From c60ceceff59531abddca6a30e75673b22dbcbb98 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Wed, 23 Jun 2021 15:27:29 +0200 Subject: [PATCH 25/28] Some debug logging --- .../mixin/LockableContainerBlockEntityMixin.java | 7 +++++++ .../vanilladeathchest/world/DeathChestsState.java | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java index 5096efc..8ca64bd 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/mixin/LockableContainerBlockEntityMixin.java @@ -23,6 +23,7 @@ package com.therandomlabs.vanilladeathchest.mixin; +import com.therandomlabs.vanilladeathchest.VanillaDeathChest; import com.therandomlabs.vanilladeathchest.deathchest.DeathChest; import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; import com.therandomlabs.vanilladeathchest.world.DeathChestsState; @@ -84,6 +85,9 @@ private void readNbt( NbtCompound nbt, CallbackInfo ci ) { isDeathChest = nbt.getBoolean("IsDeathChest"); + VanillaDeathChest.logger.atDebug().log( + "Loading DeathChest at x:" + nbt.getInt("x") + ",y:" + nbt.get("y") + ",z:" + + nbt.get("z")); } @Inject(method = "writeNbt", at = @At("TAIL")) @@ -92,6 +96,9 @@ private void writeNbt( ) { if (isDeathChest) { nbt.putBoolean("IsDeathChest", true); + VanillaDeathChest.logger.atDebug().log( + "Storing DeathChest at x:" + nbt.getInt("x") + ",y:" + nbt.get("y") + + ",z:" + nbt.get("z")); } } } diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java index 827101d..dbc3e14 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java @@ -243,9 +243,19 @@ public static void onBlockEntityUnload(BlockEntity blockEntity, ServerWorld worl isEmpty = false; } + if (deathChest != null) { + VanillaDeathChest.logger.atDebug().log( + "Unload requested for DeathChest at x:" + blockEntity.getPos().getX() + + ",y:" + blockEntity.getPos().getY() + ",z:" + + blockEntity.getPos().getZ() + ",empty:" + isEmpty + ",chestCount:" + + state.existingDeathChests.size()); + } + if (deathChest != null && isEmpty) { Block bl = blockEntity.getCachedState().getBlock(); state.existingDeathChests.values().remove(deathChest); + VanillaDeathChest.logger.atDebug().log( + "Removed DeathChest, " + state.existingDeathChests.size() + " remaining."); } } } From 8032ff5a8efdb5aca7c098c1e32b41eb9c47d6bf Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Wed, 23 Jun 2021 15:27:45 +0200 Subject: [PATCH 26/28] CheckStyle fixes --- .../vanilladeathchest/deathchest/DeathChestInteractions.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java index 9b8ca20..7bf54f0 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java @@ -94,6 +94,10 @@ public static ActionResult interact( */ @SuppressWarnings("PMD.CompareObjectsWithEquals") public static boolean attemptInteract(DeathChest deathChest, ServerPlayerEntity player) { + if (player != null) { + return true; + } + if (deathChest.isProtectedFrom(player)) { return false; } From cdf2444e48cd64f006cbee87bb159166d41d9372 Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Wed, 23 Jun 2021 15:36:13 +0200 Subject: [PATCH 27/28] Removed unused variable --- .../therandomlabs/vanilladeathchest/world/DeathChestsState.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java index dbc3e14..f18b6fd 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/world/DeathChestsState.java @@ -40,7 +40,6 @@ import com.therandomlabs.vanilladeathchest.mixin.ChestBlockEntityAccessor; import com.therandomlabs.vanilladeathchest.util.DeathChestBlockEntity; import net.fabricmc.fabric.api.util.NbtType; -import net.minecraft.block.Block; import net.minecraft.block.entity.BlockEntity; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; @@ -252,7 +251,6 @@ public static void onBlockEntityUnload(BlockEntity blockEntity, ServerWorld worl } if (deathChest != null && isEmpty) { - Block bl = blockEntity.getCachedState().getBlock(); state.existingDeathChests.values().remove(deathChest); VanillaDeathChest.logger.atDebug().log( "Removed DeathChest, " + state.existingDeathChests.size() + " remaining."); From 3c74e83d286e18a9aa82576293ad7268374c821a Mon Sep 17 00:00:00 2001 From: Frank Bauer Date: Mon, 5 Jul 2021 00:19:45 +0200 Subject: [PATCH 28/28] Removed Debug Code --- .../vanilladeathchest/deathchest/DeathChestInteractions.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java index 7bf54f0..9b8ca20 100644 --- a/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java +++ b/src/main/java/com/therandomlabs/vanilladeathchest/deathchest/DeathChestInteractions.java @@ -94,10 +94,6 @@ public static ActionResult interact( */ @SuppressWarnings("PMD.CompareObjectsWithEquals") public static boolean attemptInteract(DeathChest deathChest, ServerPlayerEntity player) { - if (player != null) { - return true; - } - if (deathChest.isProtectedFrom(player)) { return false; }